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