added my responsibility for the `cpm' port
[unix-history] / sys / netiso / tp_output.c
CommitLineData
15637ed4
RG
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
b120d1cf 33 * From: @(#)tp_output.c 7.10 (Berkeley) 6/27/91
fde1aeb2 34 * $Id: tp_output.c,v 1.3 1993/10/16 21:05:51 rgrimes Exp $
15637ed4
RG
35 */
36
37/***********************************************************
38 Copyright IBM Corporation 1987
39
40 All Rights Reserved
41
42Permission to use, copy, modify, and distribute this software and its
43documentation for any purpose and without fee is hereby granted,
44provided that the above copyright notice appear in all copies and that
45both that copyright notice and this permission notice appear in
46supporting documentation, and that the name of IBM not be
47used in advertising or publicity pertaining to distribution of the
48software without specific, written prior permission.
49
50IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
51ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
52IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
53ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
54WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
55ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
56SOFTWARE.
57
58******************************************************************/
59
60/*
61 * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
62 */
43371f85 63
15637ed4
RG
64/*
65 * ARGO TP
66 *
15637ed4
RG
67 * In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
68 */
69
70#include "param.h"
71#include "mbuf.h"
72#include "systm.h"
73#include "socket.h"
74#include "socketvar.h"
75#include "protosw.h"
76#include "errno.h"
77#include "time.h"
78#include "tp_param.h"
79#include "tp_user.h"
80#include "tp_stat.h"
81#include "tp_ip.h"
82#include "tp_clnp.h"
83#include "tp_timer.h"
84#include "argo_debug.h"
85#include "tp_pcb.h"
86#include "tp_trace.h"
87#include "kernel.h"
88
89#define USERFLAGSMASK_G 0x0f00643b
90#define USERFLAGSMASK_S 0x0f000432
91#define TPDUSIZESHIFT 24
92#define CLASSHIFT 16
93
94/*
95 * NAME: tp_consistency()
96 *
97 * CALLED FROM:
98 * tp_ctloutput(), tp_input()
99 *
100 * FUNCTION and ARGUMENTS:
101 * Checks the consistency of options and tpdusize with class,
102 * using the parameters passed in via (param).
103 * (cmd) may be TP_STRICT or TP_FORCE or both.
104 * Force means it will set all the values in (tpcb) to those in
105 * the input arguements iff no errors were encountered.
106 * Strict means that no inconsistency will be tolerated. If it's
107 * not used, checksum and tpdusize inconsistencies will be tolerated.
108 * The reason for this is that in some cases, when we're negotiating down
109 * from class 4, these options should be changed but should not
110 * cause negotiation to fail.
111 *
112 * RETURNS
113 * E* or EOK
114 * E* if the various parms aren't ok for a given class
115 * EOK if they are ok for a given class
116 */
117
118int
119tp_consistency( tpcb, cmd, param )
fde1aeb2 120 struct tp_pcb *tpcb;
15637ed4
RG
121 u_int cmd;
122 struct tp_conn_param *param;
15637ed4
RG
123{
124 register int error = EOK;
125 int class_to_use = tp_mask_to_num(param->p_class);
126
127 IFTRACE(D_SETPARAMS)
128 tptrace(TPPTmisc,
129 "tp_consist enter class_to_use dontchange param.class cmd",
130 class_to_use, param->p_dont_change_params, param->p_class, cmd);
131 ENDTRACE
132 IFDEBUG(D_SETPARAMS)
133 printf("tp_consistency %s %s\n",
134 cmd& TP_FORCE? "TP_FORCE": "",
135 cmd& TP_STRICT? "TP_STRICT":"");
136 ENDDEBUG
137 if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
138 cmd &= ~TP_FORCE;
139 }
140 /* can switch net services within a domain, but
141 * cannot switch domains
142 */
143 switch( param->p_netservice) {
144 case ISO_CONS:
145 case ISO_CLNS:
146 case ISO_COSNS:
147 /* param->p_netservice in ISO DOMAIN */
148 if(tpcb->tp_domain != AF_ISO ) {
149 error = EINVAL; goto done;
150 }
151 break;
152 case IN_CLNS:
153 /* param->p_netservice in INET DOMAIN */
154 if( tpcb->tp_domain != AF_INET ) {
155 error = EINVAL; goto done;
156 }
157 break;
158 /* no others not possible-> netservice is a 2-bit field! */
159 }
160
161 IFDEBUG(D_SETPARAMS)
162 printf("p_class 0x%x, class_to_use 0x%x\n", param->p_class,
163 class_to_use);
164 ENDDEBUG
b120d1cf
RG
165#ifdef XXXRWGXXX
166 /* this next line is questionable p_netservice is u_char, how can it
167 * ever be less than 0 ??
168 */
15637ed4 169 if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){
b120d1cf
RG
170#endif
171 if(/*(param->p_netservice < 0) ||*/ (param->p_netservice > TP_MAX_NETSERVICES)){
15637ed4
RG
172 error = EINVAL; goto done;
173 }
174 if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
175 error = EINVAL; goto done;
176 }
177 IFDEBUG(D_SETPARAMS)
178 printf("Nretrans 0x%x\n", param->p_Nretrans );
179 ENDDEBUG
180 if( ( param->p_Nretrans < 1 ) ||
181 (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
182 /* bad for any class because negot has to be done a la class 4 */
183 error = EINVAL; goto done;
184 }
185 IFDEBUG(D_SETPARAMS)
186 printf("winsize 0x%x\n", param->p_winsize );
187 ENDDEBUG
188 if( (param->p_winsize < 128 ) ||
189 (param->p_winsize < param->p_tpdusize ) ||
190 (param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
191 error = EINVAL; goto done;
192 } else {
193 if( tpcb->tp_state == TP_CLOSED )
194 soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
195 (u_long)param->p_winsize);
196 }
197 IFDEBUG(D_SETPARAMS)
198 printf("use_csum 0x%x\n", param->p_use_checksum );
199 printf("xtd_format 0x%x\n", param->p_xtd_format );
200 printf("xpd_service 0x%x\n", param->p_xpd_service );
201 printf("tpdusize 0x%x\n", param->p_tpdusize );
202 printf("tpcb->flags 0x%x\n", tpcb->tp_flags );
203 ENDDEBUG
204 switch( class_to_use ) {
205
206 case 0:
207 /* do not use checksums, xtd format, or XPD */
208
209 if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
210 if(cmd & TP_STRICT) {
211 error = EINVAL;
212 } else {
213 param->p_use_checksum = 0;
214 param->p_xtd_format = 0;
215 param->p_xpd_service = 0;
216 }
217 break;
218 }
219
220 if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
221 if(cmd & TP_STRICT) {
222 error = EINVAL;
223 } else {
224 param->p_tpdusize = TP_MIN_TPDUSIZE;
225 }
226 break;
227 }
228 if (param->p_tpdusize > TP0_TPDUSIZE) {
229 if (cmd & TP_STRICT) {
230 error = EINVAL;
231 } else {
232 param->p_tpdusize = TP0_TPDUSIZE;
233 }
234 break;
235 }
236
237 /* connect/disc data not allowed for class 0 */
238 if (tpcb->tp_ucddata) {
239 if(cmd & TP_STRICT) {
240 error = EINVAL;
241 } else if(cmd & TP_FORCE) {
242 m_freem(tpcb->tp_ucddata);
243 tpcb->tp_ucddata = 0;
244 }
245 }
246 break;
247
248 case 4:
249 IFDEBUG(D_SETPARAMS)
250 printf("dt_ticks 0x%x\n", param->p_dt_ticks );
251 printf("x_ticks 0x%x\n", param->p_x_ticks );
252 printf("dr_ticks 0x%x\n", param->p_dr_ticks );
253 printf("keepalive 0x%x\n", param->p_keepalive_ticks );
254 printf("sendack 0x%x\n", param->p_sendack_ticks );
255 printf("inact 0x%x\n", param->p_inact_ticks );
256 printf("ref 0x%x\n", param->p_ref_ticks );
257 ENDDEBUG
258 if( (param->p_class & TP_CLASS_4 ) && (
259 (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
260 (param->p_x_ticks < 1) || (param->p_keepalive_ticks < 1) ||
261 (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
262 (param->p_inact_ticks < 1) ) ) {
263 error = EINVAL;
264 break;
265 }
266 IFDEBUG(D_SETPARAMS)
267 printf("rx_strat 0x%x\n", param->p_rx_strat );
268 ENDDEBUG
269 if(param->p_rx_strat >
270 ( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
271 if(cmd & TP_STRICT) {
272 error = EINVAL;
273 } else {
274 param->p_rx_strat = TPRX_USE_CW;
275 }
276 break;
277 }
278 IFDEBUG(D_SETPARAMS)
279 printf("ack_strat 0x%x\n", param->p_ack_strat );
280 ENDDEBUG
281 if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
282 if(cmd & TP_STRICT) {
283 error = EINVAL;
284 } else {
285 param->p_ack_strat = TPACK_WINDOW;
286 }
287 break;
288 }
289 if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
290 if(cmd & TP_STRICT) {
291 error = EINVAL;
292 } else {
293 param->p_tpdusize = TP_MIN_TPDUSIZE;
294 }
295 break;
296 }
297 if (param->p_tpdusize > TP_TPDUSIZE) {
298 if(cmd & TP_STRICT) {
299 error = EINVAL;
300 } else {
301 param->p_tpdusize = TP_TPDUSIZE;
302 }
303 break;
304 }
305 break;
306 }
307
308 if ((error==0) && (cmd & TP_FORCE)) {
309 /* Enforce Negotation rules below */
310 if (tpcb->tp_tpdusize > param->p_tpdusize)
311 tpcb->tp_tpdusize = param->p_tpdusize;
312 tpcb->tp_class = param->p_class;
313 if (tpcb->tp_use_checksum || param->p_use_checksum)
314 tpcb->tp_use_checksum = 1;
315 if (!tpcb->tp_xpd_service || !param->p_xpd_service)
316 tpcb->tp_xpd_service = 0;
317 if (!tpcb->tp_xtd_format || !param->p_xtd_format)
318 tpcb->tp_xtd_format = 0;
319 }
320
321done:
322
323 IFTRACE(D_CONN)
324 tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
325 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
326 ENDTRACE
327 IFDEBUG(D_CONN)
328 printf(
329 "tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
330 error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
331 ENDDEBUG
332 return error;
333}
334
335/*
336 * NAME: tp_ctloutput()
337 *
338 * CALLED FROM:
339 * [sg]etsockopt(), via so[sg]etopt().
340 *
341 * FUNCTION and ARGUMENTS:
342 * Implements the socket options at transport level.
343 * (cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
344 * (so) is the socket.
345 * (level) is SOL_TRANSPORT (see ../sys/socket.h)
346 * (optname) is the particular command or option to be set.
347 * (**mp) is an mbuf structure.
348 *
349 * RETURN VALUE:
350 * ENOTSOCK if the socket hasn't got an associated tpcb
351 * EINVAL if
352 * trying to set window too big
353 * trying to set illegal max tpdu size
354 * trying to set illegal credit fraction
355 * trying to use unknown or unimplemented class of TP
356 * structure passed to set timer values is wrong size
357 * illegal combination of command/GET-SET option,
358 * e.g., GET w/ TPOPT_CDDATA_CLEAR:
359 * EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
360 * or if the transport-specific command is not implemented
361 * EISCONN if trying a command that isn't allowed after a connection
362 * is established
363 * ENOTCONN if trying a command that is allowed only if a connection is
364 * established
365 * EMSGSIZE if trying to give too much data on connect/disconnect
366 *
367 * SIDE EFFECTS:
368 *
369 * NOTES:
370 */
371ProtoHook
372tp_ctloutput(cmd, so, level, optname, mp)
373 int cmd, level, optname;
374 struct socket *so;
375 struct mbuf **mp;
376{
377 struct tp_pcb *tpcb = sototpcb(so);
378 int s = splnet();
379 caddr_t value;
380 unsigned val_len;
381 int error = 0;
382
383 IFTRACE(D_REQUEST)
384 tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
385 cmd, so, optname, mp);
386 ENDTRACE
387 IFDEBUG(D_REQUEST)
388 printf(
389 "tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
390 so, cmd, optname, mp, mp?*mp:0, tpcb);
391 ENDDEBUG
392 if( tpcb == (struct tp_pcb *)0 ) {
393 error = ENOTSOCK; goto done;
394 }
395 if(*mp == MNULL) {
396 register struct mbuf *m;
397
398 MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
399 if (m == NULL) {
400 splx(s);
401 return ENOBUFS;
402 }
403 m->m_len = 0;
404 m->m_act = 0;
405 *mp = m;
406 }
407
408 /*
409 * Hook so one can set network options via a tp socket.
410 */
411 if ( level == SOL_NETWORK ) {
412 if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
413 error = ENOTSOCK;
414 else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
415 error = EOPNOTSUPP;
416 else
417 error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
418 tpcb->tp_npcb, *mp);
419 goto done;
420 } else if ( level != SOL_TRANSPORT ) {
421 error = EOPNOTSUPP; goto done;
422 }
423 if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
424 error = EOPNOTSUPP; goto done;
425 }
426 if ( so->so_error ) {
427 error = so->so_error; goto done;
428 }
429
430 /* The only options allowed after connection is established
431 * are GET (anything) and SET DISC DATA and SET PERF MEAS
432 */
433 if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
434 &&
435 (cmd == PRCO_SETOPT &&
436 optname != TPOPT_DISC_DATA &&
437 optname != TPOPT_CFRM_DATA &&
438 optname != TPOPT_PERF_MEAS &&
439 optname != TPOPT_CDDATA_CLEAR ) ) {
440 error = EISCONN; goto done;
441 }
442 /* The only options allowed after disconnection are GET DISC DATA,
443 * and TPOPT_PSTATISTICS
444 * and they're not allowed if the ref timer has gone off, because
445 * the tpcb is gone
446 */
447 if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) == 0) {
448 if ( so->so_tpcb == (caddr_t)0 ) {
449 error = ENOTCONN; goto done;
450 }
451 if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
452 (optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
453 error = ENOTCONN; goto done;
454 }
455 }
456
457 value = mtod(*mp, caddr_t); /* it's aligned, don't worry,
458 * but lint complains about it
459 */
460 val_len = (*mp)->m_len;
461
462 switch (optname) {
463
464 case TPOPT_INTERCEPT:
465 if ((so->so_state & SS_PRIV) == 0) {
466 error = EPERM;
467 break;
468 } else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_LISTENING)
469 error = EINVAL;
470 else {
471 register struct tp_pcb *t = 0;
472 struct mbuf *m = m_getclr(M_WAIT, MT_SONAME);
473 struct sockaddr *sa = mtod(m, struct sockaddr *);
474 (*tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, m, TP_LOCAL);
475 switch (sa->sa_family) {
476 case AF_ISO:
477 if (((struct sockaddr_iso *)sa)->siso_nlen == 0)
478 default: error = EINVAL;
479 break;
480 case AF_INET:
481 if (((struct sockaddr_in *)sa)->sin_addr.s_addr == 0)
482 error = EINVAL;
483 break;
484 }
485 for (t = tp_intercepts; t; t = t->tp_nextlisten) {
486 if (t->tp_nlproto->nlp_afamily != tpcb->tp_nlproto->nlp_afamily)
487 continue;
488 if ((*t->tp_nlproto->nlp_cmpnetaddr)(t->tp_npcb, sa, TP_LOCAL))
489 error = EADDRINUSE;
490 }
491 m_freem(m);
492 if (error)
493 break;
494 }
495 {
496 register struct tp_pcb **tt;
497 for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
498 if (*tt == tpcb)
499 break;
500 if (*tt)
501 *tt = tpcb->tp_nextlisten;
502 else
503 {error = EHOSTUNREACH; goto done; }
504 }
505 tpcb->tp_nextlisten = tp_intercepts;
506 tp_intercepts = tpcb;
507 break;
508
509 case TPOPT_MY_TSEL:
510 if ( cmd == PRCO_GETOPT ) {
511 ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
512 bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
513 (*mp)->m_len = tpcb->tp_lsuffixlen;
514 } else /* cmd == PRCO_SETOPT */ {
515 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
516 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
517 error = EINVAL;
518 } else {
519 bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
520 tpcb->tp_lsuffixlen = val_len;
521 }
522 }
523 break;
524
525 case TPOPT_PEER_TSEL:
526 if ( cmd == PRCO_GETOPT ) {
527 ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
528 bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
529 (*mp)->m_len = tpcb->tp_fsuffixlen;
530 } else /* cmd == PRCO_SETOPT */ {
531 if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
532 printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
533 error = EINVAL;
534 } else {
535 bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
536 tpcb->tp_fsuffixlen = val_len;
537 }
538 }
539 break;
540
541 case TPOPT_FLAGS:
542 IFDEBUG(D_REQUEST)
543 printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
544 cmd==PRCO_GETOPT?"GET":"SET",
545 value,
546 *value,
547 tpcb->tp_flags);
548 ENDDEBUG
549
550 if ( cmd == PRCO_GETOPT ) {
551 *(int *)value = (int)tpcb->tp_flags;
552 (*mp)->m_len = sizeof(u_int);
553 } else /* cmd == PRCO_SETOPT */ {
554 error = EINVAL; goto done;
555 }
556 break;
557
558 case TPOPT_PARAMS:
559 /* This handles:
560 * timer values,
561 * class, use of transport expedited data,
562 * max tpdu size, checksum, xtd format and
563 * disconnect indications, and may get rid of connect/disc data
564 */
565 IFDEBUG(D_SETPARAMS)
566 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
567 cmd==PRCO_GETOPT?"GET":"SET");
568 ENDDEBUG
569 IFDEBUG(D_REQUEST)
570 printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
571 cmd==PRCO_GETOPT?"GET":"SET");
572 ENDDEBUG
573
574 if ( cmd == PRCO_GETOPT ) {
575 *(struct tp_conn_param *)value = tpcb->_tp_param;
576 (*mp)->m_len = sizeof(tpcb->_tp_param);
577 } else /* cmd == PRCO_SETOPT */ {
578 if( (error =
579 tp_consistency(tpcb, TP_STRICT | TP_FORCE,
580 (struct tp_conn_param *)value))==0) {
581 /*
582 * tp_consistency doesn't copy the whole set of params
583 */
584 tpcb->_tp_param = *(struct tp_conn_param *)value;
585 (*mp)->m_len = sizeof(tpcb->_tp_param);
586 }
587 }
588 break;
589
590 case TPOPT_PSTATISTICS:
591#ifdef TP_PERF_MEAS
592 if (cmd == PRCO_SETOPT) {
593 error = EINVAL; goto done;
594 }
595 IFPERF(tpcb)
596 if (*mp) {
597 struct mbuf * n;
598 do {
599 MFREE(*mp, n);
600 *mp = n;
601 } while (n);
602 }
603 *mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
604 ENDPERF
605 else {
606 error = EINVAL; goto done;
607 }
608 break;
609#else
610 error = EOPNOTSUPP;
611 goto done;
612#endif TP_PERF_MEAS
613
614 case TPOPT_CDDATA_CLEAR:
615 if (cmd == PRCO_GETOPT) {
616 error = EINVAL;
617 } else {
618 if (tpcb->tp_ucddata) {
619 m_freem(tpcb->tp_ucddata);
620 tpcb->tp_ucddata = 0;
621 }
622 }
623 break;
624
625 case TPOPT_CFRM_DATA:
626 case TPOPT_DISC_DATA:
627 case TPOPT_CONN_DATA:
628 if( tpcb->tp_class == TP_CLASS_0 ) {
629 error = EOPNOTSUPP;
630 break;
631 }
632 IFDEBUG(D_REQUEST)
633 printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
634 printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
635 (*mp)->m_len, val_len, so->so_snd.sb_cc);
636 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
637 ENDDEBUG
638 if (cmd == PRCO_SETOPT) {
639 int len = tpcb->tp_ucddata ? tpcb->tp_ucddata->m_len : 0;
640 /* can append connect data in several calls */
641 if (len + val_len >
642 (optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
643 error = EMSGSIZE; goto done;
644 }
645 (*mp)->m_next = MNULL;
646 (*mp)->m_act = 0;
647 if (tpcb->tp_ucddata)
648 m_cat(tpcb->tp_ucddata, *mp);
649 else
650 tpcb->tp_ucddata = *mp;
651 IFDEBUG(D_REQUEST)
652 dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
653 ENDDEBUG
654 IFTRACE(D_REQUEST)
655 tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
656 tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
657 ENDTRACE
658 *mp = MNULL; /* prevent sosetopt from freeing it! */
659 if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
660 (void) tp_confirm(tpcb);
661 }
662 break;
663
664 case TPOPT_PERF_MEAS:
665#ifdef TP_PERF_MEAS
666 if (cmd == PRCO_GETOPT) {
667 *value = (u_int)tpcb->tp_perf_on;
668 (*mp)->m_len = sizeof(u_int);
669 } else if (cmd == PRCO_SETOPT) {
670 (*mp)->m_len = 0;
671 if ((*value) != 0 && (*value) != 1 )
672 error = EINVAL;
673 else tpcb->tp_perf_on = (*value);
674 }
675 if( tpcb->tp_perf_on )
676 error = tp_setup_perf(tpcb);
677#else TP_PERF_MEAS
678 error = EOPNOTSUPP;
679#endif TP_PERF_MEAS
680 break;
681
682 default:
683 error = EOPNOTSUPP;
684 }
685
686done:
687 IFDEBUG(D_REQUEST)
688 dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
689 dump_mbuf(*mp, "tp_ctloutput *mp");
690 ENDDEBUG
691 /*
692 * sigh: getsockopt looks only at m_len : all output data must
693 * reside in the first mbuf
694 */
695 if ( error && (*mp) != MNULL )
696 (*mp)->m_len = 0;
697 if( (*mp) != MNULL ) {
698 ASSERT ( m_compress(*mp, mp) <= MLEN );
699 IFDEBUG(D_REQUEST)
700 dump_mbuf(*mp, "tp_ctloutput *mp after compress");
701 ENDDEBUG
702 }
703
704 splx(s);
705 return error;
706}