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