xref: /original-bsd/sys/netiso/tp_output.c (revision 12f5d8d0)
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.12 (Berkeley) 07/29/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("winsize 0x%x\n",  param->p_winsize );
154 	ENDDEBUG
155 	if( (param->p_winsize < 128 ) ||
156 		(param->p_winsize < param->p_tpdusize ) ||
157 		(param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
158 			error = EINVAL; goto done;
159 	} else {
160 		if( tpcb->tp_state == TP_CLOSED )
161 			soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
162 						(u_long)param->p_winsize);
163 	}
164 	IFDEBUG(D_SETPARAMS)
165 		printf("use_csum 0x%x\n",  param->p_use_checksum );
166 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
167 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
168 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
169 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
170 	ENDDEBUG
171 	switch( class_to_use ) {
172 
173 	case 0:
174 		/* do not use checksums, xtd format, or XPD */
175 
176 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
177 			if(cmd & TP_STRICT) {
178 				error = EINVAL;
179 			} else {
180 				param->p_use_checksum = 0;
181 				param->p_xtd_format = 0;
182 				param->p_xpd_service = 0;
183 			}
184 			break;
185 		}
186 
187 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
188 			if(cmd & TP_STRICT) {
189 				error = EINVAL;
190 			} else {
191 				param->p_tpdusize = TP_MIN_TPDUSIZE;
192 			}
193 			break;
194 		}
195 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
196 			if (cmd & TP_STRICT) {
197 				error = EINVAL;
198 			} else {
199 				param->p_tpdusize = TP0_TPDUSIZE;
200 			}
201 			break;
202 		}
203 
204 		/* connect/disc data not allowed for class 0 */
205 		if (tpcb->tp_ucddata) {
206 			if(cmd & TP_STRICT) {
207 				error = EINVAL;
208 			} else if(cmd & TP_FORCE) {
209 				m_freem(tpcb->tp_ucddata);
210 				tpcb->tp_ucddata = 0;
211 			}
212 		}
213 		break;
214 
215 	case 4:
216 		IFDEBUG(D_SETPARAMS)
217 			printf("dt_ticks 0x%x\n",  param->p_dt_ticks );
218 			printf("x_ticks 0x%x\n",  param->p_x_ticks );
219 			printf("dr_ticks 0x%x\n",  param->p_dr_ticks );
220 			printf("keepalive 0x%x\n",  param->p_keepalive_ticks );
221 			printf("sendack 0x%x\n",  param->p_sendack_ticks );
222 			printf("inact 0x%x\n",  param->p_inact_ticks );
223 			printf("ref 0x%x\n",  param->p_ref_ticks );
224 		ENDDEBUG
225 		if( (param->p_class & TP_CLASS_4 ) && (
226 			  (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
227 			  (param->p_x_ticks < 1)	|| (param->p_keepalive_ticks < 1) ||
228 			  (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
229 			  (param->p_inact_ticks < 1) ) ) {
230 				error = EINVAL;
231 				break;
232 		}
233 		IFDEBUG(D_SETPARAMS)
234 			printf("rx_strat 0x%x\n",  param->p_rx_strat );
235 		ENDDEBUG
236 		if(param->p_rx_strat >
237 			( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
238 				if(cmd & TP_STRICT) {
239 					error = EINVAL;
240 				} else {
241 					param->p_rx_strat = TPRX_USE_CW;
242 				}
243 				break;
244 		}
245 		IFDEBUG(D_SETPARAMS)
246 			printf("ack_strat 0x%x\n",  param->p_ack_strat );
247 		ENDDEBUG
248 		if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
249 			if(cmd & TP_STRICT) {
250 				error = EINVAL;
251 			} else {
252 				param->p_ack_strat = TPACK_WINDOW;
253 			}
254 			break;
255 		}
256 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
257 			if(cmd & TP_STRICT) {
258 				error = EINVAL;
259 			} else {
260 				param->p_tpdusize = TP_MIN_TPDUSIZE;
261 			}
262 			break;
263 		}
264 		if (param->p_tpdusize > TP_TPDUSIZE)  {
265 			if(cmd & TP_STRICT) {
266 				error = EINVAL;
267 			} else {
268 				param->p_tpdusize = TP_TPDUSIZE;
269 			}
270 			break;
271 		}
272 		break;
273 	}
274 
275 	if ((error==0) && (cmd & TP_FORCE)) {
276 		/* Enforce Negotation rules below */
277 		if (tpcb->tp_tpdusize > param->p_tpdusize)
278 			tpcb->tp_tpdusize = param->p_tpdusize;
279 		tpcb->tp_class = param->p_class;
280 		if (tpcb->tp_use_checksum || param->p_use_checksum)
281 			tpcb->tp_use_checksum = 1;
282 		if (!tpcb->tp_xpd_service || !param->p_xpd_service)
283 			tpcb->tp_xpd_service = 0;
284 		if (!tpcb->tp_xtd_format || !param->p_xtd_format)
285 			tpcb->tp_xtd_format = 0;
286 	}
287 
288 done:
289 
290 	IFTRACE(D_CONN)
291 		tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
292 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
293 	ENDTRACE
294 	IFDEBUG(D_CONN)
295 		printf(
296 		"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
297 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
298 	ENDDEBUG
299 	return error;
300 }
301 
302 /*
303  * NAME: 	tp_ctloutput()
304  *
305  * CALLED FROM:
306  * 	[sg]etsockopt(), via so[sg]etopt().
307  *
308  * FUNCTION and ARGUMENTS:
309  * 	Implements the socket options at transport level.
310  * 	(cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
311  * 	(so) is the socket.
312  * 	(level) is SOL_TRANSPORT (see ../sys/socket.h)
313  * 	(optname) is the particular command or option to be set.
314  * 	(**mp) is an mbuf structure.
315  *
316  * RETURN VALUE:
317  * 	ENOTSOCK if the socket hasn't got an associated tpcb
318  *  EINVAL if
319  * 		trying to set window too big
320  * 		trying to set illegal max tpdu size
321  * 		trying to set illegal credit fraction
322  * 		trying to use unknown or unimplemented class of TP
323  *		structure passed to set timer values is wrong size
324  *  	illegal combination of command/GET-SET option,
325  *			e.g., GET w/ TPOPT_CDDATA_CLEAR:
326  *  EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
327  *   or if the transport-specific command is not implemented
328  *  EISCONN if trying a command that isn't allowed after a connection
329  *   is established
330  *  ENOTCONN if trying a command that is allowed only if a connection is
331  *   established
332  *  EMSGSIZE if trying to give too much data on connect/disconnect
333  *
334  * SIDE EFFECTS:
335  *
336  * NOTES:
337  */
338 ProtoHook
339 tp_ctloutput(cmd, so, level, optname, mp)
340 	int 			cmd, level, optname;
341 	struct socket	*so;
342 	struct mbuf 	**mp;
343 {
344 	struct		tp_pcb	*tpcb = sototpcb(so);
345 	int 		s = splnet();
346 	caddr_t		value;
347 	unsigned	val_len;
348 	int			error = 0;
349 
350 	IFTRACE(D_REQUEST)
351 		tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
352 			cmd, so, optname, mp);
353 	ENDTRACE
354 	IFDEBUG(D_REQUEST)
355 		printf(
356 	"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
357 			so, cmd, optname, mp, mp?*mp:0, tpcb);
358 	ENDDEBUG
359 	if( tpcb == (struct tp_pcb *)0 ) {
360 		error = ENOTSOCK; goto done;
361 	}
362 	if(*mp == MNULL) {
363 		register struct mbuf *m;
364 
365 		MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
366 		if (m == NULL) {
367 			splx(s);
368 			return ENOBUFS;
369 		}
370 		m->m_len = 0;
371 		m->m_act = 0;
372 		*mp = m;
373 	}
374 
375 	/*
376 	 *	Hook so one can set network options via a tp socket.
377 	 */
378 	if ( level == SOL_NETWORK ) {
379 		if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
380 			error = ENOTSOCK;
381 		else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
382 			error = EOPNOTSUPP;
383 		else
384 			error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
385 				tpcb->tp_npcb, *mp);
386 		goto done;
387 	} else if ( level !=  SOL_TRANSPORT ) {
388 		error = EOPNOTSUPP; goto done;
389 	}
390 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
391 		error = EOPNOTSUPP; goto done;
392 	}
393 	if ( so->so_error ) {
394 		error = so->so_error; goto done;
395 	}
396 
397 	/* The only options allowed after connection is established
398 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
399 	 */
400 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
401 		&&
402 		(cmd == PRCO_SETOPT  &&
403 			optname != TPOPT_DISC_DATA &&
404 			optname != TPOPT_CFRM_DATA &&
405 			optname != TPOPT_PERF_MEAS &&
406 			optname != TPOPT_CDDATA_CLEAR ) ) {
407 		error = EISCONN; goto done;
408 	}
409 	/* The only options allowed after disconnection are GET DISC DATA,
410 	 * and TPOPT_PSTATISTICS
411 	 * and they're not allowed if the ref timer has gone off, because
412 	 * the tpcb is gone
413 	 */
414 	if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) ==  0) {
415 		if ( so->so_pcb == (caddr_t)0 ) {
416 			error = ENOTCONN; goto done;
417 		}
418 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
419 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
420 			error = ENOTCONN; goto done;
421 		}
422 	}
423 
424 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
425 								  * but lint complains about it
426 								  */
427 	val_len = (*mp)->m_len;
428 
429 	switch (optname) {
430 
431 	case TPOPT_INTERCEPT:
432 #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
433 #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
434 
435 		if ((so->so_state & SS_PRIV) == 0) {
436 			error = EPERM;
437 		} else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
438 					(tpcb->tp_flags & TPF_GENERAL_ADDR) ||
439 					tpcb->tp_next == 0)
440 			error = EINVAL;
441 		else {
442 			register struct tp_pcb *t;
443 			error = EADDRINUSE;
444 			for (t = tp_listeners; t; t = t->tp_nextlisten)
445 				if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
446 						t->tp_domain == tpcb->tp_domain)
447 					switch (tpcb->tp_domain) {
448 					default:
449 						goto done;
450 #ifdef	INET
451 					case AF_INET:
452 						if (INA(t) == INA(tpcb))
453 							goto done;
454 						continue;
455 #endif
456 #ifdef ISO
457 					case AF_ISO:
458 						if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
459 										ISOA(t).isoa_len) == 0)
460 							goto done;
461 						continue;
462 #endif
463 					}
464 			tpcb->tp_lsuffixlen = 0;
465 			tpcb->tp_state = TP_LISTENING;
466 			error = 0;
467 			remque(tpcb);
468 			tpcb->tp_next = tpcb->tp_prev = tpcb;
469 			tpcb->tp_nextlisten = tp_listeners;
470 			tp_listeners = tpcb;
471 		}
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