xref: /csrg-svn/sys/netiso/tp_output.c (revision 50974)
149268Sbostic /*-
249268Sbostic  * Copyright (c) 1991 The Regents of the University of California.
349268Sbostic  * All rights reserved.
449268Sbostic  *
549268Sbostic  * %sccs.include.redist.c%
649268Sbostic  *
7*50974Ssklower  *	@(#)tp_output.c	7.13 (Berkeley) 09/03/91
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 
4536406Ssklower #include "param.h"
4644424Ssklower #include "mbuf.h"
4736406Ssklower #include "systm.h"
4836406Ssklower #include "socket.h"
4936406Ssklower #include "socketvar.h"
5044424Ssklower #include "protosw.h"
5136406Ssklower #include "errno.h"
5236406Ssklower #include "time.h"
5337469Ssklower #include "tp_param.h"
5437469Ssklower #include "tp_user.h"
5537469Ssklower #include "tp_stat.h"
5637469Ssklower #include "tp_ip.h"
5744424Ssklower #include "tp_clnp.h"
5837469Ssklower #include "tp_timer.h"
5937469Ssklower #include "argo_debug.h"
6037469Ssklower #include "tp_pcb.h"
6137469Ssklower #include "tp_trace.h"
6250236Ssklower #include "kernel.h"
6336406Ssklower 
6436406Ssklower #define TPDUSIZESHIFT 24
6536406Ssklower #define CLASSHIFT 16
6636406Ssklower 
6736406Ssklower /*
6836406Ssklower  * NAME: 	tp_consistency()
6936406Ssklower  *
7036406Ssklower  * CALLED FROM:
7136406Ssklower  * 	tp_ctloutput(), tp_input()
7236406Ssklower  *
7336406Ssklower  * FUNCTION and ARGUMENTS:
7436406Ssklower  * 	Checks the consistency of options and tpdusize with class,
7536406Ssklower  *	using the parameters passed in via (param).
7636406Ssklower  *	(cmd) may be TP_STRICT or TP_FORCE or both.
7736406Ssklower  *  Force means it will set all the values in (tpcb) to those in
7836406Ssklower  *  the input arguements iff no errors were encountered.
7936406Ssklower  *  Strict means that no inconsistency will be tolerated.  If it's
8036406Ssklower  *  not used, checksum and tpdusize inconsistencies will be tolerated.
8136406Ssklower  *  The reason for this is that in some cases, when we're negotiating down
8236406Ssklower  *	from class  4, these options should be changed but should not
8336406Ssklower  *  cause negotiation to fail.
8436406Ssklower  *
8536406Ssklower  * RETURNS
8636406Ssklower  *  E* or EOK
8736406Ssklower  *  E* if the various parms aren't ok for a given class
8836406Ssklower  *  EOK if they are ok for a given class
8936406Ssklower  */
9036406Ssklower 
9136406Ssklower int
9236406Ssklower tp_consistency( tpcb, cmd, param )
9336406Ssklower 	u_int cmd;
9436406Ssklower 	struct tp_conn_param *param;
9536406Ssklower 	struct tp_pcb *tpcb;
9636406Ssklower {
9736406Ssklower 	register int	error = EOK;
9836406Ssklower 	int 			class_to_use  = tp_mask_to_num(param->p_class);
9936406Ssklower 
10036406Ssklower 	IFTRACE(D_SETPARAMS)
10136406Ssklower 		tptrace(TPPTmisc,
10236406Ssklower 		"tp_consist enter class_to_use dontchange param.class cmd",
10336406Ssklower 		class_to_use, param->p_dont_change_params, param->p_class, cmd);
10436406Ssklower 	ENDTRACE
10536406Ssklower 	IFDEBUG(D_SETPARAMS)
10636406Ssklower 		printf("tp_consistency %s %s\n",
10736406Ssklower 			cmd& TP_FORCE?	"TP_FORCE":	"",
10836406Ssklower 			cmd& TP_STRICT?	"TP_STRICT":"");
10936406Ssklower 	ENDDEBUG
11036406Ssklower 	if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
11136406Ssklower 		cmd &= ~TP_FORCE;
11236406Ssklower 	}
11336406Ssklower 	/* can switch net services within a domain, but
11436406Ssklower 	 * cannot switch domains
11536406Ssklower 	 */
11636406Ssklower 	switch( param->p_netservice) {
11736406Ssklower 	case ISO_CONS:
11836406Ssklower 	case ISO_CLNS:
11936406Ssklower 	case ISO_COSNS:
12036406Ssklower 		/* param->p_netservice in ISO DOMAIN */
12136406Ssklower 		if(tpcb->tp_domain != AF_ISO ) {
12236406Ssklower 			error = EINVAL; goto done;
12336406Ssklower 		}
12436406Ssklower 		break;
12536406Ssklower 	case IN_CLNS:
12636406Ssklower 		/* param->p_netservice in INET DOMAIN */
12736406Ssklower 		if( tpcb->tp_domain != AF_INET ) {
12836406Ssklower 			error = EINVAL; goto done;
12936406Ssklower 		}
13036406Ssklower 		break;
13136406Ssklower 		/* no others not possible-> netservice is a 2-bit field! */
13236406Ssklower 	}
13336406Ssklower 
13436406Ssklower 	IFDEBUG(D_SETPARAMS)
13536406Ssklower 		printf("p_class 0x%x, class_to_use 0x%x\n",  param->p_class,
13636406Ssklower 			class_to_use);
13736406Ssklower 	ENDDEBUG
13836406Ssklower 	if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){
13936406Ssklower 		error = EINVAL; goto done;
14036406Ssklower 	}
14136406Ssklower 	if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
14236406Ssklower 		error = EINVAL; goto done;
14336406Ssklower 	}
14436406Ssklower 	IFDEBUG(D_SETPARAMS)
14536406Ssklower 		printf("Nretrans 0x%x\n",  param->p_Nretrans );
14636406Ssklower 	ENDDEBUG
14736406Ssklower 	if( ( param->p_Nretrans < 1 ) ||
14836406Ssklower 		  (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
14936406Ssklower 			/* bad for any class because negot has to be done a la class 4 */
15036406Ssklower 			error = EINVAL; goto done;
15136406Ssklower 	}
15236406Ssklower 	IFDEBUG(D_SETPARAMS)
15336406Ssklower 		printf("winsize 0x%x\n",  param->p_winsize );
15436406Ssklower 	ENDDEBUG
15536406Ssklower 	if( (param->p_winsize < 128 ) ||
15636406Ssklower 		(param->p_winsize < param->p_tpdusize ) ||
15736406Ssklower 		(param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
15836406Ssklower 			error = EINVAL; goto done;
15936406Ssklower 	} else {
16036406Ssklower 		if( tpcb->tp_state == TP_CLOSED )
16137469Ssklower 			soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
16237469Ssklower 						(u_long)param->p_winsize);
16336406Ssklower 	}
16436406Ssklower 	IFDEBUG(D_SETPARAMS)
16536406Ssklower 		printf("use_csum 0x%x\n",  param->p_use_checksum );
16636406Ssklower 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
16736406Ssklower 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
16836406Ssklower 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
16936406Ssklower 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
17036406Ssklower 	ENDDEBUG
17136406Ssklower 	switch( class_to_use ) {
17236406Ssklower 
17336406Ssklower 	case 0:
17436406Ssklower 		/* do not use checksums, xtd format, or XPD */
17536406Ssklower 
17636406Ssklower 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
17736406Ssklower 			if(cmd & TP_STRICT) {
17836406Ssklower 				error = EINVAL;
17936406Ssklower 			} else {
18036406Ssklower 				param->p_use_checksum = 0;
18136406Ssklower 				param->p_xtd_format = 0;
18236406Ssklower 				param->p_xpd_service = 0;
18336406Ssklower 			}
18436406Ssklower 			break;
18536406Ssklower 		}
18636406Ssklower 
18736406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
18836406Ssklower 			if(cmd & TP_STRICT) {
18936406Ssklower 				error = EINVAL;
19036406Ssklower 			} else {
19136406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
19236406Ssklower 			}
19336406Ssklower 			break;
19436406Ssklower 		}
19536406Ssklower 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
19636406Ssklower 			if (cmd & TP_STRICT) {
19736406Ssklower 				error = EINVAL;
19836406Ssklower 			} else {
19936406Ssklower 				param->p_tpdusize = TP0_TPDUSIZE;
20036406Ssklower 			}
20136406Ssklower 			break;
20236406Ssklower 		}
20336406Ssklower 
20436406Ssklower 		/* connect/disc data not allowed for class 0 */
20537469Ssklower 		if (tpcb->tp_ucddata) {
20636406Ssklower 			if(cmd & TP_STRICT) {
20736406Ssklower 				error = EINVAL;
20836406Ssklower 			} else if(cmd & TP_FORCE) {
20937469Ssklower 				m_freem(tpcb->tp_ucddata);
21037469Ssklower 				tpcb->tp_ucddata = 0;
21136406Ssklower 			}
21236406Ssklower 		}
21336406Ssklower 		break;
21436406Ssklower 
21536406Ssklower 	case 4:
21636406Ssklower 		IFDEBUG(D_SETPARAMS)
21736406Ssklower 			printf("dt_ticks 0x%x\n",  param->p_dt_ticks );
21836406Ssklower 			printf("x_ticks 0x%x\n",  param->p_x_ticks );
21936406Ssklower 			printf("dr_ticks 0x%x\n",  param->p_dr_ticks );
22036406Ssklower 			printf("keepalive 0x%x\n",  param->p_keepalive_ticks );
22136406Ssklower 			printf("sendack 0x%x\n",  param->p_sendack_ticks );
22236406Ssklower 			printf("inact 0x%x\n",  param->p_inact_ticks );
22336406Ssklower 			printf("ref 0x%x\n",  param->p_ref_ticks );
22436406Ssklower 		ENDDEBUG
22536406Ssklower 		if( (param->p_class & TP_CLASS_4 ) && (
22636406Ssklower 			  (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
22736406Ssklower 			  (param->p_x_ticks < 1)	|| (param->p_keepalive_ticks < 1) ||
22836406Ssklower 			  (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
22936406Ssklower 			  (param->p_inact_ticks < 1) ) ) {
23036406Ssklower 				error = EINVAL;
23136406Ssklower 				break;
23236406Ssklower 		}
23336406Ssklower 		IFDEBUG(D_SETPARAMS)
23436406Ssklower 			printf("rx_strat 0x%x\n",  param->p_rx_strat );
23536406Ssklower 		ENDDEBUG
23636406Ssklower 		if(param->p_rx_strat >
23736406Ssklower 			( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
23836406Ssklower 				if(cmd & TP_STRICT) {
23936406Ssklower 					error = EINVAL;
24036406Ssklower 				} else {
24136406Ssklower 					param->p_rx_strat = TPRX_USE_CW;
24236406Ssklower 				}
24336406Ssklower 				break;
24436406Ssklower 		}
24536406Ssklower 		IFDEBUG(D_SETPARAMS)
24636406Ssklower 			printf("ack_strat 0x%x\n",  param->p_ack_strat );
24736406Ssklower 		ENDDEBUG
24836406Ssklower 		if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
24936406Ssklower 			if(cmd & TP_STRICT) {
25036406Ssklower 				error = EINVAL;
25136406Ssklower 			} else {
25236406Ssklower 				param->p_ack_strat = TPACK_WINDOW;
25336406Ssklower 			}
25436406Ssklower 			break;
25536406Ssklower 		}
25636406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
25736406Ssklower 			if(cmd & TP_STRICT) {
25836406Ssklower 				error = EINVAL;
25936406Ssklower 			} else {
26036406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
26136406Ssklower 			}
26236406Ssklower 			break;
26336406Ssklower 		}
26436406Ssklower 		if (param->p_tpdusize > TP_TPDUSIZE)  {
26536406Ssklower 			if(cmd & TP_STRICT) {
26636406Ssklower 				error = EINVAL;
26736406Ssklower 			} else {
26836406Ssklower 				param->p_tpdusize = TP_TPDUSIZE;
26936406Ssklower 			}
27036406Ssklower 			break;
27136406Ssklower 		}
27236406Ssklower 		break;
27336406Ssklower 	}
27436406Ssklower 
27536406Ssklower 	if ((error==0) && (cmd & TP_FORCE)) {
27647282Ssklower 		/* Enforce Negotation rules below */
27747282Ssklower 		if (tpcb->tp_tpdusize > param->p_tpdusize)
27847282Ssklower 			tpcb->tp_tpdusize = param->p_tpdusize;
27936406Ssklower 		tpcb->tp_class = param->p_class;
28047282Ssklower 		if (tpcb->tp_use_checksum || param->p_use_checksum)
28147282Ssklower 			tpcb->tp_use_checksum = 1;
28247282Ssklower 		if (!tpcb->tp_xpd_service || !param->p_xpd_service)
28347282Ssklower 			tpcb->tp_xpd_service = 0;
28447282Ssklower 		if (!tpcb->tp_xtd_format || !param->p_xtd_format)
28547282Ssklower 			tpcb->tp_xtd_format = 0;
28636406Ssklower 	}
28736406Ssklower 
28836406Ssklower done:
28936406Ssklower 
29036406Ssklower 	IFTRACE(D_CONN)
29136406Ssklower 		tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
29236406Ssklower 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
29336406Ssklower 	ENDTRACE
29436406Ssklower 	IFDEBUG(D_CONN)
29536406Ssklower 		printf(
29636406Ssklower 		"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
29736406Ssklower 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
29836406Ssklower 	ENDDEBUG
29936406Ssklower 	return error;
30036406Ssklower }
30136406Ssklower 
30236406Ssklower /*
30336406Ssklower  * NAME: 	tp_ctloutput()
30436406Ssklower  *
30536406Ssklower  * CALLED FROM:
30636406Ssklower  * 	[sg]etsockopt(), via so[sg]etopt().
30736406Ssklower  *
30836406Ssklower  * FUNCTION and ARGUMENTS:
30936406Ssklower  * 	Implements the socket options at transport level.
31037536Smckusick  * 	(cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
31136406Ssklower  * 	(so) is the socket.
31237536Smckusick  * 	(level) is SOL_TRANSPORT (see ../sys/socket.h)
31336406Ssklower  * 	(optname) is the particular command or option to be set.
31436406Ssklower  * 	(**mp) is an mbuf structure.
31536406Ssklower  *
31636406Ssklower  * RETURN VALUE:
31736406Ssklower  * 	ENOTSOCK if the socket hasn't got an associated tpcb
31836406Ssklower  *  EINVAL if
31936406Ssklower  * 		trying to set window too big
32036406Ssklower  * 		trying to set illegal max tpdu size
32136406Ssklower  * 		trying to set illegal credit fraction
32236406Ssklower  * 		trying to use unknown or unimplemented class of TP
32336406Ssklower  *		structure passed to set timer values is wrong size
32436406Ssklower  *  	illegal combination of command/GET-SET option,
32536406Ssklower  *			e.g., GET w/ TPOPT_CDDATA_CLEAR:
32636406Ssklower  *  EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
32736406Ssklower  *   or if the transport-specific command is not implemented
32836406Ssklower  *  EISCONN if trying a command that isn't allowed after a connection
32936406Ssklower  *   is established
33036406Ssklower  *  ENOTCONN if trying a command that is allowed only if a connection is
33136406Ssklower  *   established
33236406Ssklower  *  EMSGSIZE if trying to give too much data on connect/disconnect
33336406Ssklower  *
33436406Ssklower  * SIDE EFFECTS:
33536406Ssklower  *
33636406Ssklower  * NOTES:
33736406Ssklower  */
33836406Ssklower ProtoHook
33936406Ssklower tp_ctloutput(cmd, so, level, optname, mp)
34036406Ssklower 	int 			cmd, level, optname;
34136406Ssklower 	struct socket	*so;
34236406Ssklower 	struct mbuf 	**mp;
34336406Ssklower {
34436406Ssklower 	struct		tp_pcb	*tpcb = sototpcb(so);
34536406Ssklower 	int 		s = splnet();
34637469Ssklower 	caddr_t		value;
34737469Ssklower 	unsigned	val_len;
34836406Ssklower 	int			error = 0;
34936406Ssklower 
35036406Ssklower 	IFTRACE(D_REQUEST)
35136406Ssklower 		tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
35236406Ssklower 			cmd, so, optname, mp);
35336406Ssklower 	ENDTRACE
35436406Ssklower 	IFDEBUG(D_REQUEST)
35536406Ssklower 		printf(
35636406Ssklower 	"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
35736406Ssklower 			so, cmd, optname, mp, mp?*mp:0, tpcb);
35836406Ssklower 	ENDDEBUG
35936406Ssklower 	if( tpcb == (struct tp_pcb *)0 ) {
36036406Ssklower 		error = ENOTSOCK; goto done;
36136406Ssklower 	}
36236406Ssklower 	if(*mp == MNULL) {
36336406Ssklower 		register struct mbuf *m;
36436406Ssklower 
36536406Ssklower 		MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
36636406Ssklower 		if (m == NULL) {
36736406Ssklower 			splx(s);
36836406Ssklower 			return ENOBUFS;
36936406Ssklower 		}
37036406Ssklower 		m->m_len = 0;
37136406Ssklower 		m->m_act = 0;
37236406Ssklower 		*mp = m;
37336406Ssklower 	}
37436406Ssklower 
37536406Ssklower 	/*
37636406Ssklower 	 *	Hook so one can set network options via a tp socket.
37736406Ssklower 	 */
37836406Ssklower 	if ( level == SOL_NETWORK ) {
37936406Ssklower 		if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
38036406Ssklower 			error = ENOTSOCK;
38136406Ssklower 		else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
38236406Ssklower 			error = EOPNOTSUPP;
38336406Ssklower 		else
384*50974Ssklower 			return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
385*50974Ssklower 				tpcb->tp_npcb, *mp));
38636406Ssklower 		goto done;
387*50974Ssklower 	} else if ( level == SOL_SOCKET) {
388*50974Ssklower 		if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
389*50974Ssklower 			tp_rsyset(tpcb);
390*50974Ssklower 		}
391*50974Ssklower 		goto done;
39236406Ssklower 	} else if ( level !=  SOL_TRANSPORT ) {
39336406Ssklower 		error = EOPNOTSUPP; goto done;
39436406Ssklower 	}
39536406Ssklower 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
39636406Ssklower 		error = EOPNOTSUPP; goto done;
39736406Ssklower 	}
39836406Ssklower 	if ( so->so_error ) {
39936406Ssklower 		error = so->so_error; goto done;
40036406Ssklower 	}
40136406Ssklower 
40236406Ssklower 	/* The only options allowed after connection is established
40336406Ssklower 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
40436406Ssklower 	 */
40536406Ssklower 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
40636406Ssklower 		&&
40736406Ssklower 		(cmd == PRCO_SETOPT  &&
40836406Ssklower 			optname != TPOPT_DISC_DATA &&
40938841Ssklower 			optname != TPOPT_CFRM_DATA &&
41036406Ssklower 			optname != TPOPT_PERF_MEAS &&
41136406Ssklower 			optname != TPOPT_CDDATA_CLEAR ) ) {
41236406Ssklower 		error = EISCONN; goto done;
41336406Ssklower 	}
41436406Ssklower 	/* The only options allowed after disconnection are GET DISC DATA,
41536406Ssklower 	 * and TPOPT_PSTATISTICS
41636406Ssklower 	 * and they're not allowed if the ref timer has gone off, because
41736406Ssklower 	 * the tpcb is gone
41836406Ssklower 	 */
41938841Ssklower 	if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) ==  0) {
42050435Ssklower 		if ( so->so_pcb == (caddr_t)0 ) {
42136406Ssklower 			error = ENOTCONN; goto done;
42236406Ssklower 		}
42336406Ssklower 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
42436406Ssklower 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
42536406Ssklower 			error = ENOTCONN; goto done;
42636406Ssklower 		}
42736406Ssklower 	}
42836406Ssklower 
42937469Ssklower 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
43037469Ssklower 								  * but lint complains about it
43137469Ssklower 								  */
43236406Ssklower 	val_len = (*mp)->m_len;
43336406Ssklower 
43436406Ssklower 	switch (optname) {
43536406Ssklower 
43644424Ssklower 	case TPOPT_INTERCEPT:
43750648Ssklower #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
43850648Ssklower #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
43950648Ssklower 
44050236Ssklower 		if ((so->so_state & SS_PRIV) == 0) {
44150236Ssklower 			error = EPERM;
44250648Ssklower 		} else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
44350648Ssklower 					(tpcb->tp_flags & TPF_GENERAL_ADDR) ||
44450648Ssklower 					tpcb->tp_next == 0)
44544424Ssklower 			error = EINVAL;
44644424Ssklower 		else {
44750648Ssklower 			register struct tp_pcb *t;
44850648Ssklower 			error = EADDRINUSE;
44950648Ssklower 			for (t = tp_listeners; t; t = t->tp_nextlisten)
45050648Ssklower 				if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
45150648Ssklower 						t->tp_domain == tpcb->tp_domain)
45250648Ssklower 					switch (tpcb->tp_domain) {
45350648Ssklower 					default:
45450648Ssklower 						goto done;
45550648Ssklower #ifdef	INET
45650648Ssklower 					case AF_INET:
45750648Ssklower 						if (INA(t) == INA(tpcb))
45850648Ssklower 							goto done;
45950648Ssklower 						continue;
46050648Ssklower #endif
46150648Ssklower #ifdef ISO
46250648Ssklower 					case AF_ISO:
46350648Ssklower 						if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
46450648Ssklower 										ISOA(t).isoa_len) == 0)
46550648Ssklower 							goto done;
46650648Ssklower 						continue;
46750648Ssklower #endif
46850648Ssklower 					}
46950648Ssklower 			tpcb->tp_lsuffixlen = 0;
47050648Ssklower 			tpcb->tp_state = TP_LISTENING;
47150648Ssklower 			error = 0;
47250648Ssklower 			remque(tpcb);
47350648Ssklower 			tpcb->tp_next = tpcb->tp_prev = tpcb;
47450648Ssklower 			tpcb->tp_nextlisten = tp_listeners;
47550648Ssklower 			tp_listeners = tpcb;
47644424Ssklower 		}
47744424Ssklower 		break;
47844424Ssklower 
47936406Ssklower 	case TPOPT_MY_TSEL:
48036406Ssklower 		if ( cmd == PRCO_GETOPT ) {
48136406Ssklower 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
48237469Ssklower 			bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
48336406Ssklower 			(*mp)->m_len = tpcb->tp_lsuffixlen;
48436406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
48536406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
48636406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
48736406Ssklower 				error = EINVAL;
48836406Ssklower 			} else {
48937469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
49036406Ssklower 				tpcb->tp_lsuffixlen = val_len;
49136406Ssklower 			}
49236406Ssklower 		}
49336406Ssklower 		break;
49436406Ssklower 
49536406Ssklower 	case TPOPT_PEER_TSEL:
49636406Ssklower 		if ( cmd == PRCO_GETOPT ) {
49736406Ssklower 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
49837469Ssklower 			bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
49936406Ssklower 			(*mp)->m_len = tpcb->tp_fsuffixlen;
50036406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
50136406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
50236406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
50336406Ssklower 				error = EINVAL;
50436406Ssklower 			} else {
50537469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
50636406Ssklower 				tpcb->tp_fsuffixlen = val_len;
50736406Ssklower 			}
50836406Ssklower 		}
50936406Ssklower 		break;
51036406Ssklower 
51136406Ssklower 	case TPOPT_FLAGS:
51236406Ssklower 		IFDEBUG(D_REQUEST)
51336406Ssklower 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
51436406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET",
51536406Ssklower 				value,
51636406Ssklower 				*value,
51736406Ssklower 				tpcb->tp_flags);
51836406Ssklower 		ENDDEBUG
51936406Ssklower 
52036406Ssklower 		if ( cmd == PRCO_GETOPT ) {
52136406Ssklower 			*(int *)value = (int)tpcb->tp_flags;
52236406Ssklower 			(*mp)->m_len = sizeof(u_int);
52336406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
52436406Ssklower 			error = EINVAL; goto done;
52536406Ssklower 		}
52636406Ssklower 		break;
52736406Ssklower 
52836406Ssklower 	case TPOPT_PARAMS:
52936406Ssklower 		/* This handles:
53036406Ssklower 		 * timer values,
53136406Ssklower 		 * class, use of transport expedited data,
53236406Ssklower 		 * max tpdu size, checksum, xtd format and
53336406Ssklower 		 * disconnect indications, and may get rid of connect/disc data
53436406Ssklower 		 */
53536406Ssklower 		IFDEBUG(D_SETPARAMS)
53636406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
53736406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
53836406Ssklower 		ENDDEBUG
53936406Ssklower 		IFDEBUG(D_REQUEST)
54036406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
54136406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
54236406Ssklower 		ENDDEBUG
54336406Ssklower 
54436406Ssklower 		if ( cmd == PRCO_GETOPT ) {
54536406Ssklower 			*(struct tp_conn_param *)value = tpcb->_tp_param;
54636406Ssklower 			(*mp)->m_len = sizeof(tpcb->_tp_param);
54736406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
54836406Ssklower 			if( (error =
54936406Ssklower 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
55036406Ssklower 								(struct tp_conn_param *)value))==0) {
55136406Ssklower 				/*
55236406Ssklower 				 * tp_consistency doesn't copy the whole set of params
55336406Ssklower 				 */
55436406Ssklower 				tpcb->_tp_param = *(struct tp_conn_param *)value;
55536406Ssklower 				(*mp)->m_len = sizeof(tpcb->_tp_param);
55636406Ssklower 			}
55736406Ssklower 		}
55836406Ssklower 		break;
55936406Ssklower 
56036406Ssklower 	case TPOPT_PSTATISTICS:
56136406Ssklower #ifdef TP_PERF_MEAS
56236406Ssklower 		if (cmd == PRCO_SETOPT) {
56336406Ssklower 			error = EINVAL; goto done;
56436406Ssklower 		}
56536406Ssklower 		IFPERF(tpcb)
56637469Ssklower 			if (*mp) {
56737469Ssklower 				struct mbuf * n;
56837469Ssklower 				do {
56937469Ssklower 					MFREE(*mp, n);
57037469Ssklower 					*mp = n;
57137469Ssklower 				} while (n);
57237469Ssklower 			}
57337469Ssklower 			*mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
57436406Ssklower 		ENDPERF
57536406Ssklower 		else {
57636406Ssklower 			error = EINVAL; goto done;
57736406Ssklower 		}
57836406Ssklower 		break;
57936406Ssklower #else
58036406Ssklower 		error = EOPNOTSUPP;
58136406Ssklower 		goto done;
58236406Ssklower #endif TP_PERF_MEAS
58336406Ssklower 
58436406Ssklower 	case TPOPT_CDDATA_CLEAR:
58536406Ssklower 		if (cmd == PRCO_GETOPT) {
58636406Ssklower 			error = EINVAL;
58736406Ssklower 		} else {
58837469Ssklower 			if (tpcb->tp_ucddata) {
58938841Ssklower 				m_freem(tpcb->tp_ucddata);
59038841Ssklower 				tpcb->tp_ucddata = 0;
59136406Ssklower 			}
59236406Ssklower 		}
59336406Ssklower 		break;
59436406Ssklower 
59538841Ssklower 	case TPOPT_CFRM_DATA:
59636406Ssklower 	case TPOPT_DISC_DATA:
59736406Ssklower 	case TPOPT_CONN_DATA:
59836406Ssklower 		if( tpcb->tp_class == TP_CLASS_0 ) {
59936406Ssklower 			error = EOPNOTSUPP;
60036406Ssklower 			break;
60136406Ssklower 		}
60236406Ssklower 		IFDEBUG(D_REQUEST)
60336406Ssklower 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
60436406Ssklower 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
60536406Ssklower 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
60636406Ssklower 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
60736406Ssklower 		ENDDEBUG
60836406Ssklower 		if (cmd == PRCO_SETOPT) {
60937469Ssklower 			int len = tpcb->tp_ucddata ?  tpcb->tp_ucddata->m_len : 0;
61036406Ssklower 			/* can append connect data in several calls */
61137469Ssklower 			if (len + val_len >
61236406Ssklower 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
61336406Ssklower 				error = EMSGSIZE; goto done;
61436406Ssklower 			}
61536406Ssklower 			(*mp)->m_next = MNULL;
61636406Ssklower 			(*mp)->m_act = 0;
61737469Ssklower 			if (tpcb->tp_ucddata)
61837469Ssklower 				m_cat(tpcb->tp_ucddata, *mp);
61937469Ssklower 			else
62037469Ssklower 				tpcb->tp_ucddata = *mp;
62140241Ssklower 			IFDEBUG(D_REQUEST)
62240241Ssklower 				dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
62340241Ssklower 			ENDDEBUG
62436406Ssklower 			IFTRACE(D_REQUEST)
62536406Ssklower 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
62636406Ssklower 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
62736406Ssklower 			ENDTRACE
628*50974Ssklower 			*mp = MNULL;
62940241Ssklower 			if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
63040241Ssklower 				(void) tp_confirm(tpcb);
63136406Ssklower 		}
63236406Ssklower 		break;
63336406Ssklower 
63436406Ssklower 	case TPOPT_PERF_MEAS:
63536406Ssklower #ifdef TP_PERF_MEAS
63636406Ssklower 		if (cmd == PRCO_GETOPT) {
63736406Ssklower 			*value = (u_int)tpcb->tp_perf_on;
63836406Ssklower 			(*mp)->m_len = sizeof(u_int);
63936406Ssklower 		} else if (cmd == PRCO_SETOPT) {
64036406Ssklower 			(*mp)->m_len = 0;
64136406Ssklower 			if ((*value) != 0 && (*value) != 1 )
64236406Ssklower 				error = EINVAL;
64336406Ssklower 			else  tpcb->tp_perf_on = (*value);
64436406Ssklower 		}
64536406Ssklower 		if( tpcb->tp_perf_on )
64636406Ssklower 			error = tp_setup_perf(tpcb);
64736406Ssklower #else  TP_PERF_MEAS
64836406Ssklower 		error = EOPNOTSUPP;
64936406Ssklower #endif TP_PERF_MEAS
65036406Ssklower 		break;
65136406Ssklower 
65236406Ssklower 	default:
65336406Ssklower 		error = EOPNOTSUPP;
65436406Ssklower 	}
65536406Ssklower 
65636406Ssklower done:
65736406Ssklower 	IFDEBUG(D_REQUEST)
65836406Ssklower 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
65936406Ssklower 		dump_mbuf(*mp, "tp_ctloutput *mp");
66036406Ssklower 	ENDDEBUG
66136406Ssklower 	/*
66236406Ssklower 	 * sigh: getsockopt looks only at m_len : all output data must
66336406Ssklower 	 * reside in the first mbuf
66436406Ssklower 	 */
665*50974Ssklower 	if (*mp) {
666*50974Ssklower 		if (cmd == PRCO_SETOPT)
667*50974Ssklower 			m_freem(*mp);
668*50974Ssklower 		else {
669*50974Ssklower 			ASSERT ( m_compress(*mp, mp) <= MLEN );
670*50974Ssklower 			if (error)
671*50974Ssklower 				(*mp)->m_len = 0;
672*50974Ssklower 			IFDEBUG(D_REQUEST)
673*50974Ssklower 				dump_mbuf(*mp, "tp_ctloutput *mp after compress");
674*50974Ssklower 			ENDDEBUG
675*50974Ssklower 		}
67636406Ssklower 	}
67736406Ssklower 	splx(s);
67836406Ssklower 	return error;
67936406Ssklower }
680