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