xref: /csrg-svn/sys/netiso/tp_output.c (revision 47282)
136406Ssklower /***********************************************************
236406Ssklower 		Copyright IBM Corporation 1987
336406Ssklower 
436406Ssklower                       All Rights Reserved
536406Ssklower 
636406Ssklower Permission to use, copy, modify, and distribute this software and its
736406Ssklower documentation for any purpose and without fee is hereby granted,
836406Ssklower provided that the above copyright notice appear in all copies and that
936406Ssklower both that copyright notice and this permission notice appear in
1036406Ssklower supporting documentation, and that the name of IBM not be
1136406Ssklower used in advertising or publicity pertaining to distribution of the
1236406Ssklower software without specific, written prior permission.
1336406Ssklower 
1436406Ssklower IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
1536406Ssklower ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
1636406Ssklower IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
1736406Ssklower ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
1836406Ssklower WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
1936406Ssklower ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2036406Ssklower SOFTWARE.
2136406Ssklower 
2236406Ssklower ******************************************************************/
2336406Ssklower 
2436406Ssklower /*
2536406Ssklower  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
2636406Ssklower  */
2736406Ssklower /*
2836406Ssklower  * ARGO TP
2936406Ssklower  *
3036406Ssklower  * $Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $
3136406Ssklower  * $Source: /usr/argo/sys/netiso/RCS/tp_output.c,v $
32*47282Ssklower  *	@(#)tp_output.c	7.8 (Berkeley) 03/12/91 *
3336406Ssklower  *
3436406Ssklower  * In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
3536406Ssklower  */
3636406Ssklower 
3736406Ssklower #ifndef lint
3836406Ssklower static char *rcsid = "$Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $";
3936406Ssklower #endif lint
4036406Ssklower 
4136406Ssklower #include "param.h"
4244424Ssklower #include "mbuf.h"
4336406Ssklower #include "systm.h"
4436406Ssklower #include "socket.h"
4536406Ssklower #include "socketvar.h"
4644424Ssklower #include "protosw.h"
4744424Ssklower #include "user.h"
4844424Ssklower #include "kernel.h"
4936406Ssklower #include "errno.h"
5036406Ssklower #include "time.h"
5137469Ssklower #include "tp_param.h"
5237469Ssklower #include "tp_user.h"
5337469Ssklower #include "tp_stat.h"
5437469Ssklower #include "tp_ip.h"
5544424Ssklower #include "tp_clnp.h"
5637469Ssklower #include "tp_timer.h"
5737469Ssklower #include "argo_debug.h"
5837469Ssklower #include "tp_pcb.h"
5937469Ssklower #include "tp_trace.h"
6036406Ssklower 
6136406Ssklower #define USERFLAGSMASK_G 0x0f00643b
6236406Ssklower #define USERFLAGSMASK_S 0x0f000432
6336406Ssklower #define TPDUSIZESHIFT 24
6436406Ssklower #define CLASSHIFT 16
6536406Ssklower 
6636406Ssklower /*
6736406Ssklower  * NAME: 	tp_consistency()
6836406Ssklower  *
6936406Ssklower  * CALLED FROM:
7036406Ssklower  * 	tp_ctloutput(), tp_input()
7136406Ssklower  *
7236406Ssklower  * FUNCTION and ARGUMENTS:
7336406Ssklower  * 	Checks the consistency of options and tpdusize with class,
7436406Ssklower  *	using the parameters passed in via (param).
7536406Ssklower  *	(cmd) may be TP_STRICT or TP_FORCE or both.
7636406Ssklower  *  Force means it will set all the values in (tpcb) to those in
7736406Ssklower  *  the input arguements iff no errors were encountered.
7836406Ssklower  *  Strict means that no inconsistency will be tolerated.  If it's
7936406Ssklower  *  not used, checksum and tpdusize inconsistencies will be tolerated.
8036406Ssklower  *  The reason for this is that in some cases, when we're negotiating down
8136406Ssklower  *	from class  4, these options should be changed but should not
8236406Ssklower  *  cause negotiation to fail.
8336406Ssklower  *
8436406Ssklower  * RETURNS
8536406Ssklower  *  E* or EOK
8636406Ssklower  *  E* if the various parms aren't ok for a given class
8736406Ssklower  *  EOK if they are ok for a given class
8836406Ssklower  */
8936406Ssklower 
9036406Ssklower int
9136406Ssklower tp_consistency( tpcb, cmd, param )
9236406Ssklower 	u_int cmd;
9336406Ssklower 	struct tp_conn_param *param;
9436406Ssklower 	struct tp_pcb *tpcb;
9536406Ssklower {
9636406Ssklower 	register int	error = EOK;
9736406Ssklower 	int 			class_to_use  = tp_mask_to_num(param->p_class);
9836406Ssklower 
9936406Ssklower 	IFTRACE(D_SETPARAMS)
10036406Ssklower 		tptrace(TPPTmisc,
10136406Ssklower 		"tp_consist enter class_to_use dontchange param.class cmd",
10236406Ssklower 		class_to_use, param->p_dont_change_params, param->p_class, cmd);
10336406Ssklower 	ENDTRACE
10436406Ssklower 	IFDEBUG(D_SETPARAMS)
10536406Ssklower 		printf("tp_consistency %s %s\n",
10636406Ssklower 			cmd& TP_FORCE?	"TP_FORCE":	"",
10736406Ssklower 			cmd& TP_STRICT?	"TP_STRICT":"");
10836406Ssklower 	ENDDEBUG
10936406Ssklower 	if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
11036406Ssklower 		cmd &= ~TP_FORCE;
11136406Ssklower 	}
11236406Ssklower 	/* can switch net services within a domain, but
11336406Ssklower 	 * cannot switch domains
11436406Ssklower 	 */
11536406Ssklower 	switch( param->p_netservice) {
11636406Ssklower 	case ISO_CONS:
11736406Ssklower 	case ISO_CLNS:
11836406Ssklower 	case ISO_COSNS:
11936406Ssklower 		/* param->p_netservice in ISO DOMAIN */
12036406Ssklower 		if(tpcb->tp_domain != AF_ISO ) {
12136406Ssklower 			error = EINVAL; goto done;
12236406Ssklower 		}
12336406Ssklower 		break;
12436406Ssklower 	case IN_CLNS:
12536406Ssklower 		/* param->p_netservice in INET DOMAIN */
12636406Ssklower 		if( tpcb->tp_domain != AF_INET ) {
12736406Ssklower 			error = EINVAL; goto done;
12836406Ssklower 		}
12936406Ssklower 		break;
13036406Ssklower 		/* no others not possible-> netservice is a 2-bit field! */
13136406Ssklower 	}
13236406Ssklower 
13336406Ssklower 	IFDEBUG(D_SETPARAMS)
13436406Ssklower 		printf("p_class 0x%x, class_to_use 0x%x\n",  param->p_class,
13536406Ssklower 			class_to_use);
13636406Ssklower 	ENDDEBUG
13736406Ssklower 	if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){
13836406Ssklower 		error = EINVAL; goto done;
13936406Ssklower 	}
14036406Ssklower 	if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
14136406Ssklower 		error = EINVAL; goto done;
14236406Ssklower 	}
14336406Ssklower 	IFDEBUG(D_SETPARAMS)
14436406Ssklower 		printf("Nretrans 0x%x\n",  param->p_Nretrans );
14536406Ssklower 	ENDDEBUG
14636406Ssklower 	if( ( param->p_Nretrans < 1 ) ||
14736406Ssklower 		  (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
14836406Ssklower 			/* bad for any class because negot has to be done a la class 4 */
14936406Ssklower 			error = EINVAL; goto done;
15036406Ssklower 	}
15136406Ssklower 	IFDEBUG(D_SETPARAMS)
15236406Ssklower 		printf("winsize 0x%x\n",  param->p_winsize );
15336406Ssklower 	ENDDEBUG
15436406Ssklower 	if( (param->p_winsize < 128 ) ||
15536406Ssklower 		(param->p_winsize < param->p_tpdusize ) ||
15636406Ssklower 		(param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
15736406Ssklower 			error = EINVAL; goto done;
15836406Ssklower 	} else {
15936406Ssklower 		if( tpcb->tp_state == TP_CLOSED )
16037469Ssklower 			soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
16137469Ssklower 						(u_long)param->p_winsize);
16236406Ssklower 	}
16336406Ssklower 	IFDEBUG(D_SETPARAMS)
16436406Ssklower 		printf("use_csum 0x%x\n",  param->p_use_checksum );
16536406Ssklower 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
16636406Ssklower 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
16736406Ssklower 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
16836406Ssklower 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
16936406Ssklower 	ENDDEBUG
17036406Ssklower 	switch( class_to_use ) {
17136406Ssklower 
17236406Ssklower 	case 0:
17336406Ssklower 		/* do not use checksums, xtd format, or XPD */
17436406Ssklower 
17536406Ssklower 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
17636406Ssklower 			if(cmd & TP_STRICT) {
17736406Ssklower 				error = EINVAL;
17836406Ssklower 			} else {
17936406Ssklower 				param->p_use_checksum = 0;
18036406Ssklower 				param->p_xtd_format = 0;
18136406Ssklower 				param->p_xpd_service = 0;
18236406Ssklower 			}
18336406Ssklower 			break;
18436406Ssklower 		}
18536406Ssklower 
18636406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
18736406Ssklower 			if(cmd & TP_STRICT) {
18836406Ssklower 				error = EINVAL;
18936406Ssklower 			} else {
19036406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
19136406Ssklower 			}
19236406Ssklower 			break;
19336406Ssklower 		}
19436406Ssklower 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
19536406Ssklower 			if (cmd & TP_STRICT) {
19636406Ssklower 				error = EINVAL;
19736406Ssklower 			} else {
19836406Ssklower 				param->p_tpdusize = TP0_TPDUSIZE;
19936406Ssklower 			}
20036406Ssklower 			break;
20136406Ssklower 		}
20236406Ssklower 
20336406Ssklower 		/* connect/disc data not allowed for class 0 */
20437469Ssklower 		if (tpcb->tp_ucddata) {
20536406Ssklower 			if(cmd & TP_STRICT) {
20636406Ssklower 				error = EINVAL;
20736406Ssklower 			} else if(cmd & TP_FORCE) {
20837469Ssklower 				m_freem(tpcb->tp_ucddata);
20937469Ssklower 				tpcb->tp_ucddata = 0;
21036406Ssklower 			}
21136406Ssklower 		}
21236406Ssklower 		break;
21336406Ssklower 
21436406Ssklower 	case 4:
21536406Ssklower 		IFDEBUG(D_SETPARAMS)
21636406Ssklower 			printf("dt_ticks 0x%x\n",  param->p_dt_ticks );
21736406Ssklower 			printf("x_ticks 0x%x\n",  param->p_x_ticks );
21836406Ssklower 			printf("dr_ticks 0x%x\n",  param->p_dr_ticks );
21936406Ssklower 			printf("keepalive 0x%x\n",  param->p_keepalive_ticks );
22036406Ssklower 			printf("sendack 0x%x\n",  param->p_sendack_ticks );
22136406Ssklower 			printf("inact 0x%x\n",  param->p_inact_ticks );
22236406Ssklower 			printf("ref 0x%x\n",  param->p_ref_ticks );
22336406Ssklower 		ENDDEBUG
22436406Ssklower 		if( (param->p_class & TP_CLASS_4 ) && (
22536406Ssklower 			  (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
22636406Ssklower 			  (param->p_x_ticks < 1)	|| (param->p_keepalive_ticks < 1) ||
22736406Ssklower 			  (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
22836406Ssklower 			  (param->p_inact_ticks < 1) ) ) {
22936406Ssklower 				error = EINVAL;
23036406Ssklower 				break;
23136406Ssklower 		}
23236406Ssklower 		IFDEBUG(D_SETPARAMS)
23336406Ssklower 			printf("rx_strat 0x%x\n",  param->p_rx_strat );
23436406Ssklower 		ENDDEBUG
23536406Ssklower 		if(param->p_rx_strat >
23636406Ssklower 			( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
23736406Ssklower 				if(cmd & TP_STRICT) {
23836406Ssklower 					error = EINVAL;
23936406Ssklower 				} else {
24036406Ssklower 					param->p_rx_strat = TPRX_USE_CW;
24136406Ssklower 				}
24236406Ssklower 				break;
24336406Ssklower 		}
24436406Ssklower 		IFDEBUG(D_SETPARAMS)
24536406Ssklower 			printf("ack_strat 0x%x\n",  param->p_ack_strat );
24636406Ssklower 		ENDDEBUG
24736406Ssklower 		if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
24836406Ssklower 			if(cmd & TP_STRICT) {
24936406Ssklower 				error = EINVAL;
25036406Ssklower 			} else {
25136406Ssklower 				param->p_ack_strat = TPACK_WINDOW;
25236406Ssklower 			}
25336406Ssklower 			break;
25436406Ssklower 		}
25536406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
25636406Ssklower 			if(cmd & TP_STRICT) {
25736406Ssklower 				error = EINVAL;
25836406Ssklower 			} else {
25936406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
26036406Ssklower 			}
26136406Ssklower 			break;
26236406Ssklower 		}
26336406Ssklower 		if (param->p_tpdusize > TP_TPDUSIZE)  {
26436406Ssklower 			if(cmd & TP_STRICT) {
26536406Ssklower 				error = EINVAL;
26636406Ssklower 			} else {
26736406Ssklower 				param->p_tpdusize = TP_TPDUSIZE;
26836406Ssklower 			}
26936406Ssklower 			break;
27036406Ssklower 		}
27136406Ssklower 		break;
27236406Ssklower 	}
27336406Ssklower 
27436406Ssklower 	if ((error==0) && (cmd & TP_FORCE)) {
275*47282Ssklower 		/* Enforce Negotation rules below */
276*47282Ssklower 		if (tpcb->tp_tpdusize > param->p_tpdusize)
277*47282Ssklower 			tpcb->tp_tpdusize = param->p_tpdusize;
27836406Ssklower 		tpcb->tp_class = param->p_class;
279*47282Ssklower 		if (tpcb->tp_use_checksum || param->p_use_checksum)
280*47282Ssklower 			tpcb->tp_use_checksum = 1;
281*47282Ssklower 		if (!tpcb->tp_xpd_service || !param->p_xpd_service)
282*47282Ssklower 			tpcb->tp_xpd_service = 0;
283*47282Ssklower 		if (!tpcb->tp_xtd_format || !param->p_xtd_format)
284*47282Ssklower 			tpcb->tp_xtd_format = 0;
28536406Ssklower 	}
28636406Ssklower 
28736406Ssklower done:
28836406Ssklower 
28936406Ssklower 	IFTRACE(D_CONN)
29036406Ssklower 		tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
29136406Ssklower 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
29236406Ssklower 	ENDTRACE
29336406Ssklower 	IFDEBUG(D_CONN)
29436406Ssklower 		printf(
29536406Ssklower 		"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
29636406Ssklower 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
29736406Ssklower 	ENDDEBUG
29836406Ssklower 	return error;
29936406Ssklower }
30036406Ssklower 
30136406Ssklower /*
30236406Ssklower  * NAME: 	tp_ctloutput()
30336406Ssklower  *
30436406Ssklower  * CALLED FROM:
30536406Ssklower  * 	[sg]etsockopt(), via so[sg]etopt().
30636406Ssklower  *
30736406Ssklower  * FUNCTION and ARGUMENTS:
30836406Ssklower  * 	Implements the socket options at transport level.
30937536Smckusick  * 	(cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
31036406Ssklower  * 	(so) is the socket.
31137536Smckusick  * 	(level) is SOL_TRANSPORT (see ../sys/socket.h)
31236406Ssklower  * 	(optname) is the particular command or option to be set.
31336406Ssklower  * 	(**mp) is an mbuf structure.
31436406Ssklower  *
31536406Ssklower  * RETURN VALUE:
31636406Ssklower  * 	ENOTSOCK if the socket hasn't got an associated tpcb
31736406Ssklower  *  EINVAL if
31836406Ssklower  * 		trying to set window too big
31936406Ssklower  * 		trying to set illegal max tpdu size
32036406Ssklower  * 		trying to set illegal credit fraction
32136406Ssklower  * 		trying to use unknown or unimplemented class of TP
32236406Ssklower  *		structure passed to set timer values is wrong size
32336406Ssklower  *  	illegal combination of command/GET-SET option,
32436406Ssklower  *			e.g., GET w/ TPOPT_CDDATA_CLEAR:
32536406Ssklower  *  EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
32636406Ssklower  *   or if the transport-specific command is not implemented
32736406Ssklower  *  EISCONN if trying a command that isn't allowed after a connection
32836406Ssklower  *   is established
32936406Ssklower  *  ENOTCONN if trying a command that is allowed only if a connection is
33036406Ssklower  *   established
33136406Ssklower  *  EMSGSIZE if trying to give too much data on connect/disconnect
33236406Ssklower  *
33336406Ssklower  * SIDE EFFECTS:
33436406Ssklower  *
33536406Ssklower  * NOTES:
33636406Ssklower  */
33736406Ssklower ProtoHook
33836406Ssklower tp_ctloutput(cmd, so, level, optname, mp)
33936406Ssklower 	int 			cmd, level, optname;
34036406Ssklower 	struct socket	*so;
34136406Ssklower 	struct mbuf 	**mp;
34236406Ssklower {
34336406Ssklower 	struct		tp_pcb	*tpcb = sototpcb(so);
34436406Ssklower 	int 		s = splnet();
34537469Ssklower 	caddr_t		value;
34637469Ssklower 	unsigned	val_len;
34736406Ssklower 	int			error = 0;
34836406Ssklower 
34936406Ssklower 	IFTRACE(D_REQUEST)
35036406Ssklower 		tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
35136406Ssklower 			cmd, so, optname, mp);
35236406Ssklower 	ENDTRACE
35336406Ssklower 	IFDEBUG(D_REQUEST)
35436406Ssklower 		printf(
35536406Ssklower 	"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
35636406Ssklower 			so, cmd, optname, mp, mp?*mp:0, tpcb);
35736406Ssklower 	ENDDEBUG
35836406Ssklower 	if( tpcb == (struct tp_pcb *)0 ) {
35936406Ssklower 		error = ENOTSOCK; goto done;
36036406Ssklower 	}
36136406Ssklower 	if(*mp == MNULL) {
36236406Ssklower 		register struct mbuf *m;
36336406Ssklower 
36436406Ssklower 		MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
36536406Ssklower 		if (m == NULL) {
36636406Ssklower 			splx(s);
36736406Ssklower 			return ENOBUFS;
36836406Ssklower 		}
36936406Ssklower 		m->m_len = 0;
37036406Ssklower 		m->m_act = 0;
37136406Ssklower 		*mp = m;
37236406Ssklower 	}
37336406Ssklower 
37436406Ssklower 	/*
37536406Ssklower 	 *	Hook so one can set network options via a tp socket.
37636406Ssklower 	 */
37736406Ssklower 	if ( level == SOL_NETWORK ) {
37836406Ssklower 		if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
37936406Ssklower 			error = ENOTSOCK;
38036406Ssklower 		else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
38136406Ssklower 			error = EOPNOTSUPP;
38236406Ssklower 		else
38336406Ssklower 			error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
38436406Ssklower 				tpcb->tp_npcb, *mp);
38536406Ssklower 		goto done;
38636406Ssklower 	} else if ( level !=  SOL_TRANSPORT ) {
38736406Ssklower 		error = EOPNOTSUPP; goto done;
38836406Ssklower 	}
38936406Ssklower 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
39036406Ssklower 		error = EOPNOTSUPP; goto done;
39136406Ssklower 	}
39236406Ssklower 	if ( so->so_error ) {
39336406Ssklower 		error = so->so_error; goto done;
39436406Ssklower 	}
39536406Ssklower 
39636406Ssklower 	/* The only options allowed after connection is established
39736406Ssklower 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
39836406Ssklower 	 */
39936406Ssklower 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
40036406Ssklower 		&&
40136406Ssklower 		(cmd == PRCO_SETOPT  &&
40236406Ssklower 			optname != TPOPT_DISC_DATA &&
40338841Ssklower 			optname != TPOPT_CFRM_DATA &&
40436406Ssklower 			optname != TPOPT_PERF_MEAS &&
40536406Ssklower 			optname != TPOPT_CDDATA_CLEAR ) ) {
40636406Ssklower 		error = EISCONN; goto done;
40736406Ssklower 	}
40836406Ssklower 	/* The only options allowed after disconnection are GET DISC DATA,
40936406Ssklower 	 * and TPOPT_PSTATISTICS
41036406Ssklower 	 * and they're not allowed if the ref timer has gone off, because
41136406Ssklower 	 * the tpcb is gone
41236406Ssklower 	 */
41338841Ssklower 	if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) ==  0) {
41436406Ssklower 		if ( so->so_tpcb == (caddr_t)0 ) {
41536406Ssklower 			error = ENOTCONN; goto done;
41636406Ssklower 		}
41736406Ssklower 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
41836406Ssklower 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
41936406Ssklower 			error = ENOTCONN; goto done;
42036406Ssklower 		}
42136406Ssklower 	}
42236406Ssklower 
42337469Ssklower 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
42437469Ssklower 								  * but lint complains about it
42537469Ssklower 								  */
42636406Ssklower 	val_len = (*mp)->m_len;
42736406Ssklower 
42836406Ssklower 	switch (optname) {
42936406Ssklower 
43044424Ssklower 	case TPOPT_INTERCEPT:
43144424Ssklower 		if (error = suser(u.u_cred, &u.u_acflag))
43244424Ssklower 			break;
43344424Ssklower 		else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_LISTENING)
43444424Ssklower 			error = EINVAL;
43544424Ssklower 		else {
43644424Ssklower 			register struct tp_pcb *t = 0;
43744424Ssklower 			struct mbuf *m = m_getclr(M_WAIT, MT_SONAME);
43844424Ssklower 			struct sockaddr *sa = mtod(m, struct sockaddr *);
43944424Ssklower 			(*tpcb->tp_nlproto->nlp_getnetaddr)(tpcb->tp_npcb, m, TP_LOCAL);
44044424Ssklower 			switch (sa->sa_family) {
44144424Ssklower 			case AF_ISO:
44244424Ssklower 				if (((struct sockaddr_iso *)sa)->siso_nlen == 0)
44344424Ssklower 					default: error = EINVAL;
44444424Ssklower 				break;
44544424Ssklower 			case AF_INET:
44644424Ssklower 				if (((struct sockaddr_in *)sa)->sin_addr.s_addr == 0)
44744424Ssklower 					error = EINVAL;
44844424Ssklower 				break;
44944424Ssklower 			}
45044424Ssklower 			for (t = tp_intercepts; t; t = t->tp_nextlisten) {
45144424Ssklower 				if (t->tp_nlproto->nlp_afamily != tpcb->tp_nlproto->nlp_afamily)
45244424Ssklower 					continue;
45344424Ssklower 				if ((*t->tp_nlproto->nlp_cmpnetaddr)(t->tp_npcb, sa, TP_LOCAL))
45444424Ssklower 					error = EADDRINUSE;
45544424Ssklower 			}
45644424Ssklower 			m_freem(m);
45744424Ssklower 			if (error)
45844424Ssklower 				break;
45944424Ssklower 		}
46044424Ssklower 		{
46144424Ssklower 			register struct tp_pcb **tt;
46244424Ssklower 			for (tt = &tp_listeners; *tt; tt = &((*tt)->tp_nextlisten))
46344424Ssklower 				if (*tt == tpcb)
46444424Ssklower 					break;
46544424Ssklower 			if (*tt)
46644424Ssklower 				*tt = tpcb->tp_nextlisten;
46744424Ssklower 			else
46844424Ssklower 				{error = EHOSTUNREACH; goto done; }
46944424Ssklower 		}
47044601Ssklower 		tpcb->tp_nextlisten = tp_intercepts;
47144424Ssklower 		tp_intercepts = tpcb;
47244424Ssklower 		break;
47344424Ssklower 
47436406Ssklower 	case TPOPT_MY_TSEL:
47536406Ssklower 		if ( cmd == PRCO_GETOPT ) {
47636406Ssklower 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
47737469Ssklower 			bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
47836406Ssklower 			(*mp)->m_len = tpcb->tp_lsuffixlen;
47936406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
48036406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
48136406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
48236406Ssklower 				error = EINVAL;
48336406Ssklower 			} else {
48437469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
48536406Ssklower 				tpcb->tp_lsuffixlen = val_len;
48636406Ssklower 			}
48736406Ssklower 		}
48836406Ssklower 		break;
48936406Ssklower 
49036406Ssklower 	case TPOPT_PEER_TSEL:
49136406Ssklower 		if ( cmd == PRCO_GETOPT ) {
49236406Ssklower 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
49337469Ssklower 			bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
49436406Ssklower 			(*mp)->m_len = tpcb->tp_fsuffixlen;
49536406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
49636406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
49736406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
49836406Ssklower 				error = EINVAL;
49936406Ssklower 			} else {
50037469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
50136406Ssklower 				tpcb->tp_fsuffixlen = val_len;
50236406Ssklower 			}
50336406Ssklower 		}
50436406Ssklower 		break;
50536406Ssklower 
50636406Ssklower 	case TPOPT_FLAGS:
50736406Ssklower 		IFDEBUG(D_REQUEST)
50836406Ssklower 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
50936406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET",
51036406Ssklower 				value,
51136406Ssklower 				*value,
51236406Ssklower 				tpcb->tp_flags);
51336406Ssklower 		ENDDEBUG
51436406Ssklower 
51536406Ssklower 		if ( cmd == PRCO_GETOPT ) {
51636406Ssklower 			*(int *)value = (int)tpcb->tp_flags;
51736406Ssklower 			(*mp)->m_len = sizeof(u_int);
51836406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
51936406Ssklower 			error = EINVAL; goto done;
52036406Ssklower 		}
52136406Ssklower 		break;
52236406Ssklower 
52336406Ssklower 	case TPOPT_PARAMS:
52436406Ssklower 		/* This handles:
52536406Ssklower 		 * timer values,
52636406Ssklower 		 * class, use of transport expedited data,
52736406Ssklower 		 * max tpdu size, checksum, xtd format and
52836406Ssklower 		 * disconnect indications, and may get rid of connect/disc data
52936406Ssklower 		 */
53036406Ssklower 		IFDEBUG(D_SETPARAMS)
53136406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
53236406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
53336406Ssklower 		ENDDEBUG
53436406Ssklower 		IFDEBUG(D_REQUEST)
53536406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
53636406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
53736406Ssklower 		ENDDEBUG
53836406Ssklower 
53936406Ssklower 		if ( cmd == PRCO_GETOPT ) {
54036406Ssklower 			*(struct tp_conn_param *)value = tpcb->_tp_param;
54136406Ssklower 			(*mp)->m_len = sizeof(tpcb->_tp_param);
54236406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
54336406Ssklower 			if( (error =
54436406Ssklower 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
54536406Ssklower 								(struct tp_conn_param *)value))==0) {
54636406Ssklower 				/*
54736406Ssklower 				 * tp_consistency doesn't copy the whole set of params
54836406Ssklower 				 */
54936406Ssklower 				tpcb->_tp_param = *(struct tp_conn_param *)value;
55036406Ssklower 				(*mp)->m_len = sizeof(tpcb->_tp_param);
55136406Ssklower 			}
55236406Ssklower 		}
55336406Ssklower 		break;
55436406Ssklower 
55536406Ssklower 	case TPOPT_PSTATISTICS:
55636406Ssklower #ifdef TP_PERF_MEAS
55736406Ssklower 		if (cmd == PRCO_SETOPT) {
55836406Ssklower 			error = EINVAL; goto done;
55936406Ssklower 		}
56036406Ssklower 		IFPERF(tpcb)
56137469Ssklower 			if (*mp) {
56237469Ssklower 				struct mbuf * n;
56337469Ssklower 				do {
56437469Ssklower 					MFREE(*mp, n);
56537469Ssklower 					*mp = n;
56637469Ssklower 				} while (n);
56737469Ssklower 			}
56837469Ssklower 			*mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
56936406Ssklower 		ENDPERF
57036406Ssklower 		else {
57136406Ssklower 			error = EINVAL; goto done;
57236406Ssklower 		}
57336406Ssklower 		break;
57436406Ssklower #else
57536406Ssklower 		error = EOPNOTSUPP;
57636406Ssklower 		goto done;
57736406Ssklower #endif TP_PERF_MEAS
57836406Ssklower 
57936406Ssklower 	case TPOPT_CDDATA_CLEAR:
58036406Ssklower 		if (cmd == PRCO_GETOPT) {
58136406Ssklower 			error = EINVAL;
58236406Ssklower 		} else {
58337469Ssklower 			if (tpcb->tp_ucddata) {
58438841Ssklower 				m_freem(tpcb->tp_ucddata);
58538841Ssklower 				tpcb->tp_ucddata = 0;
58636406Ssklower 			}
58736406Ssklower 		}
58836406Ssklower 		break;
58936406Ssklower 
59038841Ssklower 	case TPOPT_CFRM_DATA:
59136406Ssklower 	case TPOPT_DISC_DATA:
59236406Ssklower 	case TPOPT_CONN_DATA:
59336406Ssklower 		if( tpcb->tp_class == TP_CLASS_0 ) {
59436406Ssklower 			error = EOPNOTSUPP;
59536406Ssklower 			break;
59636406Ssklower 		}
59736406Ssklower 		IFDEBUG(D_REQUEST)
59836406Ssklower 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
59936406Ssklower 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
60036406Ssklower 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
60136406Ssklower 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
60236406Ssklower 		ENDDEBUG
60336406Ssklower 		if (cmd == PRCO_SETOPT) {
60437469Ssklower 			int len = tpcb->tp_ucddata ?  tpcb->tp_ucddata->m_len : 0;
60536406Ssklower 			/* can append connect data in several calls */
60637469Ssklower 			if (len + val_len >
60736406Ssklower 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
60836406Ssklower 				error = EMSGSIZE; goto done;
60936406Ssklower 			}
61036406Ssklower 			(*mp)->m_next = MNULL;
61136406Ssklower 			(*mp)->m_act = 0;
61237469Ssklower 			if (tpcb->tp_ucddata)
61337469Ssklower 				m_cat(tpcb->tp_ucddata, *mp);
61437469Ssklower 			else
61537469Ssklower 				tpcb->tp_ucddata = *mp;
61640241Ssklower 			IFDEBUG(D_REQUEST)
61740241Ssklower 				dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
61840241Ssklower 			ENDDEBUG
61936406Ssklower 			IFTRACE(D_REQUEST)
62036406Ssklower 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
62136406Ssklower 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
62236406Ssklower 			ENDTRACE
62336406Ssklower 			*mp = MNULL; /* prevent sosetopt from freeing it! */
62440241Ssklower 			if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
62540241Ssklower 				(void) tp_confirm(tpcb);
62636406Ssklower 		}
62736406Ssklower 		break;
62836406Ssklower 
62936406Ssklower 	case TPOPT_PERF_MEAS:
63036406Ssklower #ifdef TP_PERF_MEAS
63136406Ssklower 		if (cmd == PRCO_GETOPT) {
63236406Ssklower 			*value = (u_int)tpcb->tp_perf_on;
63336406Ssklower 			(*mp)->m_len = sizeof(u_int);
63436406Ssklower 		} else if (cmd == PRCO_SETOPT) {
63536406Ssklower 			(*mp)->m_len = 0;
63636406Ssklower 			if ((*value) != 0 && (*value) != 1 )
63736406Ssklower 				error = EINVAL;
63836406Ssklower 			else  tpcb->tp_perf_on = (*value);
63936406Ssklower 		}
64036406Ssklower 		if( tpcb->tp_perf_on )
64136406Ssklower 			error = tp_setup_perf(tpcb);
64236406Ssklower #else  TP_PERF_MEAS
64336406Ssklower 		error = EOPNOTSUPP;
64436406Ssklower #endif TP_PERF_MEAS
64536406Ssklower 		break;
64636406Ssklower 
64736406Ssklower 	default:
64836406Ssklower 		error = EOPNOTSUPP;
64936406Ssklower 	}
65036406Ssklower 
65136406Ssklower done:
65236406Ssklower 	IFDEBUG(D_REQUEST)
65336406Ssklower 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
65436406Ssklower 		dump_mbuf(*mp, "tp_ctloutput *mp");
65536406Ssklower 	ENDDEBUG
65636406Ssklower 	/*
65736406Ssklower 	 * sigh: getsockopt looks only at m_len : all output data must
65836406Ssklower 	 * reside in the first mbuf
65936406Ssklower 	 */
66036406Ssklower 	if ( error  && (*mp) != MNULL )
66136406Ssklower 		(*mp)->m_len = 0;
66236406Ssklower 	if( (*mp) != MNULL ) {
66336406Ssklower 		ASSERT ( m_compress(*mp, mp) <= MLEN );
66436406Ssklower 		IFDEBUG(D_REQUEST)
66536406Ssklower 			dump_mbuf(*mp, "tp_ctloutput *mp after compress");
66636406Ssklower 		ENDDEBUG
66736406Ssklower 	}
66836406Ssklower 
66936406Ssklower 	splx(s);
67036406Ssklower 	return error;
67136406Ssklower }
672