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