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