xref: /csrg-svn/sys/netiso/tp_output.c (revision 36406)
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 "../netiso/tp_param.h"
50 #include "../netiso/tp_user.h"
51 #include "../netiso/tp_stat.h"
52 #include "../netiso/tp_ip.h"
53 #include "../netiso/tp_timer.h"
54 #include "../netiso/argo_debug.h"
55 #include "../netiso/tp_pcb.h"
56 #include "../netiso/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, param->p_winsize, param->p_winsize);
158 	}
159 	IFDEBUG(D_SETPARAMS)
160 		printf("use_csum 0x%x\n",  param->p_use_checksum );
161 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
162 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
163 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
164 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
165 	ENDDEBUG
166 	switch( class_to_use ) {
167 
168 	case 0:
169 		/* do not use checksums, xtd format, or XPD */
170 
171 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
172 			if(cmd & TP_STRICT) {
173 				error = EINVAL;
174 			} else {
175 				param->p_use_checksum = 0;
176 				param->p_xtd_format = 0;
177 				param->p_xpd_service = 0;
178 			}
179 			break;
180 		}
181 
182 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
183 			if(cmd & TP_STRICT) {
184 				error = EINVAL;
185 			} else {
186 				param->p_tpdusize = TP_MIN_TPDUSIZE;
187 			}
188 			break;
189 		}
190 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
191 			if (cmd & TP_STRICT) {
192 				error = EINVAL;
193 			} else {
194 				param->p_tpdusize = TP0_TPDUSIZE;
195 			}
196 			break;
197 		}
198 
199 		/* connect/disc data not allowed for class 0 */
200 		if ( tpcb->tp_flags &
201 			(TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT) ) {
202 			if(cmd & TP_STRICT) {
203 				error = EINVAL;
204 			} else if(cmd & TP_FORCE) {
205 				sbdrop(&tpcb->tp_sock->so_snd, tpcb->tp_sock->so_snd.sb_cc);
206 				tpcb->tp_flags &=
207 					~(TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT);
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 ../h/protosw.h).
303  * 	(so) is the socket.
304  * 	(level) is SOL_TRANSPORT (see ../h/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 	u_char 		*value;
339 	int			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_PERF_MEAS &&
397 			optname != TPOPT_CDDATA_CLEAR ) ) {
398 		error = EISCONN; goto done;
399 	}
400 	/* The only options allowed after disconnection are GET DISC DATA,
401 	 * and TPOPT_PSTATISTICS
402 	 * and they're not allowed if the ref timer has gone off, because
403 	 * the tpcb is gone
404 	 */
405 	if ((so->so_state & SS_ISCONNECTED) ==  0) {
406 		if ( so->so_tpcb == (caddr_t)0 ) {
407 			error = ENOTCONN; goto done;
408 		}
409 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
410 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
411 			error = ENOTCONN; goto done;
412 		}
413 	}
414 
415 	value = (u_char *) mtod(*mp, u_char *);  /* it's aligned, don't worry,
416 											* but lint complains about it
417 											*/
418 	val_len = (*mp)->m_len;
419 
420 	switch (optname) {
421 
422 	case TPOPT_MY_TSEL:
423 		if ( cmd == PRCO_GETOPT ) {
424 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
425 			bcopy( tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
426 			(*mp)->m_len = tpcb->tp_lsuffixlen;
427 		} else /* cmd == PRCO_SETOPT  */ {
428 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
429 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
430 				error = EINVAL;
431 			} else {
432 				bcopy( value, tpcb->tp_lsuffix, val_len );
433 				tpcb->tp_lsuffixlen = val_len;
434 			}
435 		}
436 		break;
437 
438 	case TPOPT_PEER_TSEL:
439 		if ( cmd == PRCO_GETOPT ) {
440 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
441 			bcopy( tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
442 			(*mp)->m_len = tpcb->tp_fsuffixlen;
443 		} else /* cmd == PRCO_SETOPT  */ {
444 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
445 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
446 				error = EINVAL;
447 			} else {
448 				bcopy( value, tpcb->tp_fsuffix, val_len );
449 				tpcb->tp_fsuffixlen = val_len;
450 			}
451 		}
452 		break;
453 
454 	case TPOPT_FLAGS:
455 		IFDEBUG(D_REQUEST)
456 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
457 				cmd==PRCO_GETOPT?"GET":"SET",
458 				value,
459 				*value,
460 				tpcb->tp_flags);
461 		ENDDEBUG
462 
463 		if ( cmd == PRCO_GETOPT ) {
464 			*(int *)value = (int)tpcb->tp_flags;
465 			(*mp)->m_len = sizeof(u_int);
466 		} else /* cmd == PRCO_SETOPT  */ {
467 			error = EINVAL; goto done;
468 		}
469 		break;
470 
471 	case TPOPT_PARAMS:
472 		/* This handles:
473 		 * timer values,
474 		 * class, use of transport expedited data,
475 		 * max tpdu size, checksum, xtd format and
476 		 * disconnect indications, and may get rid of connect/disc data
477 		 */
478 		IFDEBUG(D_SETPARAMS)
479 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
480 				cmd==PRCO_GETOPT?"GET":"SET");
481 		ENDDEBUG
482 		IFDEBUG(D_REQUEST)
483 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
484 				cmd==PRCO_GETOPT?"GET":"SET");
485 		ENDDEBUG
486 
487 		if ( cmd == PRCO_GETOPT ) {
488 			*(struct tp_conn_param *)value = tpcb->_tp_param;
489 			(*mp)->m_len = sizeof(tpcb->_tp_param);
490 		} else /* cmd == PRCO_SETOPT  */ {
491 			if( (error =
492 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
493 								(struct tp_conn_param *)value))==0) {
494 				/*
495 				 * tp_consistency doesn't copy the whole set of params
496 				 */
497 				tpcb->_tp_param = *(struct tp_conn_param *)value;
498 				(*mp)->m_len = sizeof(tpcb->_tp_param);
499 			}
500 		}
501 		break;
502 
503 	case TPOPT_PSTATISTICS:
504 #ifdef TP_PERF_MEAS
505 		if (cmd == PRCO_SETOPT) {
506 			error = EINVAL; goto done;
507 		}
508 		IFPERF(tpcb)
509 			/* tp_p_meas is a cluster : "copy" it */
510 			mclrefcnt[mtocl( (tpcb->tp_p_meas) )]++;
511 			(*mp)->m_off = (u_long)((int)tpcb->tp_p_meas - (int)(*mp));
512 			(*mp)->m_len = sizeof(struct tp_pmeas);
513 		ENDPERF
514 		else {
515 			error = EINVAL; goto done;
516 		}
517 		break;
518 #else
519 		error = EOPNOTSUPP;
520 		goto done;
521 #endif TP_PERF_MEAS
522 
523 	case TPOPT_CDDATA_CLEAR:
524 		if (cmd == PRCO_GETOPT) {
525 			error = EINVAL;
526 		} else {
527 			if ( tpcb->tp_flags & (TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT) ) {
528 				sbdrop(&so->so_snd, so->so_snd.sb_cc);
529 				tpcb->tp_flags &= ~(TPF_CONN_DATA_OUT | TPF_DISC_DATA_OUT);
530 			}
531 		}
532 		break;
533 
534 	case TPOPT_DISC_DATA:
535 		/* drop through */
536 	/* sending is for debugging purposes only -- we don't pretend
537 	 * to support * data on connect or disconnect fully. It's a
538 	 * kludge at best.
539 	 * This data-on-connect is only for the active side.  It's sort of
540 	 * meaningless on the passive side (because
541 	 * you can't reject a connect request based on the data
542 	 * arriving w/ the CR, this, and because you'd have to
543 	 * do this setsockopt system call for each accept).
544 	 * but you can use it if you want.
545 	 */
546 	case TPOPT_CONN_DATA:
547 		if( tpcb->tp_class == TP_CLASS_0 ) {
548 			error = EOPNOTSUPP;
549 			break;
550 		}
551 		IFDEBUG(D_REQUEST)
552 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
553 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
554 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
555 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
556 			dump_mbuf(tpcb->tp_Xrcv.sb_mb, "tp_ctlout: tpXrcv ");
557 		ENDDEBUG
558 		if (cmd == PRCO_SETOPT) {
559 			/* can append connect data in several calls */
560 			if (so->so_snd.sb_cc + val_len >
561 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
562 				error = EMSGSIZE; goto done;
563 			}
564 			tpcb->tp_flags |=
565 			((optname==TPOPT_CONN_DATA)?TPF_CONN_DATA_OUT:TPF_DISC_DATA_OUT);
566 			(*mp)->m_next = MNULL;
567 			(*mp)->m_act = 0;
568 			sbappendrecord( &so->so_snd, *mp);
569 		IFDEBUG(D_REQUEST)
570 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput after sbappendrecord");
571 		ENDDEBUG
572 			IFTRACE(D_REQUEST)
573 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
574 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
575 			ENDTRACE
576 			*mp = MNULL; /* prevent sosetopt from freeing it! */
577 		} else /* cmd == PRCO_GETOPT */ {
578 			register int len = tpcb->tp_Xrcv.sb_cc;
579 
580 			/* getsockopt() allocated an mbuf but it's a whole lot easier
581 			 * to do an m_copy than to explicitly copy from the socket buf
582 			 * into the buffer provided by getsockopt()
583 			 */
584 			IFDEBUG(D_REQUEST)
585 				dump_mbuf(tpcb->tp_Xrcv.sb_mb,
586 					"tp_ctlout: tpXrcv before sbdrop");
587 			ENDDEBUG
588 			if(len) {
589 				(void) m_freem(*mp);
590 				*mp = m_copy( tpcb->tp_Xrcv.sb_mb, 0, len);
591 				if( *mp != MNULL ) {
592 					(*mp)->m_act = 0;
593 					sbdrop( &tpcb->tp_Xrcv, len);
594 				} else {
595 					error = ENOBUFS;
596 				}
597 			} else {
598 				(*mp)->m_len = 0;
599 			}
600 			IFDEBUG(D_REQUEST)
601 				dump_mbuf(tpcb->tp_Xrcv.sb_mb,
602 					"tp_ctlout: tpXrcv after sbdrop");
603 			ENDDEBUG
604 			/* a potential problem here is that REAL expedited may have arrived
605 			 * after the data-on-connect
606 			 * however, this presently works because incoming XPD_TPDUs are
607 			 * dropped if tp_Xrcv.sb_cc != 0
608 			 */
609 
610 			if( tpcb->tp_Xrcv.sb_cc == 0)
611 				tpcb->tp_flags &=
612 				optname == TPOPT_CONN_DATA?~TPF_CONN_DATA_IN:~TPF_DISC_DATA_IN;
613 		}
614 		break;
615 
616 	case TPOPT_PERF_MEAS:
617 #ifdef TP_PERF_MEAS
618 		if (cmd == PRCO_GETOPT) {
619 			*value = (u_int)tpcb->tp_perf_on;
620 			(*mp)->m_len = sizeof(u_int);
621 		} else if (cmd == PRCO_SETOPT) {
622 			(*mp)->m_len = 0;
623 			if ((*value) != 0 && (*value) != 1 )
624 				error = EINVAL;
625 			else  tpcb->tp_perf_on = (*value);
626 		}
627 		if( tpcb->tp_perf_on )
628 			error = tp_setup_perf(tpcb);
629 #else  TP_PERF_MEAS
630 		error = EOPNOTSUPP;
631 #endif TP_PERF_MEAS
632 		break;
633 
634 	default:
635 		error = EOPNOTSUPP;
636 	}
637 
638 done:
639 	IFDEBUG(D_REQUEST)
640 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
641 		dump_mbuf(*mp, "tp_ctloutput *mp");
642 	ENDDEBUG
643 	/*
644 	 * sigh: getsockopt looks only at m_len : all output data must
645 	 * reside in the first mbuf
646 	 */
647 	if ( error  && (*mp) != MNULL )
648 		(*mp)->m_len = 0;
649 	if( (*mp) != MNULL ) {
650 		ASSERT ( m_compress(*mp, mp) <= MLEN );
651 		IFDEBUG(D_REQUEST)
652 			dump_mbuf(*mp, "tp_ctloutput *mp after compress");
653 		ENDDEBUG
654 	}
655 
656 	splx(s);
657 	return error;
658 }
659