xref: /original-bsd/sys/netiso/tp_output.c (revision e59fb703)
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.15 (Berkeley) 12/17/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 		long dusize = ((long)param->p_ptpdusize) << 7;
265 		/* Enforce Negotation rules below */
266 		tpcb->tp_class = param->p_class;
267 		if (tpcb->tp_use_checksum || param->p_use_checksum)
268 			tpcb->tp_use_checksum = 1;
269 		if (!tpcb->tp_xpd_service || !param->p_xpd_service)
270 			tpcb->tp_xpd_service = 0;
271 		if (!tpcb->tp_xtd_format || !param->p_xtd_format)
272 			tpcb->tp_xtd_format = 0;
273 		if (dusize) {
274 			if (tpcb->tp_l_tpdusize > dusize)
275 				tpcb->tp_l_tpdusize = dusize;
276 			if (tpcb->tp_ptpdusize == 0 ||
277 				tpcb->tp_ptpdusize > param->p_ptpdusize)
278 				tpcb->tp_ptpdusize = param->p_ptpdusize;
279 		} else {
280 			if (param->p_tpdusize != 0 &&
281 				tpcb->tp_tpdusize > param->p_tpdusize)
282 				tpcb->tp_tpdusize = param->p_tpdusize;
283 			tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
284 		}
285 	}
286 done:
287 
288 	IFTRACE(D_CONN)
289 		tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
290 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
291 	ENDTRACE
292 	IFDEBUG(D_CONN)
293 		printf(
294 		"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
295 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
296 	ENDDEBUG
297 	return error;
298 }
299 
300 /*
301  * NAME: 	tp_ctloutput()
302  *
303  * CALLED FROM:
304  * 	[sg]etsockopt(), via so[sg]etopt().
305  *
306  * FUNCTION and ARGUMENTS:
307  * 	Implements the socket options at transport level.
308  * 	(cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
309  * 	(so) is the socket.
310  * 	(level) is SOL_TRANSPORT (see ../sys/socket.h)
311  * 	(optname) is the particular command or option to be set.
312  * 	(**mp) is an mbuf structure.
313  *
314  * RETURN VALUE:
315  * 	ENOTSOCK if the socket hasn't got an associated tpcb
316  *  EINVAL if
317  * 		trying to set window too big
318  * 		trying to set illegal max tpdu size
319  * 		trying to set illegal credit fraction
320  * 		trying to use unknown or unimplemented class of TP
321  *		structure passed to set timer values is wrong size
322  *  	illegal combination of command/GET-SET option,
323  *			e.g., GET w/ TPOPT_CDDATA_CLEAR:
324  *  EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
325  *   or if the transport-specific command is not implemented
326  *  EISCONN if trying a command that isn't allowed after a connection
327  *   is established
328  *  ENOTCONN if trying a command that is allowed only if a connection is
329  *   established
330  *  EMSGSIZE if trying to give too much data on connect/disconnect
331  *
332  * SIDE EFFECTS:
333  *
334  * NOTES:
335  */
336 ProtoHook
337 tp_ctloutput(cmd, so, level, optname, mp)
338 	int 			cmd, level, optname;
339 	struct socket	*so;
340 	struct mbuf 	**mp;
341 {
342 	struct		tp_pcb	*tpcb = sototpcb(so);
343 	int 		s = splnet();
344 	caddr_t		value;
345 	unsigned	val_len;
346 	int			error = 0;
347 
348 	IFTRACE(D_REQUEST)
349 		tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
350 			cmd, so, optname, mp);
351 	ENDTRACE
352 	IFDEBUG(D_REQUEST)
353 		printf(
354 	"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
355 			so, cmd, optname, mp, mp?*mp:0, tpcb);
356 	ENDDEBUG
357 	if( tpcb == (struct tp_pcb *)0 ) {
358 		error = ENOTSOCK; goto done;
359 	}
360 	if(*mp == MNULL) {
361 		register struct mbuf *m;
362 
363 		MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
364 		if (m == NULL) {
365 			splx(s);
366 			return ENOBUFS;
367 		}
368 		m->m_len = 0;
369 		m->m_act = 0;
370 		*mp = m;
371 	}
372 
373 	/*
374 	 *	Hook so one can set network options via a tp socket.
375 	 */
376 	if ( level == SOL_NETWORK ) {
377 		if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
378 			error = ENOTSOCK;
379 		else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
380 			error = EOPNOTSUPP;
381 		else
382 			return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
383 				tpcb->tp_npcb, *mp));
384 		goto done;
385 	} else if ( level == SOL_SOCKET) {
386 		if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
387 			tp_rsyset(tpcb);
388 		}
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_pcb == (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 #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
436 #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
437 
438 		if ((so->so_state & SS_PRIV) == 0) {
439 			error = EPERM;
440 		} else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
441 					(tpcb->tp_flags & TPF_GENERAL_ADDR) ||
442 					tpcb->tp_next == 0)
443 			error = EINVAL;
444 		else {
445 			register struct tp_pcb *t;
446 			error = EADDRINUSE;
447 			for (t = tp_listeners; t; t = t->tp_nextlisten)
448 				if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
449 						t->tp_domain == tpcb->tp_domain)
450 					switch (tpcb->tp_domain) {
451 					default:
452 						goto done;
453 #ifdef	INET
454 					case AF_INET:
455 						if (INA(t) == INA(tpcb))
456 							goto done;
457 						continue;
458 #endif
459 #ifdef ISO
460 					case AF_ISO:
461 						if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
462 										ISOA(t).isoa_len) == 0)
463 							goto done;
464 						continue;
465 #endif
466 					}
467 			tpcb->tp_lsuffixlen = 0;
468 			tpcb->tp_state = TP_LISTENING;
469 			error = 0;
470 			remque(tpcb);
471 			tpcb->tp_next = tpcb->tp_prev = tpcb;
472 			tpcb->tp_nextlisten = tp_listeners;
473 			tp_listeners = tpcb;
474 		}
475 		break;
476 
477 	case TPOPT_MY_TSEL:
478 		if ( cmd == PRCO_GETOPT ) {
479 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
480 			bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
481 			(*mp)->m_len = tpcb->tp_lsuffixlen;
482 		} else /* cmd == PRCO_SETOPT  */ {
483 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
484 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
485 				error = EINVAL;
486 			} else {
487 				bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
488 				tpcb->tp_lsuffixlen = val_len;
489 			}
490 		}
491 		break;
492 
493 	case TPOPT_PEER_TSEL:
494 		if ( cmd == PRCO_GETOPT ) {
495 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
496 			bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
497 			(*mp)->m_len = tpcb->tp_fsuffixlen;
498 		} else /* cmd == PRCO_SETOPT  */ {
499 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
500 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
501 				error = EINVAL;
502 			} else {
503 				bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
504 				tpcb->tp_fsuffixlen = val_len;
505 			}
506 		}
507 		break;
508 
509 	case TPOPT_FLAGS:
510 		IFDEBUG(D_REQUEST)
511 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
512 				cmd==PRCO_GETOPT?"GET":"SET",
513 				value,
514 				*value,
515 				tpcb->tp_flags);
516 		ENDDEBUG
517 
518 		if ( cmd == PRCO_GETOPT ) {
519 			*(int *)value = (int)tpcb->tp_flags;
520 			(*mp)->m_len = sizeof(u_int);
521 		} else /* cmd == PRCO_SETOPT  */ {
522 			error = EINVAL; goto done;
523 		}
524 		break;
525 
526 	case TPOPT_PARAMS:
527 		/* This handles:
528 		 * timer values,
529 		 * class, use of transport expedited data,
530 		 * max tpdu size, checksum, xtd format and
531 		 * disconnect indications, and may get rid of connect/disc data
532 		 */
533 		IFDEBUG(D_SETPARAMS)
534 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
535 				cmd==PRCO_GETOPT?"GET":"SET");
536 		ENDDEBUG
537 		IFDEBUG(D_REQUEST)
538 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
539 				cmd==PRCO_GETOPT?"GET":"SET");
540 		ENDDEBUG
541 
542 		if ( cmd == PRCO_GETOPT ) {
543 			*(struct tp_conn_param *)value = tpcb->_tp_param;
544 			(*mp)->m_len = sizeof(tpcb->_tp_param);
545 		} else /* cmd == PRCO_SETOPT  */ {
546 			if( (error =
547 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
548 								(struct tp_conn_param *)value))==0) {
549 				/*
550 				 * tp_consistency doesn't copy the whole set of params
551 				 */
552 				tpcb->_tp_param = *(struct tp_conn_param *)value;
553 				(*mp)->m_len = sizeof(tpcb->_tp_param);
554 			}
555 		}
556 		break;
557 
558 	case TPOPT_PSTATISTICS:
559 #ifdef TP_PERF_MEAS
560 		if (cmd == PRCO_SETOPT) {
561 			error = EINVAL; goto done;
562 		}
563 		IFPERF(tpcb)
564 			if (*mp) {
565 				struct mbuf * n;
566 				do {
567 					MFREE(*mp, n);
568 					*mp = n;
569 				} while (n);
570 			}
571 			*mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
572 		ENDPERF
573 		else {
574 			error = EINVAL; goto done;
575 		}
576 		break;
577 #else
578 		error = EOPNOTSUPP;
579 		goto done;
580 #endif TP_PERF_MEAS
581 
582 	case TPOPT_CDDATA_CLEAR:
583 		if (cmd == PRCO_GETOPT) {
584 			error = EINVAL;
585 		} else {
586 			if (tpcb->tp_ucddata) {
587 				m_freem(tpcb->tp_ucddata);
588 				tpcb->tp_ucddata = 0;
589 			}
590 		}
591 		break;
592 
593 	case TPOPT_CFRM_DATA:
594 	case TPOPT_DISC_DATA:
595 	case TPOPT_CONN_DATA:
596 		if( tpcb->tp_class == TP_CLASS_0 ) {
597 			error = EOPNOTSUPP;
598 			break;
599 		}
600 		IFDEBUG(D_REQUEST)
601 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
602 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
603 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
604 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
605 		ENDDEBUG
606 		if (cmd == PRCO_SETOPT) {
607 			int len = tpcb->tp_ucddata ?  tpcb->tp_ucddata->m_len : 0;
608 			/* can append connect data in several calls */
609 			if (len + val_len >
610 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
611 				error = EMSGSIZE; goto done;
612 			}
613 			(*mp)->m_next = MNULL;
614 			(*mp)->m_act = 0;
615 			if (tpcb->tp_ucddata)
616 				m_cat(tpcb->tp_ucddata, *mp);
617 			else
618 				tpcb->tp_ucddata = *mp;
619 			IFDEBUG(D_REQUEST)
620 				dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
621 			ENDDEBUG
622 			IFTRACE(D_REQUEST)
623 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
624 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
625 			ENDTRACE
626 			*mp = MNULL;
627 			if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
628 				(void) tp_confirm(tpcb);
629 		}
630 		break;
631 
632 	case TPOPT_PERF_MEAS:
633 #ifdef TP_PERF_MEAS
634 		if (cmd == PRCO_GETOPT) {
635 			*value = (u_int)tpcb->tp_perf_on;
636 			(*mp)->m_len = sizeof(u_int);
637 		} else if (cmd == PRCO_SETOPT) {
638 			(*mp)->m_len = 0;
639 			if ((*value) != 0 && (*value) != 1 )
640 				error = EINVAL;
641 			else  tpcb->tp_perf_on = (*value);
642 		}
643 		if( tpcb->tp_perf_on )
644 			error = tp_setup_perf(tpcb);
645 #else  TP_PERF_MEAS
646 		error = EOPNOTSUPP;
647 #endif TP_PERF_MEAS
648 		break;
649 
650 	default:
651 		error = EOPNOTSUPP;
652 	}
653 
654 done:
655 	IFDEBUG(D_REQUEST)
656 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
657 		dump_mbuf(*mp, "tp_ctloutput *mp");
658 	ENDDEBUG
659 	/*
660 	 * sigh: getsockopt looks only at m_len : all output data must
661 	 * reside in the first mbuf
662 	 */
663 	if (*mp) {
664 		if (cmd == PRCO_SETOPT)
665 			m_freem(*mp);
666 		else {
667 			ASSERT ( m_compress(*mp, mp) <= MLEN );
668 			if (error)
669 				(*mp)->m_len = 0;
670 			IFDEBUG(D_REQUEST)
671 				dump_mbuf(*mp, "tp_ctloutput *mp after compress");
672 			ENDDEBUG
673 		}
674 	}
675 	splx(s);
676 	return error;
677 }
678