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