xref: /original-bsd/sys/netiso/tp_output.c (revision a1c2194a)
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.11 (Berkeley) 07/18/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 USERFLAGSMASK_G 0x0f00643b
65 #define USERFLAGSMASK_S 0x0f000432
66 #define TPDUSIZESHIFT 24
67 #define CLASSHIFT 16
68 
69 /*
70  * NAME: 	tp_consistency()
71  *
72  * CALLED FROM:
73  * 	tp_ctloutput(), tp_input()
74  *
75  * FUNCTION and ARGUMENTS:
76  * 	Checks the consistency of options and tpdusize with class,
77  *	using the parameters passed in via (param).
78  *	(cmd) may be TP_STRICT or TP_FORCE or both.
79  *  Force means it will set all the values in (tpcb) to those in
80  *  the input arguements iff no errors were encountered.
81  *  Strict means that no inconsistency will be tolerated.  If it's
82  *  not used, checksum and tpdusize inconsistencies will be tolerated.
83  *  The reason for this is that in some cases, when we're negotiating down
84  *	from class  4, these options should be changed but should not
85  *  cause negotiation to fail.
86  *
87  * RETURNS
88  *  E* or EOK
89  *  E* if the various parms aren't ok for a given class
90  *  EOK if they are ok for a given class
91  */
92 
93 int
94 tp_consistency( tpcb, cmd, param )
95 	u_int cmd;
96 	struct tp_conn_param *param;
97 	struct tp_pcb *tpcb;
98 {
99 	register int	error = EOK;
100 	int 			class_to_use  = tp_mask_to_num(param->p_class);
101 
102 	IFTRACE(D_SETPARAMS)
103 		tptrace(TPPTmisc,
104 		"tp_consist enter class_to_use dontchange param.class cmd",
105 		class_to_use, param->p_dont_change_params, param->p_class, cmd);
106 	ENDTRACE
107 	IFDEBUG(D_SETPARAMS)
108 		printf("tp_consistency %s %s\n",
109 			cmd& TP_FORCE?	"TP_FORCE":	"",
110 			cmd& TP_STRICT?	"TP_STRICT":"");
111 	ENDDEBUG
112 	if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
113 		cmd &= ~TP_FORCE;
114 	}
115 	/* can switch net services within a domain, but
116 	 * cannot switch domains
117 	 */
118 	switch( param->p_netservice) {
119 	case ISO_CONS:
120 	case ISO_CLNS:
121 	case ISO_COSNS:
122 		/* param->p_netservice in ISO DOMAIN */
123 		if(tpcb->tp_domain != AF_ISO ) {
124 			error = EINVAL; goto done;
125 		}
126 		break;
127 	case IN_CLNS:
128 		/* param->p_netservice in INET DOMAIN */
129 		if( tpcb->tp_domain != AF_INET ) {
130 			error = EINVAL; goto done;
131 		}
132 		break;
133 		/* no others not possible-> netservice is a 2-bit field! */
134 	}
135 
136 	IFDEBUG(D_SETPARAMS)
137 		printf("p_class 0x%x, class_to_use 0x%x\n",  param->p_class,
138 			class_to_use);
139 	ENDDEBUG
140 	if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){
141 		error = EINVAL; goto done;
142 	}
143 	if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
144 		error = EINVAL; goto done;
145 	}
146 	IFDEBUG(D_SETPARAMS)
147 		printf("Nretrans 0x%x\n",  param->p_Nretrans );
148 	ENDDEBUG
149 	if( ( param->p_Nretrans < 1 ) ||
150 		  (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
151 			/* bad for any class because negot has to be done a la class 4 */
152 			error = EINVAL; goto done;
153 	}
154 	IFDEBUG(D_SETPARAMS)
155 		printf("winsize 0x%x\n",  param->p_winsize );
156 	ENDDEBUG
157 	if( (param->p_winsize < 128 ) ||
158 		(param->p_winsize < param->p_tpdusize ) ||
159 		(param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
160 			error = EINVAL; goto done;
161 	} else {
162 		if( tpcb->tp_state == TP_CLOSED )
163 			soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
164 						(u_long)param->p_winsize);
165 	}
166 	IFDEBUG(D_SETPARAMS)
167 		printf("use_csum 0x%x\n",  param->p_use_checksum );
168 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
169 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
170 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
171 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
172 	ENDDEBUG
173 	switch( class_to_use ) {
174 
175 	case 0:
176 		/* do not use checksums, xtd format, or XPD */
177 
178 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
179 			if(cmd & TP_STRICT) {
180 				error = EINVAL;
181 			} else {
182 				param->p_use_checksum = 0;
183 				param->p_xtd_format = 0;
184 				param->p_xpd_service = 0;
185 			}
186 			break;
187 		}
188 
189 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
190 			if(cmd & TP_STRICT) {
191 				error = EINVAL;
192 			} else {
193 				param->p_tpdusize = TP_MIN_TPDUSIZE;
194 			}
195 			break;
196 		}
197 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
198 			if (cmd & TP_STRICT) {
199 				error = EINVAL;
200 			} else {
201 				param->p_tpdusize = TP0_TPDUSIZE;
202 			}
203 			break;
204 		}
205 
206 		/* connect/disc data not allowed for class 0 */
207 		if (tpcb->tp_ucddata) {
208 			if(cmd & TP_STRICT) {
209 				error = EINVAL;
210 			} else if(cmd & TP_FORCE) {
211 				m_freem(tpcb->tp_ucddata);
212 				tpcb->tp_ucddata = 0;
213 			}
214 		}
215 		break;
216 
217 	case 4:
218 		IFDEBUG(D_SETPARAMS)
219 			printf("dt_ticks 0x%x\n",  param->p_dt_ticks );
220 			printf("x_ticks 0x%x\n",  param->p_x_ticks );
221 			printf("dr_ticks 0x%x\n",  param->p_dr_ticks );
222 			printf("keepalive 0x%x\n",  param->p_keepalive_ticks );
223 			printf("sendack 0x%x\n",  param->p_sendack_ticks );
224 			printf("inact 0x%x\n",  param->p_inact_ticks );
225 			printf("ref 0x%x\n",  param->p_ref_ticks );
226 		ENDDEBUG
227 		if( (param->p_class & TP_CLASS_4 ) && (
228 			  (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
229 			  (param->p_x_ticks < 1)	|| (param->p_keepalive_ticks < 1) ||
230 			  (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
231 			  (param->p_inact_ticks < 1) ) ) {
232 				error = EINVAL;
233 				break;
234 		}
235 		IFDEBUG(D_SETPARAMS)
236 			printf("rx_strat 0x%x\n",  param->p_rx_strat );
237 		ENDDEBUG
238 		if(param->p_rx_strat >
239 			( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
240 				if(cmd & TP_STRICT) {
241 					error = EINVAL;
242 				} else {
243 					param->p_rx_strat = TPRX_USE_CW;
244 				}
245 				break;
246 		}
247 		IFDEBUG(D_SETPARAMS)
248 			printf("ack_strat 0x%x\n",  param->p_ack_strat );
249 		ENDDEBUG
250 		if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
251 			if(cmd & TP_STRICT) {
252 				error = EINVAL;
253 			} else {
254 				param->p_ack_strat = TPACK_WINDOW;
255 			}
256 			break;
257 		}
258 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
259 			if(cmd & TP_STRICT) {
260 				error = EINVAL;
261 			} else {
262 				param->p_tpdusize = TP_MIN_TPDUSIZE;
263 			}
264 			break;
265 		}
266 		if (param->p_tpdusize > TP_TPDUSIZE)  {
267 			if(cmd & TP_STRICT) {
268 				error = EINVAL;
269 			} else {
270 				param->p_tpdusize = TP_TPDUSIZE;
271 			}
272 			break;
273 		}
274 		break;
275 	}
276 
277 	if ((error==0) && (cmd & TP_FORCE)) {
278 		/* Enforce Negotation rules below */
279 		if (tpcb->tp_tpdusize > param->p_tpdusize)
280 			tpcb->tp_tpdusize = param->p_tpdusize;
281 		tpcb->tp_class = param->p_class;
282 		if (tpcb->tp_use_checksum || param->p_use_checksum)
283 			tpcb->tp_use_checksum = 1;
284 		if (!tpcb->tp_xpd_service || !param->p_xpd_service)
285 			tpcb->tp_xpd_service = 0;
286 		if (!tpcb->tp_xtd_format || !param->p_xtd_format)
287 			tpcb->tp_xtd_format = 0;
288 	}
289 
290 done:
291 
292 	IFTRACE(D_CONN)
293 		tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
294 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
295 	ENDTRACE
296 	IFDEBUG(D_CONN)
297 		printf(
298 		"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
299 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
300 	ENDDEBUG
301 	return error;
302 }
303 
304 /*
305  * NAME: 	tp_ctloutput()
306  *
307  * CALLED FROM:
308  * 	[sg]etsockopt(), via so[sg]etopt().
309  *
310  * FUNCTION and ARGUMENTS:
311  * 	Implements the socket options at transport level.
312  * 	(cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
313  * 	(so) is the socket.
314  * 	(level) is SOL_TRANSPORT (see ../sys/socket.h)
315  * 	(optname) is the particular command or option to be set.
316  * 	(**mp) is an mbuf structure.
317  *
318  * RETURN VALUE:
319  * 	ENOTSOCK if the socket hasn't got an associated tpcb
320  *  EINVAL if
321  * 		trying to set window too big
322  * 		trying to set illegal max tpdu size
323  * 		trying to set illegal credit fraction
324  * 		trying to use unknown or unimplemented class of TP
325  *		structure passed to set timer values is wrong size
326  *  	illegal combination of command/GET-SET option,
327  *			e.g., GET w/ TPOPT_CDDATA_CLEAR:
328  *  EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
329  *   or if the transport-specific command is not implemented
330  *  EISCONN if trying a command that isn't allowed after a connection
331  *   is established
332  *  ENOTCONN if trying a command that is allowed only if a connection is
333  *   established
334  *  EMSGSIZE if trying to give too much data on connect/disconnect
335  *
336  * SIDE EFFECTS:
337  *
338  * NOTES:
339  */
340 ProtoHook
341 tp_ctloutput(cmd, so, level, optname, mp)
342 	int 			cmd, level, optname;
343 	struct socket	*so;
344 	struct mbuf 	**mp;
345 {
346 	struct		tp_pcb	*tpcb = sototpcb(so);
347 	int 		s = splnet();
348 	caddr_t		value;
349 	unsigned	val_len;
350 	int			error = 0;
351 
352 	IFTRACE(D_REQUEST)
353 		tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
354 			cmd, so, optname, mp);
355 	ENDTRACE
356 	IFDEBUG(D_REQUEST)
357 		printf(
358 	"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
359 			so, cmd, optname, mp, mp?*mp:0, tpcb);
360 	ENDDEBUG
361 	if( tpcb == (struct tp_pcb *)0 ) {
362 		error = ENOTSOCK; goto done;
363 	}
364 	if(*mp == MNULL) {
365 		register struct mbuf *m;
366 
367 		MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
368 		if (m == NULL) {
369 			splx(s);
370 			return ENOBUFS;
371 		}
372 		m->m_len = 0;
373 		m->m_act = 0;
374 		*mp = m;
375 	}
376 
377 	/*
378 	 *	Hook so one can set network options via a tp socket.
379 	 */
380 	if ( level == SOL_NETWORK ) {
381 		if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
382 			error = ENOTSOCK;
383 		else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
384 			error = EOPNOTSUPP;
385 		else
386 			error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
387 				tpcb->tp_npcb, *mp);
388 		goto done;
389 	} else if ( level !=  SOL_TRANSPORT ) {
390 		error = EOPNOTSUPP; goto done;
391 	}
392 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
393 		error = EOPNOTSUPP; goto done;
394 	}
395 	if ( so->so_error ) {
396 		error = so->so_error; goto done;
397 	}
398 
399 	/* The only options allowed after connection is established
400 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
401 	 */
402 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
403 		&&
404 		(cmd == PRCO_SETOPT  &&
405 			optname != TPOPT_DISC_DATA &&
406 			optname != TPOPT_CFRM_DATA &&
407 			optname != TPOPT_PERF_MEAS &&
408 			optname != TPOPT_CDDATA_CLEAR ) ) {
409 		error = EISCONN; goto done;
410 	}
411 	/* The only options allowed after disconnection are GET DISC DATA,
412 	 * and TPOPT_PSTATISTICS
413 	 * and they're not allowed if the ref timer has gone off, because
414 	 * the tpcb is gone
415 	 */
416 	if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) ==  0) {
417 		if ( so->so_pcb == (caddr_t)0 ) {
418 			error = ENOTCONN; goto done;
419 		}
420 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
421 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
422 			error = ENOTCONN; goto done;
423 		}
424 	}
425 
426 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
427 								  * but lint complains about it
428 								  */
429 	val_len = (*mp)->m_len;
430 
431 	switch (optname) {
432 
433 	case TPOPT_INTERCEPT:
434 		if ((so->so_state & SS_PRIV) == 0) {
435 			error = EPERM;
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