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