xref: /csrg-svn/sys/netiso/tp_output.c (revision 68262)
149268Sbostic /*-
263222Sbostic  * Copyright (c) 1991, 1993
363222Sbostic  *	The Regents of the University of California.  All rights reserved.
449268Sbostic  *
549268Sbostic  * %sccs.include.redist.c%
649268Sbostic  *
7*68262Scgd  *	@(#)tp_output.c	8.2 (Berkeley) 02/09/95
849268Sbostic  */
949268Sbostic 
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 
4556533Sbostic #include <sys/param.h>
4656533Sbostic #include <sys/mbuf.h>
4756533Sbostic #include <sys/systm.h>
4856533Sbostic #include <sys/socket.h>
4956533Sbostic #include <sys/socketvar.h>
5056533Sbostic #include <sys/protosw.h>
5156533Sbostic #include <sys/errno.h>
5256533Sbostic #include <sys/time.h>
5356533Sbostic #include <sys/kernel.h>
5436406Ssklower 
5556533Sbostic #include <netiso/tp_param.h>
5656533Sbostic #include <netiso/tp_user.h>
5756533Sbostic #include <netiso/tp_stat.h>
5856533Sbostic #include <netiso/tp_ip.h>
5956533Sbostic #include <netiso/tp_clnp.h>
6056533Sbostic #include <netiso/tp_timer.h>
6156533Sbostic #include <netiso/argo_debug.h>
6256533Sbostic #include <netiso/tp_pcb.h>
6356533Sbostic #include <netiso/tp_trace.h>
6456533Sbostic 
6536406Ssklower #define TPDUSIZESHIFT 24
6636406Ssklower #define CLASSHIFT 16
6736406Ssklower 
6836406Ssklower /*
6936406Ssklower  * NAME: 	tp_consistency()
7036406Ssklower  *
7136406Ssklower  * CALLED FROM:
7236406Ssklower  * 	tp_ctloutput(), tp_input()
7336406Ssklower  *
7436406Ssklower  * FUNCTION and ARGUMENTS:
7536406Ssklower  * 	Checks the consistency of options and tpdusize with class,
7636406Ssklower  *	using the parameters passed in via (param).
7736406Ssklower  *	(cmd) may be TP_STRICT or TP_FORCE or both.
7836406Ssklower  *  Force means it will set all the values in (tpcb) to those in
7936406Ssklower  *  the input arguements iff no errors were encountered.
8036406Ssklower  *  Strict means that no inconsistency will be tolerated.  If it's
8136406Ssklower  *  not used, checksum and tpdusize inconsistencies will be tolerated.
8236406Ssklower  *  The reason for this is that in some cases, when we're negotiating down
8336406Ssklower  *	from class  4, these options should be changed but should not
8436406Ssklower  *  cause negotiation to fail.
8536406Ssklower  *
8636406Ssklower  * RETURNS
8736406Ssklower  *  E* or EOK
8836406Ssklower  *  E* if the various parms aren't ok for a given class
8936406Ssklower  *  EOK if they are ok for a given class
9036406Ssklower  */
9136406Ssklower 
9236406Ssklower int
tp_consistency(tpcb,cmd,param)9336406Ssklower tp_consistency( tpcb, cmd, param )
9436406Ssklower 	u_int cmd;
9536406Ssklower 	struct tp_conn_param *param;
9636406Ssklower 	struct tp_pcb *tpcb;
9736406Ssklower {
9836406Ssklower 	register int	error = EOK;
9936406Ssklower 	int 			class_to_use  = tp_mask_to_num(param->p_class);
10036406Ssklower 
10136406Ssklower 	IFTRACE(D_SETPARAMS)
10236406Ssklower 		tptrace(TPPTmisc,
10336406Ssklower 		"tp_consist enter class_to_use dontchange param.class cmd",
10436406Ssklower 		class_to_use, param->p_dont_change_params, param->p_class, cmd);
10536406Ssklower 	ENDTRACE
10636406Ssklower 	IFDEBUG(D_SETPARAMS)
10736406Ssklower 		printf("tp_consistency %s %s\n",
10836406Ssklower 			cmd& TP_FORCE?	"TP_FORCE":	"",
10936406Ssklower 			cmd& TP_STRICT?	"TP_STRICT":"");
11036406Ssklower 	ENDDEBUG
11136406Ssklower 	if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
11236406Ssklower 		cmd &= ~TP_FORCE;
11336406Ssklower 	}
11436406Ssklower 	/* can switch net services within a domain, but
11536406Ssklower 	 * cannot switch domains
11636406Ssklower 	 */
11736406Ssklower 	switch( param->p_netservice) {
11836406Ssklower 	case ISO_CONS:
11936406Ssklower 	case ISO_CLNS:
12036406Ssklower 	case ISO_COSNS:
12136406Ssklower 		/* param->p_netservice in ISO DOMAIN */
12236406Ssklower 		if(tpcb->tp_domain != AF_ISO ) {
12336406Ssklower 			error = EINVAL; goto done;
12436406Ssklower 		}
12536406Ssklower 		break;
12636406Ssklower 	case IN_CLNS:
12736406Ssklower 		/* param->p_netservice in INET DOMAIN */
12836406Ssklower 		if( tpcb->tp_domain != AF_INET ) {
12936406Ssklower 			error = EINVAL; goto done;
13036406Ssklower 		}
13136406Ssklower 		break;
13236406Ssklower 		/* no others not possible-> netservice is a 2-bit field! */
13336406Ssklower 	}
13436406Ssklower 
13536406Ssklower 	IFDEBUG(D_SETPARAMS)
13636406Ssklower 		printf("p_class 0x%x, class_to_use 0x%x\n",  param->p_class,
13736406Ssklower 			class_to_use);
13836406Ssklower 	ENDDEBUG
139*68262Scgd 	if(param->p_netservice > TP_MAX_NETSERVICES){
14036406Ssklower 		error = EINVAL; goto done;
14136406Ssklower 	}
14236406Ssklower 	if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
14336406Ssklower 		error = EINVAL; goto done;
14436406Ssklower 	}
14536406Ssklower 	IFDEBUG(D_SETPARAMS)
14636406Ssklower 		printf("Nretrans 0x%x\n",  param->p_Nretrans );
14736406Ssklower 	ENDDEBUG
14836406Ssklower 	if( ( param->p_Nretrans < 1 ) ||
14936406Ssklower 		  (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
15036406Ssklower 			/* bad for any class because negot has to be done a la class 4 */
15136406Ssklower 			error = EINVAL; goto done;
15236406Ssklower 	}
15336406Ssklower 	IFDEBUG(D_SETPARAMS)
15436406Ssklower 		printf("use_csum 0x%x\n",  param->p_use_checksum );
15536406Ssklower 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
15636406Ssklower 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
15736406Ssklower 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
15836406Ssklower 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
15936406Ssklower 	ENDDEBUG
16036406Ssklower 	switch( class_to_use ) {
16136406Ssklower 
16236406Ssklower 	case 0:
16336406Ssklower 		/* do not use checksums, xtd format, or XPD */
16436406Ssklower 
16536406Ssklower 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
16636406Ssklower 			if(cmd & TP_STRICT) {
16736406Ssklower 				error = EINVAL;
16836406Ssklower 			} else {
16936406Ssklower 				param->p_use_checksum = 0;
17036406Ssklower 				param->p_xtd_format = 0;
17136406Ssklower 				param->p_xpd_service = 0;
17236406Ssklower 			}
17336406Ssklower 			break;
17436406Ssklower 		}
17536406Ssklower 
17636406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
17736406Ssklower 			if(cmd & TP_STRICT) {
17836406Ssklower 				error = EINVAL;
17936406Ssklower 			} else {
18036406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
18136406Ssklower 			}
18236406Ssklower 			break;
18336406Ssklower 		}
18436406Ssklower 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
18536406Ssklower 			if (cmd & TP_STRICT) {
18636406Ssklower 				error = EINVAL;
18736406Ssklower 			} else {
18836406Ssklower 				param->p_tpdusize = TP0_TPDUSIZE;
18936406Ssklower 			}
19036406Ssklower 			break;
19136406Ssklower 		}
19236406Ssklower 
19336406Ssklower 		/* connect/disc data not allowed for class 0 */
19437469Ssklower 		if (tpcb->tp_ucddata) {
19536406Ssklower 			if(cmd & TP_STRICT) {
19636406Ssklower 				error = EINVAL;
19736406Ssklower 			} else if(cmd & TP_FORCE) {
19837469Ssklower 				m_freem(tpcb->tp_ucddata);
19937469Ssklower 				tpcb->tp_ucddata = 0;
20036406Ssklower 			}
20136406Ssklower 		}
20236406Ssklower 		break;
20336406Ssklower 
20436406Ssklower 	case 4:
20536406Ssklower 		IFDEBUG(D_SETPARAMS)
20636406Ssklower 			printf("dt_ticks 0x%x\n",  param->p_dt_ticks );
20736406Ssklower 			printf("x_ticks 0x%x\n",  param->p_x_ticks );
20836406Ssklower 			printf("dr_ticks 0x%x\n",  param->p_dr_ticks );
20936406Ssklower 			printf("keepalive 0x%x\n",  param->p_keepalive_ticks );
21036406Ssklower 			printf("sendack 0x%x\n",  param->p_sendack_ticks );
21136406Ssklower 			printf("inact 0x%x\n",  param->p_inact_ticks );
21236406Ssklower 			printf("ref 0x%x\n",  param->p_ref_ticks );
21336406Ssklower 		ENDDEBUG
21436406Ssklower 		if( (param->p_class & TP_CLASS_4 ) && (
21536406Ssklower 			  (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
21636406Ssklower 			  (param->p_x_ticks < 1)	|| (param->p_keepalive_ticks < 1) ||
21736406Ssklower 			  (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
21836406Ssklower 			  (param->p_inact_ticks < 1) ) ) {
21936406Ssklower 				error = EINVAL;
22036406Ssklower 				break;
22136406Ssklower 		}
22236406Ssklower 		IFDEBUG(D_SETPARAMS)
22336406Ssklower 			printf("rx_strat 0x%x\n",  param->p_rx_strat );
22436406Ssklower 		ENDDEBUG
22536406Ssklower 		if(param->p_rx_strat >
22636406Ssklower 			( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
22736406Ssklower 				if(cmd & TP_STRICT) {
22836406Ssklower 					error = EINVAL;
22936406Ssklower 				} else {
23036406Ssklower 					param->p_rx_strat = TPRX_USE_CW;
23136406Ssklower 				}
23236406Ssklower 				break;
23336406Ssklower 		}
23436406Ssklower 		IFDEBUG(D_SETPARAMS)
23536406Ssklower 			printf("ack_strat 0x%x\n",  param->p_ack_strat );
23636406Ssklower 		ENDDEBUG
23736406Ssklower 		if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
23836406Ssklower 			if(cmd & TP_STRICT) {
23936406Ssklower 				error = EINVAL;
24036406Ssklower 			} else {
24136406Ssklower 				param->p_ack_strat = TPACK_WINDOW;
24236406Ssklower 			}
24336406Ssklower 			break;
24436406Ssklower 		}
24536406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
24636406Ssklower 			if(cmd & TP_STRICT) {
24736406Ssklower 				error = EINVAL;
24836406Ssklower 			} else {
24936406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
25036406Ssklower 			}
25136406Ssklower 			break;
25236406Ssklower 		}
25336406Ssklower 		if (param->p_tpdusize > TP_TPDUSIZE)  {
25436406Ssklower 			if(cmd & TP_STRICT) {
25536406Ssklower 				error = EINVAL;
25636406Ssklower 			} else {
25736406Ssklower 				param->p_tpdusize = TP_TPDUSIZE;
25836406Ssklower 			}
25936406Ssklower 			break;
26036406Ssklower 		}
26136406Ssklower 		break;
26236406Ssklower 	}
26336406Ssklower 
26436406Ssklower 	if ((error==0) && (cmd & TP_FORCE)) {
26551996Ssklower 		long dusize = ((long)param->p_ptpdusize) << 7;
26647282Ssklower 		/* Enforce Negotation rules below */
26736406Ssklower 		tpcb->tp_class = param->p_class;
26847282Ssklower 		if (tpcb->tp_use_checksum || param->p_use_checksum)
26947282Ssklower 			tpcb->tp_use_checksum = 1;
27047282Ssklower 		if (!tpcb->tp_xpd_service || !param->p_xpd_service)
27147282Ssklower 			tpcb->tp_xpd_service = 0;
27247282Ssklower 		if (!tpcb->tp_xtd_format || !param->p_xtd_format)
27347282Ssklower 			tpcb->tp_xtd_format = 0;
27451996Ssklower 		if (dusize) {
27551996Ssklower 			if (tpcb->tp_l_tpdusize > dusize)
27651996Ssklower 				tpcb->tp_l_tpdusize = dusize;
27751996Ssklower 			if (tpcb->tp_ptpdusize == 0 ||
27851996Ssklower 				tpcb->tp_ptpdusize > param->p_ptpdusize)
27951996Ssklower 				tpcb->tp_ptpdusize = param->p_ptpdusize;
28051996Ssklower 		} else {
28151996Ssklower 			if (param->p_tpdusize != 0 &&
28251996Ssklower 				tpcb->tp_tpdusize > param->p_tpdusize)
28351996Ssklower 				tpcb->tp_tpdusize = param->p_tpdusize;
28451996Ssklower 			tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
28551996Ssklower 		}
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
tp_ctloutput(cmd,so,level,optname,mp)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
38350974Ssklower 			return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
38450974Ssklower 				tpcb->tp_npcb, *mp));
38536406Ssklower 		goto done;
38650974Ssklower 	} else if ( level == SOL_SOCKET) {
38750974Ssklower 		if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
38853688Ssklower 			u_long old_credit = tpcb->tp_maxlcredit;
38950974Ssklower 			tp_rsyset(tpcb);
39053688Ssklower 			if (tpcb->tp_rhiwat != so->so_rcv.sb_hiwat &&
39153688Ssklower 			    tpcb->tp_state == TP_OPEN &&
39253688Ssklower 			    (old_credit < tpcb->tp_maxlcredit))
39353688Ssklower 				tp_emit(AK_TPDU_type, tpcb,
39453688Ssklower 					tpcb->tp_rcvnxt, 0, MNULL);
39553688Ssklower 			tpcb->tp_rhiwat = so->so_rcv.sb_hiwat;
39650974Ssklower 		}
39750974Ssklower 		goto done;
39836406Ssklower 	} else if ( level !=  SOL_TRANSPORT ) {
39936406Ssklower 		error = EOPNOTSUPP; goto done;
40036406Ssklower 	}
40136406Ssklower 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
40236406Ssklower 		error = EOPNOTSUPP; goto done;
40336406Ssklower 	}
40436406Ssklower 	if ( so->so_error ) {
40536406Ssklower 		error = so->so_error; goto done;
40636406Ssklower 	}
40736406Ssklower 
40836406Ssklower 	/* The only options allowed after connection is established
40936406Ssklower 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
41036406Ssklower 	 */
41136406Ssklower 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
41236406Ssklower 		&&
41336406Ssklower 		(cmd == PRCO_SETOPT  &&
41436406Ssklower 			optname != TPOPT_DISC_DATA &&
41538841Ssklower 			optname != TPOPT_CFRM_DATA &&
41636406Ssklower 			optname != TPOPT_PERF_MEAS &&
41736406Ssklower 			optname != TPOPT_CDDATA_CLEAR ) ) {
41836406Ssklower 		error = EISCONN; goto done;
41936406Ssklower 	}
42036406Ssklower 	/* The only options allowed after disconnection are GET DISC DATA,
42136406Ssklower 	 * and TPOPT_PSTATISTICS
42236406Ssklower 	 * and they're not allowed if the ref timer has gone off, because
42336406Ssklower 	 * the tpcb is gone
42436406Ssklower 	 */
42538841Ssklower 	if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) ==  0) {
42650435Ssklower 		if ( so->so_pcb == (caddr_t)0 ) {
42736406Ssklower 			error = ENOTCONN; goto done;
42836406Ssklower 		}
42936406Ssklower 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
43036406Ssklower 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
43136406Ssklower 			error = ENOTCONN; goto done;
43236406Ssklower 		}
43336406Ssklower 	}
43436406Ssklower 
43537469Ssklower 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
43637469Ssklower 								  * but lint complains about it
43737469Ssklower 								  */
43836406Ssklower 	val_len = (*mp)->m_len;
43936406Ssklower 
44036406Ssklower 	switch (optname) {
44136406Ssklower 
44244424Ssklower 	case TPOPT_INTERCEPT:
44350648Ssklower #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
44450648Ssklower #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
44550648Ssklower 
44650236Ssklower 		if ((so->so_state & SS_PRIV) == 0) {
44750236Ssklower 			error = EPERM;
44850648Ssklower 		} else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
44950648Ssklower 					(tpcb->tp_flags & TPF_GENERAL_ADDR) ||
45050648Ssklower 					tpcb->tp_next == 0)
45144424Ssklower 			error = EINVAL;
45244424Ssklower 		else {
45350648Ssklower 			register struct tp_pcb *t;
45450648Ssklower 			error = EADDRINUSE;
45550648Ssklower 			for (t = tp_listeners; t; t = t->tp_nextlisten)
45650648Ssklower 				if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
45750648Ssklower 						t->tp_domain == tpcb->tp_domain)
45850648Ssklower 					switch (tpcb->tp_domain) {
45950648Ssklower 					default:
46050648Ssklower 						goto done;
46150648Ssklower #ifdef	INET
46250648Ssklower 					case AF_INET:
46350648Ssklower 						if (INA(t) == INA(tpcb))
46450648Ssklower 							goto done;
46550648Ssklower 						continue;
46650648Ssklower #endif
46750648Ssklower #ifdef ISO
46850648Ssklower 					case AF_ISO:
46950648Ssklower 						if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
47050648Ssklower 										ISOA(t).isoa_len) == 0)
47150648Ssklower 							goto done;
47250648Ssklower 						continue;
47350648Ssklower #endif
47450648Ssklower 					}
47550648Ssklower 			tpcb->tp_lsuffixlen = 0;
47650648Ssklower 			tpcb->tp_state = TP_LISTENING;
47750648Ssklower 			error = 0;
47850648Ssklower 			remque(tpcb);
47950648Ssklower 			tpcb->tp_next = tpcb->tp_prev = tpcb;
48050648Ssklower 			tpcb->tp_nextlisten = tp_listeners;
48150648Ssklower 			tp_listeners = tpcb;
48244424Ssklower 		}
48344424Ssklower 		break;
48444424Ssklower 
48536406Ssklower 	case TPOPT_MY_TSEL:
48636406Ssklower 		if ( cmd == PRCO_GETOPT ) {
48736406Ssklower 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
48837469Ssklower 			bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
48936406Ssklower 			(*mp)->m_len = tpcb->tp_lsuffixlen;
49036406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
49136406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
49236406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
49336406Ssklower 				error = EINVAL;
49436406Ssklower 			} else {
49537469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
49636406Ssklower 				tpcb->tp_lsuffixlen = val_len;
49736406Ssklower 			}
49836406Ssklower 		}
49936406Ssklower 		break;
50036406Ssklower 
50136406Ssklower 	case TPOPT_PEER_TSEL:
50236406Ssklower 		if ( cmd == PRCO_GETOPT ) {
50336406Ssklower 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
50437469Ssklower 			bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
50536406Ssklower 			(*mp)->m_len = tpcb->tp_fsuffixlen;
50636406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
50736406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
50836406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
50936406Ssklower 				error = EINVAL;
51036406Ssklower 			} else {
51137469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
51236406Ssklower 				tpcb->tp_fsuffixlen = val_len;
51336406Ssklower 			}
51436406Ssklower 		}
51536406Ssklower 		break;
51636406Ssklower 
51736406Ssklower 	case TPOPT_FLAGS:
51836406Ssklower 		IFDEBUG(D_REQUEST)
51936406Ssklower 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
52036406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET",
52136406Ssklower 				value,
52236406Ssklower 				*value,
52336406Ssklower 				tpcb->tp_flags);
52436406Ssklower 		ENDDEBUG
52536406Ssklower 
52636406Ssklower 		if ( cmd == PRCO_GETOPT ) {
52736406Ssklower 			*(int *)value = (int)tpcb->tp_flags;
52836406Ssklower 			(*mp)->m_len = sizeof(u_int);
52936406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
53036406Ssklower 			error = EINVAL; goto done;
53136406Ssklower 		}
53236406Ssklower 		break;
53336406Ssklower 
53436406Ssklower 	case TPOPT_PARAMS:
53536406Ssklower 		/* This handles:
53636406Ssklower 		 * timer values,
53736406Ssklower 		 * class, use of transport expedited data,
53836406Ssklower 		 * max tpdu size, checksum, xtd format and
53936406Ssklower 		 * disconnect indications, and may get rid of connect/disc data
54036406Ssklower 		 */
54136406Ssklower 		IFDEBUG(D_SETPARAMS)
54236406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
54336406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
54436406Ssklower 		ENDDEBUG
54536406Ssklower 		IFDEBUG(D_REQUEST)
54636406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
54736406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
54836406Ssklower 		ENDDEBUG
54936406Ssklower 
55036406Ssklower 		if ( cmd == PRCO_GETOPT ) {
55136406Ssklower 			*(struct tp_conn_param *)value = tpcb->_tp_param;
55236406Ssklower 			(*mp)->m_len = sizeof(tpcb->_tp_param);
55336406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
55436406Ssklower 			if( (error =
55536406Ssklower 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
55636406Ssklower 								(struct tp_conn_param *)value))==0) {
55736406Ssklower 				/*
55836406Ssklower 				 * tp_consistency doesn't copy the whole set of params
55936406Ssklower 				 */
56036406Ssklower 				tpcb->_tp_param = *(struct tp_conn_param *)value;
56136406Ssklower 				(*mp)->m_len = sizeof(tpcb->_tp_param);
56236406Ssklower 			}
56336406Ssklower 		}
56436406Ssklower 		break;
56536406Ssklower 
56636406Ssklower 	case TPOPT_PSTATISTICS:
56736406Ssklower #ifdef TP_PERF_MEAS
56836406Ssklower 		if (cmd == PRCO_SETOPT) {
56936406Ssklower 			error = EINVAL; goto done;
57036406Ssklower 		}
57136406Ssklower 		IFPERF(tpcb)
57237469Ssklower 			if (*mp) {
57337469Ssklower 				struct mbuf * n;
57437469Ssklower 				do {
57537469Ssklower 					MFREE(*mp, n);
57637469Ssklower 					*mp = n;
57737469Ssklower 				} while (n);
57837469Ssklower 			}
57937469Ssklower 			*mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
58036406Ssklower 		ENDPERF
58136406Ssklower 		else {
58236406Ssklower 			error = EINVAL; goto done;
58336406Ssklower 		}
58436406Ssklower 		break;
58536406Ssklower #else
58636406Ssklower 		error = EOPNOTSUPP;
58736406Ssklower 		goto done;
58860359Sbostic #endif /* TP_PERF_MEAS */
58936406Ssklower 
59036406Ssklower 	case TPOPT_CDDATA_CLEAR:
59136406Ssklower 		if (cmd == PRCO_GETOPT) {
59236406Ssklower 			error = EINVAL;
59336406Ssklower 		} else {
59437469Ssklower 			if (tpcb->tp_ucddata) {
59538841Ssklower 				m_freem(tpcb->tp_ucddata);
59638841Ssklower 				tpcb->tp_ucddata = 0;
59736406Ssklower 			}
59836406Ssklower 		}
59936406Ssklower 		break;
60036406Ssklower 
60138841Ssklower 	case TPOPT_CFRM_DATA:
60236406Ssklower 	case TPOPT_DISC_DATA:
60336406Ssklower 	case TPOPT_CONN_DATA:
60436406Ssklower 		if( tpcb->tp_class == TP_CLASS_0 ) {
60536406Ssklower 			error = EOPNOTSUPP;
60636406Ssklower 			break;
60736406Ssklower 		}
60836406Ssklower 		IFDEBUG(D_REQUEST)
60936406Ssklower 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
61036406Ssklower 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
61136406Ssklower 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
61236406Ssklower 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
61336406Ssklower 		ENDDEBUG
61436406Ssklower 		if (cmd == PRCO_SETOPT) {
61537469Ssklower 			int len = tpcb->tp_ucddata ?  tpcb->tp_ucddata->m_len : 0;
61636406Ssklower 			/* can append connect data in several calls */
61737469Ssklower 			if (len + val_len >
61836406Ssklower 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
61936406Ssklower 				error = EMSGSIZE; goto done;
62036406Ssklower 			}
62136406Ssklower 			(*mp)->m_next = MNULL;
62236406Ssklower 			(*mp)->m_act = 0;
62337469Ssklower 			if (tpcb->tp_ucddata)
62437469Ssklower 				m_cat(tpcb->tp_ucddata, *mp);
62537469Ssklower 			else
62637469Ssklower 				tpcb->tp_ucddata = *mp;
62740241Ssklower 			IFDEBUG(D_REQUEST)
62840241Ssklower 				dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
62940241Ssklower 			ENDDEBUG
63036406Ssklower 			IFTRACE(D_REQUEST)
63136406Ssklower 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
63236406Ssklower 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
63336406Ssklower 			ENDTRACE
63450974Ssklower 			*mp = MNULL;
63540241Ssklower 			if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
63640241Ssklower 				(void) tp_confirm(tpcb);
63736406Ssklower 		}
63836406Ssklower 		break;
63936406Ssklower 
64036406Ssklower 	case TPOPT_PERF_MEAS:
64136406Ssklower #ifdef TP_PERF_MEAS
64236406Ssklower 		if (cmd == PRCO_GETOPT) {
64336406Ssklower 			*value = (u_int)tpcb->tp_perf_on;
64436406Ssklower 			(*mp)->m_len = sizeof(u_int);
64536406Ssklower 		} else if (cmd == PRCO_SETOPT) {
64636406Ssklower 			(*mp)->m_len = 0;
64736406Ssklower 			if ((*value) != 0 && (*value) != 1 )
64836406Ssklower 				error = EINVAL;
64936406Ssklower 			else  tpcb->tp_perf_on = (*value);
65036406Ssklower 		}
65136406Ssklower 		if( tpcb->tp_perf_on )
65236406Ssklower 			error = tp_setup_perf(tpcb);
65360359Sbostic #else  /* TP_PERF_MEAS */
65436406Ssklower 		error = EOPNOTSUPP;
65560359Sbostic #endif /* TP_PERF_MEAS */
65636406Ssklower 		break;
65736406Ssklower 
65836406Ssklower 	default:
65936406Ssklower 		error = EOPNOTSUPP;
66036406Ssklower 	}
66136406Ssklower 
66236406Ssklower done:
66336406Ssklower 	IFDEBUG(D_REQUEST)
66436406Ssklower 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
66536406Ssklower 		dump_mbuf(*mp, "tp_ctloutput *mp");
66636406Ssklower 	ENDDEBUG
66736406Ssklower 	/*
66836406Ssklower 	 * sigh: getsockopt looks only at m_len : all output data must
66936406Ssklower 	 * reside in the first mbuf
67036406Ssklower 	 */
67150974Ssklower 	if (*mp) {
67253688Ssklower 		if (cmd == PRCO_SETOPT) {
67350974Ssklower 			m_freem(*mp);
67453688Ssklower 			*mp = MNULL;
67553688Ssklower 		} else {
67650974Ssklower 			ASSERT ( m_compress(*mp, mp) <= MLEN );
67750974Ssklower 			if (error)
67850974Ssklower 				(*mp)->m_len = 0;
67950974Ssklower 			IFDEBUG(D_REQUEST)
68050974Ssklower 				dump_mbuf(*mp, "tp_ctloutput *mp after compress");
68150974Ssklower 			ENDDEBUG
68250974Ssklower 		}
68336406Ssklower 	}
68436406Ssklower 	splx(s);
68536406Ssklower 	return error;
68636406Ssklower }
687