xref: /original-bsd/sys/netiso/tp_output.c (revision ae309585)
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.13 (Berkeley) 09/03/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 			return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
385 				tpcb->tp_npcb, *mp));
386 		goto done;
387 	} else if ( level == SOL_SOCKET) {
388 		if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
389 			tp_rsyset(tpcb);
390 		}
391 		goto done;
392 	} else if ( level !=  SOL_TRANSPORT ) {
393 		error = EOPNOTSUPP; goto done;
394 	}
395 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
396 		error = EOPNOTSUPP; goto done;
397 	}
398 	if ( so->so_error ) {
399 		error = so->so_error; goto done;
400 	}
401 
402 	/* The only options allowed after connection is established
403 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
404 	 */
405 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
406 		&&
407 		(cmd == PRCO_SETOPT  &&
408 			optname != TPOPT_DISC_DATA &&
409 			optname != TPOPT_CFRM_DATA &&
410 			optname != TPOPT_PERF_MEAS &&
411 			optname != TPOPT_CDDATA_CLEAR ) ) {
412 		error = EISCONN; goto done;
413 	}
414 	/* The only options allowed after disconnection are GET DISC DATA,
415 	 * and TPOPT_PSTATISTICS
416 	 * and they're not allowed if the ref timer has gone off, because
417 	 * the tpcb is gone
418 	 */
419 	if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) ==  0) {
420 		if ( so->so_pcb == (caddr_t)0 ) {
421 			error = ENOTCONN; goto done;
422 		}
423 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
424 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
425 			error = ENOTCONN; goto done;
426 		}
427 	}
428 
429 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
430 								  * but lint complains about it
431 								  */
432 	val_len = (*mp)->m_len;
433 
434 	switch (optname) {
435 
436 	case TPOPT_INTERCEPT:
437 #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
438 #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
439 
440 		if ((so->so_state & SS_PRIV) == 0) {
441 			error = EPERM;
442 		} else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
443 					(tpcb->tp_flags & TPF_GENERAL_ADDR) ||
444 					tpcb->tp_next == 0)
445 			error = EINVAL;
446 		else {
447 			register struct tp_pcb *t;
448 			error = EADDRINUSE;
449 			for (t = tp_listeners; t; t = t->tp_nextlisten)
450 				if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
451 						t->tp_domain == tpcb->tp_domain)
452 					switch (tpcb->tp_domain) {
453 					default:
454 						goto done;
455 #ifdef	INET
456 					case AF_INET:
457 						if (INA(t) == INA(tpcb))
458 							goto done;
459 						continue;
460 #endif
461 #ifdef ISO
462 					case AF_ISO:
463 						if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
464 										ISOA(t).isoa_len) == 0)
465 							goto done;
466 						continue;
467 #endif
468 					}
469 			tpcb->tp_lsuffixlen = 0;
470 			tpcb->tp_state = TP_LISTENING;
471 			error = 0;
472 			remque(tpcb);
473 			tpcb->tp_next = tpcb->tp_prev = tpcb;
474 			tpcb->tp_nextlisten = tp_listeners;
475 			tp_listeners = tpcb;
476 		}
477 		break;
478 
479 	case TPOPT_MY_TSEL:
480 		if ( cmd == PRCO_GETOPT ) {
481 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
482 			bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
483 			(*mp)->m_len = tpcb->tp_lsuffixlen;
484 		} else /* cmd == PRCO_SETOPT  */ {
485 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
486 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
487 				error = EINVAL;
488 			} else {
489 				bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
490 				tpcb->tp_lsuffixlen = val_len;
491 			}
492 		}
493 		break;
494 
495 	case TPOPT_PEER_TSEL:
496 		if ( cmd == PRCO_GETOPT ) {
497 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
498 			bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
499 			(*mp)->m_len = tpcb->tp_fsuffixlen;
500 		} else /* cmd == PRCO_SETOPT  */ {
501 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
502 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
503 				error = EINVAL;
504 			} else {
505 				bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
506 				tpcb->tp_fsuffixlen = val_len;
507 			}
508 		}
509 		break;
510 
511 	case TPOPT_FLAGS:
512 		IFDEBUG(D_REQUEST)
513 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
514 				cmd==PRCO_GETOPT?"GET":"SET",
515 				value,
516 				*value,
517 				tpcb->tp_flags);
518 		ENDDEBUG
519 
520 		if ( cmd == PRCO_GETOPT ) {
521 			*(int *)value = (int)tpcb->tp_flags;
522 			(*mp)->m_len = sizeof(u_int);
523 		} else /* cmd == PRCO_SETOPT  */ {
524 			error = EINVAL; goto done;
525 		}
526 		break;
527 
528 	case TPOPT_PARAMS:
529 		/* This handles:
530 		 * timer values,
531 		 * class, use of transport expedited data,
532 		 * max tpdu size, checksum, xtd format and
533 		 * disconnect indications, and may get rid of connect/disc data
534 		 */
535 		IFDEBUG(D_SETPARAMS)
536 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
537 				cmd==PRCO_GETOPT?"GET":"SET");
538 		ENDDEBUG
539 		IFDEBUG(D_REQUEST)
540 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
541 				cmd==PRCO_GETOPT?"GET":"SET");
542 		ENDDEBUG
543 
544 		if ( cmd == PRCO_GETOPT ) {
545 			*(struct tp_conn_param *)value = tpcb->_tp_param;
546 			(*mp)->m_len = sizeof(tpcb->_tp_param);
547 		} else /* cmd == PRCO_SETOPT  */ {
548 			if( (error =
549 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
550 								(struct tp_conn_param *)value))==0) {
551 				/*
552 				 * tp_consistency doesn't copy the whole set of params
553 				 */
554 				tpcb->_tp_param = *(struct tp_conn_param *)value;
555 				(*mp)->m_len = sizeof(tpcb->_tp_param);
556 			}
557 		}
558 		break;
559 
560 	case TPOPT_PSTATISTICS:
561 #ifdef TP_PERF_MEAS
562 		if (cmd == PRCO_SETOPT) {
563 			error = EINVAL; goto done;
564 		}
565 		IFPERF(tpcb)
566 			if (*mp) {
567 				struct mbuf * n;
568 				do {
569 					MFREE(*mp, n);
570 					*mp = n;
571 				} while (n);
572 			}
573 			*mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
574 		ENDPERF
575 		else {
576 			error = EINVAL; goto done;
577 		}
578 		break;
579 #else
580 		error = EOPNOTSUPP;
581 		goto done;
582 #endif TP_PERF_MEAS
583 
584 	case TPOPT_CDDATA_CLEAR:
585 		if (cmd == PRCO_GETOPT) {
586 			error = EINVAL;
587 		} else {
588 			if (tpcb->tp_ucddata) {
589 				m_freem(tpcb->tp_ucddata);
590 				tpcb->tp_ucddata = 0;
591 			}
592 		}
593 		break;
594 
595 	case TPOPT_CFRM_DATA:
596 	case TPOPT_DISC_DATA:
597 	case TPOPT_CONN_DATA:
598 		if( tpcb->tp_class == TP_CLASS_0 ) {
599 			error = EOPNOTSUPP;
600 			break;
601 		}
602 		IFDEBUG(D_REQUEST)
603 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
604 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
605 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
606 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
607 		ENDDEBUG
608 		if (cmd == PRCO_SETOPT) {
609 			int len = tpcb->tp_ucddata ?  tpcb->tp_ucddata->m_len : 0;
610 			/* can append connect data in several calls */
611 			if (len + val_len >
612 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
613 				error = EMSGSIZE; goto done;
614 			}
615 			(*mp)->m_next = MNULL;
616 			(*mp)->m_act = 0;
617 			if (tpcb->tp_ucddata)
618 				m_cat(tpcb->tp_ucddata, *mp);
619 			else
620 				tpcb->tp_ucddata = *mp;
621 			IFDEBUG(D_REQUEST)
622 				dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
623 			ENDDEBUG
624 			IFTRACE(D_REQUEST)
625 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
626 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
627 			ENDTRACE
628 			*mp = MNULL;
629 			if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
630 				(void) tp_confirm(tpcb);
631 		}
632 		break;
633 
634 	case TPOPT_PERF_MEAS:
635 #ifdef TP_PERF_MEAS
636 		if (cmd == PRCO_GETOPT) {
637 			*value = (u_int)tpcb->tp_perf_on;
638 			(*mp)->m_len = sizeof(u_int);
639 		} else if (cmd == PRCO_SETOPT) {
640 			(*mp)->m_len = 0;
641 			if ((*value) != 0 && (*value) != 1 )
642 				error = EINVAL;
643 			else  tpcb->tp_perf_on = (*value);
644 		}
645 		if( tpcb->tp_perf_on )
646 			error = tp_setup_perf(tpcb);
647 #else  TP_PERF_MEAS
648 		error = EOPNOTSUPP;
649 #endif TP_PERF_MEAS
650 		break;
651 
652 	default:
653 		error = EOPNOTSUPP;
654 	}
655 
656 done:
657 	IFDEBUG(D_REQUEST)
658 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
659 		dump_mbuf(*mp, "tp_ctloutput *mp");
660 	ENDDEBUG
661 	/*
662 	 * sigh: getsockopt looks only at m_len : all output data must
663 	 * reside in the first mbuf
664 	 */
665 	if (*mp) {
666 		if (cmd == PRCO_SETOPT)
667 			m_freem(*mp);
668 		else {
669 			ASSERT ( m_compress(*mp, mp) <= MLEN );
670 			if (error)
671 				(*mp)->m_len = 0;
672 			IFDEBUG(D_REQUEST)
673 				dump_mbuf(*mp, "tp_ctloutput *mp after compress");
674 			ENDDEBUG
675 		}
676 	}
677 	splx(s);
678 	return error;
679 }
680