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