xref: /original-bsd/sys/netiso/tp_output.c (revision b0ceb3f2)
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  *
33  * In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
34  */
35 
36 #ifndef lint
37 static char *rcsid = "$Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $";
38 #endif lint
39 
40 #include "param.h"
41 #include "systm.h"
42 #include "mbuf.h"
43 #include "protosw.h"
44 #include "socket.h"
45 #include "socketvar.h"
46 #include "errno.h"
47 #include "types.h"
48 #include "time.h"
49 #include "tp_param.h"
50 #include "tp_user.h"
51 #include "tp_stat.h"
52 #include "tp_ip.h"
53 #include "tp_timer.h"
54 #include "argo_debug.h"
55 #include "tp_pcb.h"
56 #include "tp_trace.h"
57 
58 #define USERFLAGSMASK_G 0x0f00643b
59 #define USERFLAGSMASK_S 0x0f000432
60 #define TPDUSIZESHIFT 24
61 #define CLASSHIFT 16
62 
63 /*
64  * NAME: 	tp_consistency()
65  *
66  * CALLED FROM:
67  * 	tp_ctloutput(), tp_input()
68  *
69  * FUNCTION and ARGUMENTS:
70  * 	Checks the consistency of options and tpdusize with class,
71  *	using the parameters passed in via (param).
72  *	(cmd) may be TP_STRICT or TP_FORCE or both.
73  *  Force means it will set all the values in (tpcb) to those in
74  *  the input arguements iff no errors were encountered.
75  *  Strict means that no inconsistency will be tolerated.  If it's
76  *  not used, checksum and tpdusize inconsistencies will be tolerated.
77  *  The reason for this is that in some cases, when we're negotiating down
78  *	from class  4, these options should be changed but should not
79  *  cause negotiation to fail.
80  *
81  * RETURNS
82  *  E* or EOK
83  *  E* if the various parms aren't ok for a given class
84  *  EOK if they are ok for a given class
85  */
86 
87 int
88 tp_consistency( tpcb, cmd, param )
89 	u_int cmd;
90 	struct tp_conn_param *param;
91 	struct tp_pcb *tpcb;
92 {
93 	register int	error = EOK;
94 	int 			class_to_use  = tp_mask_to_num(param->p_class);
95 
96 	IFTRACE(D_SETPARAMS)
97 		tptrace(TPPTmisc,
98 		"tp_consist enter class_to_use dontchange param.class cmd",
99 		class_to_use, param->p_dont_change_params, param->p_class, cmd);
100 	ENDTRACE
101 	IFDEBUG(D_SETPARAMS)
102 		printf("tp_consistency %s %s\n",
103 			cmd& TP_FORCE?	"TP_FORCE":	"",
104 			cmd& TP_STRICT?	"TP_STRICT":"");
105 	ENDDEBUG
106 	if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
107 		cmd &= ~TP_FORCE;
108 	}
109 	/* can switch net services within a domain, but
110 	 * cannot switch domains
111 	 */
112 	switch( param->p_netservice) {
113 	case ISO_CONS:
114 	case ISO_CLNS:
115 	case ISO_COSNS:
116 		/* param->p_netservice in ISO DOMAIN */
117 		if(tpcb->tp_domain != AF_ISO ) {
118 			error = EINVAL; goto done;
119 		}
120 		break;
121 	case IN_CLNS:
122 		/* param->p_netservice in INET DOMAIN */
123 		if( tpcb->tp_domain != AF_INET ) {
124 			error = EINVAL; goto done;
125 		}
126 		break;
127 		/* no others not possible-> netservice is a 2-bit field! */
128 	}
129 
130 	IFDEBUG(D_SETPARAMS)
131 		printf("p_class 0x%x, class_to_use 0x%x\n",  param->p_class,
132 			class_to_use);
133 	ENDDEBUG
134 	if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){
135 		error = EINVAL; goto done;
136 	}
137 	if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
138 		error = EINVAL; goto done;
139 	}
140 	IFDEBUG(D_SETPARAMS)
141 		printf("Nretrans 0x%x\n",  param->p_Nretrans );
142 	ENDDEBUG
143 	if( ( param->p_Nretrans < 1 ) ||
144 		  (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
145 			/* bad for any class because negot has to be done a la class 4 */
146 			error = EINVAL; goto done;
147 	}
148 	IFDEBUG(D_SETPARAMS)
149 		printf("winsize 0x%x\n",  param->p_winsize );
150 	ENDDEBUG
151 	if( (param->p_winsize < 128 ) ||
152 		(param->p_winsize < param->p_tpdusize ) ||
153 		(param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
154 			error = EINVAL; goto done;
155 	} else {
156 		if( tpcb->tp_state == TP_CLOSED )
157 			soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
158 						(u_long)param->p_winsize);
159 	}
160 	IFDEBUG(D_SETPARAMS)
161 		printf("use_csum 0x%x\n",  param->p_use_checksum );
162 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
163 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
164 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
165 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
166 	ENDDEBUG
167 	switch( class_to_use ) {
168 
169 	case 0:
170 		/* do not use checksums, xtd format, or XPD */
171 
172 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
173 			if(cmd & TP_STRICT) {
174 				error = EINVAL;
175 			} else {
176 				param->p_use_checksum = 0;
177 				param->p_xtd_format = 0;
178 				param->p_xpd_service = 0;
179 			}
180 			break;
181 		}
182 
183 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
184 			if(cmd & TP_STRICT) {
185 				error = EINVAL;
186 			} else {
187 				param->p_tpdusize = TP_MIN_TPDUSIZE;
188 			}
189 			break;
190 		}
191 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
192 			if (cmd & TP_STRICT) {
193 				error = EINVAL;
194 			} else {
195 				param->p_tpdusize = TP0_TPDUSIZE;
196 			}
197 			break;
198 		}
199 
200 		/* connect/disc data not allowed for class 0 */
201 		if (tpcb->tp_ucddata) {
202 			if(cmd & TP_STRICT) {
203 				error = EINVAL;
204 			} else if(cmd & TP_FORCE) {
205 				m_freem(tpcb->tp_ucddata);
206 				tpcb->tp_ucddata = 0;
207 			}
208 		}
209 		break;
210 
211 	case 4:
212 		IFDEBUG(D_SETPARAMS)
213 			printf("dt_ticks 0x%x\n",  param->p_dt_ticks );
214 			printf("x_ticks 0x%x\n",  param->p_x_ticks );
215 			printf("dr_ticks 0x%x\n",  param->p_dr_ticks );
216 			printf("keepalive 0x%x\n",  param->p_keepalive_ticks );
217 			printf("sendack 0x%x\n",  param->p_sendack_ticks );
218 			printf("inact 0x%x\n",  param->p_inact_ticks );
219 			printf("ref 0x%x\n",  param->p_ref_ticks );
220 		ENDDEBUG
221 		if( (param->p_class & TP_CLASS_4 ) && (
222 			  (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
223 			  (param->p_x_ticks < 1)	|| (param->p_keepalive_ticks < 1) ||
224 			  (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
225 			  (param->p_inact_ticks < 1) ) ) {
226 				error = EINVAL;
227 				break;
228 		}
229 		IFDEBUG(D_SETPARAMS)
230 			printf("rx_strat 0x%x\n",  param->p_rx_strat );
231 		ENDDEBUG
232 		if(param->p_rx_strat >
233 			( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
234 				if(cmd & TP_STRICT) {
235 					error = EINVAL;
236 				} else {
237 					param->p_rx_strat = TPRX_USE_CW;
238 				}
239 				break;
240 		}
241 		IFDEBUG(D_SETPARAMS)
242 			printf("ack_strat 0x%x\n",  param->p_ack_strat );
243 		ENDDEBUG
244 		if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
245 			if(cmd & TP_STRICT) {
246 				error = EINVAL;
247 			} else {
248 				param->p_ack_strat = TPACK_WINDOW;
249 			}
250 			break;
251 		}
252 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
253 			if(cmd & TP_STRICT) {
254 				error = EINVAL;
255 			} else {
256 				param->p_tpdusize = TP_MIN_TPDUSIZE;
257 			}
258 			break;
259 		}
260 		if (param->p_tpdusize > TP_TPDUSIZE)  {
261 			if(cmd & TP_STRICT) {
262 				error = EINVAL;
263 			} else {
264 				param->p_tpdusize = TP_TPDUSIZE;
265 			}
266 			break;
267 		}
268 		break;
269 	}
270 
271 	if ((error==0) && (cmd & TP_FORCE)) {
272 		tpcb->tp_tpdusize = param->p_tpdusize;
273 		tpcb->tp_class = param->p_class;
274 		tpcb->tp_use_checksum = param->p_use_checksum;
275 		tpcb->tp_xpd_service = param->p_xpd_service;
276 		tpcb->tp_xtd_format = param->p_xtd_format;
277 	}
278 
279 done:
280 
281 	IFTRACE(D_CONN)
282 		tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
283 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
284 	ENDTRACE
285 	IFDEBUG(D_CONN)
286 		printf(
287 		"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
288 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
289 	ENDDEBUG
290 	return error;
291 }
292 
293 /*
294  * NAME: 	tp_ctloutput()
295  *
296  * CALLED FROM:
297  * 	[sg]etsockopt(), via so[sg]etopt().
298  *
299  * FUNCTION and ARGUMENTS:
300  * 	Implements the socket options at transport level.
301  * 	(cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
302  * 	(so) is the socket.
303  * 	(level) is SOL_TRANSPORT (see ../sys/socket.h)
304  * 	(optname) is the particular command or option to be set.
305  * 	(**mp) is an mbuf structure.
306  *
307  * RETURN VALUE:
308  * 	ENOTSOCK if the socket hasn't got an associated tpcb
309  *  EINVAL if
310  * 		trying to set window too big
311  * 		trying to set illegal max tpdu size
312  * 		trying to set illegal credit fraction
313  * 		trying to use unknown or unimplemented class of TP
314  *		structure passed to set timer values is wrong size
315  *  	illegal combination of command/GET-SET option,
316  *			e.g., GET w/ TPOPT_CDDATA_CLEAR:
317  *  EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
318  *   or if the transport-specific command is not implemented
319  *  EISCONN if trying a command that isn't allowed after a connection
320  *   is established
321  *  ENOTCONN if trying a command that is allowed only if a connection is
322  *   established
323  *  EMSGSIZE if trying to give too much data on connect/disconnect
324  *
325  * SIDE EFFECTS:
326  *
327  * NOTES:
328  */
329 ProtoHook
330 tp_ctloutput(cmd, so, level, optname, mp)
331 	int 			cmd, level, optname;
332 	struct socket	*so;
333 	struct mbuf 	**mp;
334 {
335 	struct		tp_pcb	*tpcb = sototpcb(so);
336 	int 		s = splnet();
337 	caddr_t		value;
338 	unsigned	val_len;
339 	int			error = 0;
340 
341 	IFTRACE(D_REQUEST)
342 		tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
343 			cmd, so, optname, mp);
344 	ENDTRACE
345 	IFDEBUG(D_REQUEST)
346 		printf(
347 	"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
348 			so, cmd, optname, mp, mp?*mp:0, tpcb);
349 	ENDDEBUG
350 	if( tpcb == (struct tp_pcb *)0 ) {
351 		error = ENOTSOCK; goto done;
352 	}
353 	if(*mp == MNULL) {
354 		register struct mbuf *m;
355 
356 		MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
357 		if (m == NULL) {
358 			splx(s);
359 			return ENOBUFS;
360 		}
361 		m->m_len = 0;
362 		m->m_act = 0;
363 		*mp = m;
364 	}
365 
366 	/*
367 	 *	Hook so one can set network options via a tp socket.
368 	 */
369 	if ( level == SOL_NETWORK ) {
370 		if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
371 			error = ENOTSOCK;
372 		else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
373 			error = EOPNOTSUPP;
374 		else
375 			error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
376 				tpcb->tp_npcb, *mp);
377 		goto done;
378 	} else if ( level !=  SOL_TRANSPORT ) {
379 		error = EOPNOTSUPP; goto done;
380 	}
381 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
382 		error = EOPNOTSUPP; goto done;
383 	}
384 	if ( so->so_error ) {
385 		error = so->so_error; goto done;
386 	}
387 
388 	/* The only options allowed after connection is established
389 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
390 	 */
391 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
392 		&&
393 		(cmd == PRCO_SETOPT  &&
394 			optname != TPOPT_DISC_DATA &&
395 			optname != TPOPT_PERF_MEAS &&
396 			optname != TPOPT_CDDATA_CLEAR ) ) {
397 		error = EISCONN; goto done;
398 	}
399 	/* The only options allowed after disconnection are GET DISC DATA,
400 	 * and TPOPT_PSTATISTICS
401 	 * and they're not allowed if the ref timer has gone off, because
402 	 * the tpcb is gone
403 	 */
404 	if ((so->so_state & SS_ISCONNECTED) ==  0) {
405 		if ( so->so_tpcb == (caddr_t)0 ) {
406 			error = ENOTCONN; goto done;
407 		}
408 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
409 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
410 			error = ENOTCONN; goto done;
411 		}
412 	}
413 
414 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
415 								  * but lint complains about it
416 								  */
417 	val_len = (*mp)->m_len;
418 
419 	switch (optname) {
420 
421 	case TPOPT_MY_TSEL:
422 		if ( cmd == PRCO_GETOPT ) {
423 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
424 			bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
425 			(*mp)->m_len = tpcb->tp_lsuffixlen;
426 		} else /* cmd == PRCO_SETOPT  */ {
427 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
428 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
429 				error = EINVAL;
430 			} else {
431 				bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
432 				tpcb->tp_lsuffixlen = val_len;
433 			}
434 		}
435 		break;
436 
437 	case TPOPT_PEER_TSEL:
438 		if ( cmd == PRCO_GETOPT ) {
439 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
440 			bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
441 			(*mp)->m_len = tpcb->tp_fsuffixlen;
442 		} else /* cmd == PRCO_SETOPT  */ {
443 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
444 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
445 				error = EINVAL;
446 			} else {
447 				bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
448 				tpcb->tp_fsuffixlen = val_len;
449 			}
450 		}
451 		break;
452 
453 	case TPOPT_FLAGS:
454 		IFDEBUG(D_REQUEST)
455 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
456 				cmd==PRCO_GETOPT?"GET":"SET",
457 				value,
458 				*value,
459 				tpcb->tp_flags);
460 		ENDDEBUG
461 
462 		if ( cmd == PRCO_GETOPT ) {
463 			*(int *)value = (int)tpcb->tp_flags;
464 			(*mp)->m_len = sizeof(u_int);
465 		} else /* cmd == PRCO_SETOPT  */ {
466 			error = EINVAL; goto done;
467 		}
468 		break;
469 
470 	case TPOPT_PARAMS:
471 		/* This handles:
472 		 * timer values,
473 		 * class, use of transport expedited data,
474 		 * max tpdu size, checksum, xtd format and
475 		 * disconnect indications, and may get rid of connect/disc data
476 		 */
477 		IFDEBUG(D_SETPARAMS)
478 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
479 				cmd==PRCO_GETOPT?"GET":"SET");
480 		ENDDEBUG
481 		IFDEBUG(D_REQUEST)
482 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
483 				cmd==PRCO_GETOPT?"GET":"SET");
484 		ENDDEBUG
485 
486 		if ( cmd == PRCO_GETOPT ) {
487 			*(struct tp_conn_param *)value = tpcb->_tp_param;
488 			(*mp)->m_len = sizeof(tpcb->_tp_param);
489 		} else /* cmd == PRCO_SETOPT  */ {
490 			if( (error =
491 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
492 								(struct tp_conn_param *)value))==0) {
493 				/*
494 				 * tp_consistency doesn't copy the whole set of params
495 				 */
496 				tpcb->_tp_param = *(struct tp_conn_param *)value;
497 				(*mp)->m_len = sizeof(tpcb->_tp_param);
498 			}
499 		}
500 		break;
501 
502 	case TPOPT_PSTATISTICS:
503 #ifdef TP_PERF_MEAS
504 		if (cmd == PRCO_SETOPT) {
505 			error = EINVAL; goto done;
506 		}
507 		IFPERF(tpcb)
508 			if (*mp) {
509 				struct mbuf * n;
510 				do {
511 					MFREE(*mp, n);
512 					*mp = n;
513 				} while (n);
514 			}
515 			*mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
516 		ENDPERF
517 		else {
518 			error = EINVAL; goto done;
519 		}
520 		break;
521 #else
522 		error = EOPNOTSUPP;
523 		goto done;
524 #endif TP_PERF_MEAS
525 
526 	case TPOPT_CDDATA_CLEAR:
527 		if (cmd == PRCO_GETOPT) {
528 			error = EINVAL;
529 		} else {
530 			if (tpcb->tp_ucddata) {
531 					m_freem(tpcb->tp_ucddata);
532 					tpcb->tp_ucddata = 0;
533 			}
534 		}
535 		break;
536 
537 	case TPOPT_DISC_DATA:
538 		/* drop through */
539 	/* sending is for debugging purposes only -- we don't pretend
540 	 * to support * data on connect or disconnect fully. It's a
541 	 * kludge at best.
542 	 * This data-on-connect is only for the active side.  It's sort of
543 	 * meaningless on the passive side (because
544 	 * you can't reject a connect request based on the data
545 	 * arriving w/ the CR, this, and because you'd have to
546 	 * do this setsockopt system call for each accept).
547 	 * but you can use it if you want.
548 	 */
549 	case TPOPT_CONN_DATA:
550 		if( tpcb->tp_class == TP_CLASS_0 ) {
551 			error = EOPNOTSUPP;
552 			break;
553 		}
554 		IFDEBUG(D_REQUEST)
555 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
556 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
557 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
558 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
559 		ENDDEBUG
560 		if (cmd == PRCO_SETOPT) {
561 			int len = tpcb->tp_ucddata ?  tpcb->tp_ucddata->m_len : 0;
562 			/* can append connect data in several calls */
563 			if (len + val_len >
564 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
565 				error = EMSGSIZE; goto done;
566 			}
567 			(*mp)->m_next = MNULL;
568 			(*mp)->m_act = 0;
569 			if (tpcb->tp_ucddata)
570 				m_cat(tpcb->tp_ucddata, *mp);
571 			else
572 				tpcb->tp_ucddata = *mp;
573 		IFDEBUG(D_REQUEST)
574 			dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
575 		ENDDEBUG
576 			IFTRACE(D_REQUEST)
577 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
578 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
579 			ENDTRACE
580 			*mp = MNULL; /* prevent sosetopt from freeing it! */
581 		}
582 		break;
583 
584 	case TPOPT_PERF_MEAS:
585 #ifdef TP_PERF_MEAS
586 		if (cmd == PRCO_GETOPT) {
587 			*value = (u_int)tpcb->tp_perf_on;
588 			(*mp)->m_len = sizeof(u_int);
589 		} else if (cmd == PRCO_SETOPT) {
590 			(*mp)->m_len = 0;
591 			if ((*value) != 0 && (*value) != 1 )
592 				error = EINVAL;
593 			else  tpcb->tp_perf_on = (*value);
594 		}
595 		if( tpcb->tp_perf_on )
596 			error = tp_setup_perf(tpcb);
597 #else  TP_PERF_MEAS
598 		error = EOPNOTSUPP;
599 #endif TP_PERF_MEAS
600 		break;
601 
602 	default:
603 		error = EOPNOTSUPP;
604 	}
605 
606 done:
607 	IFDEBUG(D_REQUEST)
608 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
609 		dump_mbuf(*mp, "tp_ctloutput *mp");
610 	ENDDEBUG
611 	/*
612 	 * sigh: getsockopt looks only at m_len : all output data must
613 	 * reside in the first mbuf
614 	 */
615 	if ( error  && (*mp) != MNULL )
616 		(*mp)->m_len = 0;
617 	if( (*mp) != MNULL ) {
618 		ASSERT ( m_compress(*mp, mp) <= MLEN );
619 		IFDEBUG(D_REQUEST)
620 			dump_mbuf(*mp, "tp_ctloutput *mp after compress");
621 		ENDDEBUG
622 	}
623 
624 	splx(s);
625 	return error;
626 }
627