xref: /csrg-svn/sys/netiso/tp_output.c (revision 37469)
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 $
3236406Ssklower  *
3336406Ssklower  * In here is tp_ctloutput(), the guy called by [sg]etsockopt(),
3436406Ssklower  */
3536406Ssklower 
3636406Ssklower #ifndef lint
3736406Ssklower static char *rcsid = "$Header: tp_output.c,v 5.4 88/11/18 17:28:08 nhall Exp $";
3836406Ssklower #endif lint
3936406Ssklower 
4036406Ssklower #include "param.h"
4136406Ssklower #include "systm.h"
4236406Ssklower #include "mbuf.h"
4336406Ssklower #include "protosw.h"
4436406Ssklower #include "socket.h"
4536406Ssklower #include "socketvar.h"
4636406Ssklower #include "errno.h"
4736406Ssklower #include "types.h"
4836406Ssklower #include "time.h"
49*37469Ssklower #include "tp_param.h"
50*37469Ssklower #include "tp_user.h"
51*37469Ssklower #include "tp_stat.h"
52*37469Ssklower #include "tp_ip.h"
53*37469Ssklower #include "tp_timer.h"
54*37469Ssklower #include "argo_debug.h"
55*37469Ssklower #include "tp_pcb.h"
56*37469Ssklower #include "tp_trace.h"
5736406Ssklower 
5836406Ssklower #define USERFLAGSMASK_G 0x0f00643b
5936406Ssklower #define USERFLAGSMASK_S 0x0f000432
6036406Ssklower #define TPDUSIZESHIFT 24
6136406Ssklower #define CLASSHIFT 16
6236406Ssklower 
6336406Ssklower /*
6436406Ssklower  * NAME: 	tp_consistency()
6536406Ssklower  *
6636406Ssklower  * CALLED FROM:
6736406Ssklower  * 	tp_ctloutput(), tp_input()
6836406Ssklower  *
6936406Ssklower  * FUNCTION and ARGUMENTS:
7036406Ssklower  * 	Checks the consistency of options and tpdusize with class,
7136406Ssklower  *	using the parameters passed in via (param).
7236406Ssklower  *	(cmd) may be TP_STRICT or TP_FORCE or both.
7336406Ssklower  *  Force means it will set all the values in (tpcb) to those in
7436406Ssklower  *  the input arguements iff no errors were encountered.
7536406Ssklower  *  Strict means that no inconsistency will be tolerated.  If it's
7636406Ssklower  *  not used, checksum and tpdusize inconsistencies will be tolerated.
7736406Ssklower  *  The reason for this is that in some cases, when we're negotiating down
7836406Ssklower  *	from class  4, these options should be changed but should not
7936406Ssklower  *  cause negotiation to fail.
8036406Ssklower  *
8136406Ssklower  * RETURNS
8236406Ssklower  *  E* or EOK
8336406Ssklower  *  E* if the various parms aren't ok for a given class
8436406Ssklower  *  EOK if they are ok for a given class
8536406Ssklower  */
8636406Ssklower 
8736406Ssklower int
8836406Ssklower tp_consistency( tpcb, cmd, param )
8936406Ssklower 	u_int cmd;
9036406Ssklower 	struct tp_conn_param *param;
9136406Ssklower 	struct tp_pcb *tpcb;
9236406Ssklower {
9336406Ssklower 	register int	error = EOK;
9436406Ssklower 	int 			class_to_use  = tp_mask_to_num(param->p_class);
9536406Ssklower 
9636406Ssklower 	IFTRACE(D_SETPARAMS)
9736406Ssklower 		tptrace(TPPTmisc,
9836406Ssklower 		"tp_consist enter class_to_use dontchange param.class cmd",
9936406Ssklower 		class_to_use, param->p_dont_change_params, param->p_class, cmd);
10036406Ssklower 	ENDTRACE
10136406Ssklower 	IFDEBUG(D_SETPARAMS)
10236406Ssklower 		printf("tp_consistency %s %s\n",
10336406Ssklower 			cmd& TP_FORCE?	"TP_FORCE":	"",
10436406Ssklower 			cmd& TP_STRICT?	"TP_STRICT":"");
10536406Ssklower 	ENDDEBUG
10636406Ssklower 	if ((cmd & TP_FORCE) && (param->p_dont_change_params)) {
10736406Ssklower 		cmd &= ~TP_FORCE;
10836406Ssklower 	}
10936406Ssklower 	/* can switch net services within a domain, but
11036406Ssklower 	 * cannot switch domains
11136406Ssklower 	 */
11236406Ssklower 	switch( param->p_netservice) {
11336406Ssklower 	case ISO_CONS:
11436406Ssklower 	case ISO_CLNS:
11536406Ssklower 	case ISO_COSNS:
11636406Ssklower 		/* param->p_netservice in ISO DOMAIN */
11736406Ssklower 		if(tpcb->tp_domain != AF_ISO ) {
11836406Ssklower 			error = EINVAL; goto done;
11936406Ssklower 		}
12036406Ssklower 		break;
12136406Ssklower 	case IN_CLNS:
12236406Ssklower 		/* param->p_netservice in INET DOMAIN */
12336406Ssklower 		if( tpcb->tp_domain != AF_INET ) {
12436406Ssklower 			error = EINVAL; goto done;
12536406Ssklower 		}
12636406Ssklower 		break;
12736406Ssklower 		/* no others not possible-> netservice is a 2-bit field! */
12836406Ssklower 	}
12936406Ssklower 
13036406Ssklower 	IFDEBUG(D_SETPARAMS)
13136406Ssklower 		printf("p_class 0x%x, class_to_use 0x%x\n",  param->p_class,
13236406Ssklower 			class_to_use);
13336406Ssklower 	ENDDEBUG
13436406Ssklower 	if((param->p_netservice < 0) || (param->p_netservice > TP_MAX_NETSERVICES)){
13536406Ssklower 		error = EINVAL; goto done;
13636406Ssklower 	}
13736406Ssklower 	if( (param->p_class & TP_CLASSES_IMPLEMENTED) == 0 ) {
13836406Ssklower 		error = EINVAL; goto done;
13936406Ssklower 	}
14036406Ssklower 	IFDEBUG(D_SETPARAMS)
14136406Ssklower 		printf("Nretrans 0x%x\n",  param->p_Nretrans );
14236406Ssklower 	ENDDEBUG
14336406Ssklower 	if( ( param->p_Nretrans < 1 ) ||
14436406Ssklower 		  (param->p_cr_ticks < 1) || (param->p_cc_ticks < 1) ) {
14536406Ssklower 			/* bad for any class because negot has to be done a la class 4 */
14636406Ssklower 			error = EINVAL; goto done;
14736406Ssklower 	}
14836406Ssklower 	IFDEBUG(D_SETPARAMS)
14936406Ssklower 		printf("winsize 0x%x\n",  param->p_winsize );
15036406Ssklower 	ENDDEBUG
15136406Ssklower 	if( (param->p_winsize < 128 ) ||
15236406Ssklower 		(param->p_winsize < param->p_tpdusize ) ||
15336406Ssklower 		(param->p_winsize > ((1+SB_MAX)>>2 /* 1/4 of the max */)) ) {
15436406Ssklower 			error = EINVAL; goto done;
15536406Ssklower 	} else {
15636406Ssklower 		if( tpcb->tp_state == TP_CLOSED )
157*37469Ssklower 			soreserve(tpcb->tp_sock, (u_long)param->p_winsize,
158*37469Ssklower 						(u_long)param->p_winsize);
15936406Ssklower 	}
16036406Ssklower 	IFDEBUG(D_SETPARAMS)
16136406Ssklower 		printf("use_csum 0x%x\n",  param->p_use_checksum );
16236406Ssklower 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
16336406Ssklower 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
16436406Ssklower 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
16536406Ssklower 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
16636406Ssklower 	ENDDEBUG
16736406Ssklower 	switch( class_to_use ) {
16836406Ssklower 
16936406Ssklower 	case 0:
17036406Ssklower 		/* do not use checksums, xtd format, or XPD */
17136406Ssklower 
17236406Ssklower 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
17336406Ssklower 			if(cmd & TP_STRICT) {
17436406Ssklower 				error = EINVAL;
17536406Ssklower 			} else {
17636406Ssklower 				param->p_use_checksum = 0;
17736406Ssklower 				param->p_xtd_format = 0;
17836406Ssklower 				param->p_xpd_service = 0;
17936406Ssklower 			}
18036406Ssklower 			break;
18136406Ssklower 		}
18236406Ssklower 
18336406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
18436406Ssklower 			if(cmd & TP_STRICT) {
18536406Ssklower 				error = EINVAL;
18636406Ssklower 			} else {
18736406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
18836406Ssklower 			}
18936406Ssklower 			break;
19036406Ssklower 		}
19136406Ssklower 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
19236406Ssklower 			if (cmd & TP_STRICT) {
19336406Ssklower 				error = EINVAL;
19436406Ssklower 			} else {
19536406Ssklower 				param->p_tpdusize = TP0_TPDUSIZE;
19636406Ssklower 			}
19736406Ssklower 			break;
19836406Ssklower 		}
19936406Ssklower 
20036406Ssklower 		/* connect/disc data not allowed for class 0 */
201*37469Ssklower 		if (tpcb->tp_ucddata) {
20236406Ssklower 			if(cmd & TP_STRICT) {
20336406Ssklower 				error = EINVAL;
20436406Ssklower 			} else if(cmd & TP_FORCE) {
205*37469Ssklower 				m_freem(tpcb->tp_ucddata);
206*37469Ssklower 				tpcb->tp_ucddata = 0;
20736406Ssklower 			}
20836406Ssklower 		}
20936406Ssklower 		break;
21036406Ssklower 
21136406Ssklower 	case 4:
21236406Ssklower 		IFDEBUG(D_SETPARAMS)
21336406Ssklower 			printf("dt_ticks 0x%x\n",  param->p_dt_ticks );
21436406Ssklower 			printf("x_ticks 0x%x\n",  param->p_x_ticks );
21536406Ssklower 			printf("dr_ticks 0x%x\n",  param->p_dr_ticks );
21636406Ssklower 			printf("keepalive 0x%x\n",  param->p_keepalive_ticks );
21736406Ssklower 			printf("sendack 0x%x\n",  param->p_sendack_ticks );
21836406Ssklower 			printf("inact 0x%x\n",  param->p_inact_ticks );
21936406Ssklower 			printf("ref 0x%x\n",  param->p_ref_ticks );
22036406Ssklower 		ENDDEBUG
22136406Ssklower 		if( (param->p_class & TP_CLASS_4 ) && (
22236406Ssklower 			  (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
22336406Ssklower 			  (param->p_x_ticks < 1)	|| (param->p_keepalive_ticks < 1) ||
22436406Ssklower 			  (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
22536406Ssklower 			  (param->p_inact_ticks < 1) ) ) {
22636406Ssklower 				error = EINVAL;
22736406Ssklower 				break;
22836406Ssklower 		}
22936406Ssklower 		IFDEBUG(D_SETPARAMS)
23036406Ssklower 			printf("rx_strat 0x%x\n",  param->p_rx_strat );
23136406Ssklower 		ENDDEBUG
23236406Ssklower 		if(param->p_rx_strat >
23336406Ssklower 			( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
23436406Ssklower 				if(cmd & TP_STRICT) {
23536406Ssklower 					error = EINVAL;
23636406Ssklower 				} else {
23736406Ssklower 					param->p_rx_strat = TPRX_USE_CW;
23836406Ssklower 				}
23936406Ssklower 				break;
24036406Ssklower 		}
24136406Ssklower 		IFDEBUG(D_SETPARAMS)
24236406Ssklower 			printf("ack_strat 0x%x\n",  param->p_ack_strat );
24336406Ssklower 		ENDDEBUG
24436406Ssklower 		if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
24536406Ssklower 			if(cmd & TP_STRICT) {
24636406Ssklower 				error = EINVAL;
24736406Ssklower 			} else {
24836406Ssklower 				param->p_ack_strat = TPACK_WINDOW;
24936406Ssklower 			}
25036406Ssklower 			break;
25136406Ssklower 		}
25236406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
25336406Ssklower 			if(cmd & TP_STRICT) {
25436406Ssklower 				error = EINVAL;
25536406Ssklower 			} else {
25636406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
25736406Ssklower 			}
25836406Ssklower 			break;
25936406Ssklower 		}
26036406Ssklower 		if (param->p_tpdusize > TP_TPDUSIZE)  {
26136406Ssklower 			if(cmd & TP_STRICT) {
26236406Ssklower 				error = EINVAL;
26336406Ssklower 			} else {
26436406Ssklower 				param->p_tpdusize = TP_TPDUSIZE;
26536406Ssklower 			}
26636406Ssklower 			break;
26736406Ssklower 		}
26836406Ssklower 		break;
26936406Ssklower 	}
27036406Ssklower 
27136406Ssklower 	if ((error==0) && (cmd & TP_FORCE)) {
27236406Ssklower 		tpcb->tp_tpdusize = param->p_tpdusize;
27336406Ssklower 		tpcb->tp_class = param->p_class;
27436406Ssklower 		tpcb->tp_use_checksum = param->p_use_checksum;
27536406Ssklower 		tpcb->tp_xpd_service = param->p_xpd_service;
27636406Ssklower 		tpcb->tp_xtd_format = param->p_xtd_format;
27736406Ssklower 	}
27836406Ssklower 
27936406Ssklower done:
28036406Ssklower 
28136406Ssklower 	IFTRACE(D_CONN)
28236406Ssklower 		tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
28336406Ssklower 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
28436406Ssklower 	ENDTRACE
28536406Ssklower 	IFDEBUG(D_CONN)
28636406Ssklower 		printf(
28736406Ssklower 		"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
28836406Ssklower 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
28936406Ssklower 	ENDDEBUG
29036406Ssklower 	return error;
29136406Ssklower }
29236406Ssklower 
29336406Ssklower /*
29436406Ssklower  * NAME: 	tp_ctloutput()
29536406Ssklower  *
29636406Ssklower  * CALLED FROM:
29736406Ssklower  * 	[sg]etsockopt(), via so[sg]etopt().
29836406Ssklower  *
29936406Ssklower  * FUNCTION and ARGUMENTS:
30036406Ssklower  * 	Implements the socket options at transport level.
30136406Ssklower  * 	(cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../h/protosw.h).
30236406Ssklower  * 	(so) is the socket.
30336406Ssklower  * 	(level) is SOL_TRANSPORT (see ../h/socket.h)
30436406Ssklower  * 	(optname) is the particular command or option to be set.
30536406Ssklower  * 	(**mp) is an mbuf structure.
30636406Ssklower  *
30736406Ssklower  * RETURN VALUE:
30836406Ssklower  * 	ENOTSOCK if the socket hasn't got an associated tpcb
30936406Ssklower  *  EINVAL if
31036406Ssklower  * 		trying to set window too big
31136406Ssklower  * 		trying to set illegal max tpdu size
31236406Ssklower  * 		trying to set illegal credit fraction
31336406Ssklower  * 		trying to use unknown or unimplemented class of TP
31436406Ssklower  *		structure passed to set timer values is wrong size
31536406Ssklower  *  	illegal combination of command/GET-SET option,
31636406Ssklower  *			e.g., GET w/ TPOPT_CDDATA_CLEAR:
31736406Ssklower  *  EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
31836406Ssklower  *   or if the transport-specific command is not implemented
31936406Ssklower  *  EISCONN if trying a command that isn't allowed after a connection
32036406Ssklower  *   is established
32136406Ssklower  *  ENOTCONN if trying a command that is allowed only if a connection is
32236406Ssklower  *   established
32336406Ssklower  *  EMSGSIZE if trying to give too much data on connect/disconnect
32436406Ssklower  *
32536406Ssklower  * SIDE EFFECTS:
32636406Ssklower  *
32736406Ssklower  * NOTES:
32836406Ssklower  */
32936406Ssklower ProtoHook
33036406Ssklower tp_ctloutput(cmd, so, level, optname, mp)
33136406Ssklower 	int 			cmd, level, optname;
33236406Ssklower 	struct socket	*so;
33336406Ssklower 	struct mbuf 	**mp;
33436406Ssklower {
33536406Ssklower 	struct		tp_pcb	*tpcb = sototpcb(so);
33636406Ssklower 	int 		s = splnet();
337*37469Ssklower 	caddr_t		value;
338*37469Ssklower 	unsigned	val_len;
33936406Ssklower 	int			error = 0;
34036406Ssklower 
34136406Ssklower 	IFTRACE(D_REQUEST)
34236406Ssklower 		tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
34336406Ssklower 			cmd, so, optname, mp);
34436406Ssklower 	ENDTRACE
34536406Ssklower 	IFDEBUG(D_REQUEST)
34636406Ssklower 		printf(
34736406Ssklower 	"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
34836406Ssklower 			so, cmd, optname, mp, mp?*mp:0, tpcb);
34936406Ssklower 	ENDDEBUG
35036406Ssklower 	if( tpcb == (struct tp_pcb *)0 ) {
35136406Ssklower 		error = ENOTSOCK; goto done;
35236406Ssklower 	}
35336406Ssklower 	if(*mp == MNULL) {
35436406Ssklower 		register struct mbuf *m;
35536406Ssklower 
35636406Ssklower 		MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
35736406Ssklower 		if (m == NULL) {
35836406Ssklower 			splx(s);
35936406Ssklower 			return ENOBUFS;
36036406Ssklower 		}
36136406Ssklower 		m->m_len = 0;
36236406Ssklower 		m->m_act = 0;
36336406Ssklower 		*mp = m;
36436406Ssklower 	}
36536406Ssklower 
36636406Ssklower 	/*
36736406Ssklower 	 *	Hook so one can set network options via a tp socket.
36836406Ssklower 	 */
36936406Ssklower 	if ( level == SOL_NETWORK ) {
37036406Ssklower 		if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
37136406Ssklower 			error = ENOTSOCK;
37236406Ssklower 		else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
37336406Ssklower 			error = EOPNOTSUPP;
37436406Ssklower 		else
37536406Ssklower 			error = (tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
37636406Ssklower 				tpcb->tp_npcb, *mp);
37736406Ssklower 		goto done;
37836406Ssklower 	} else if ( level !=  SOL_TRANSPORT ) {
37936406Ssklower 		error = EOPNOTSUPP; goto done;
38036406Ssklower 	}
38136406Ssklower 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
38236406Ssklower 		error = EOPNOTSUPP; goto done;
38336406Ssklower 	}
38436406Ssklower 	if ( so->so_error ) {
38536406Ssklower 		error = so->so_error; goto done;
38636406Ssklower 	}
38736406Ssklower 
38836406Ssklower 	/* The only options allowed after connection is established
38936406Ssklower 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
39036406Ssklower 	 */
39136406Ssklower 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
39236406Ssklower 		&&
39336406Ssklower 		(cmd == PRCO_SETOPT  &&
39436406Ssklower 			optname != TPOPT_DISC_DATA &&
39536406Ssklower 			optname != TPOPT_PERF_MEAS &&
39636406Ssklower 			optname != TPOPT_CDDATA_CLEAR ) ) {
39736406Ssklower 		error = EISCONN; goto done;
39836406Ssklower 	}
39936406Ssklower 	/* The only options allowed after disconnection are GET DISC DATA,
40036406Ssklower 	 * and TPOPT_PSTATISTICS
40136406Ssklower 	 * and they're not allowed if the ref timer has gone off, because
40236406Ssklower 	 * the tpcb is gone
40336406Ssklower 	 */
40436406Ssklower 	if ((so->so_state & SS_ISCONNECTED) ==  0) {
40536406Ssklower 		if ( so->so_tpcb == (caddr_t)0 ) {
40636406Ssklower 			error = ENOTCONN; goto done;
40736406Ssklower 		}
40836406Ssklower 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
40936406Ssklower 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
41036406Ssklower 			error = ENOTCONN; goto done;
41136406Ssklower 		}
41236406Ssklower 	}
41336406Ssklower 
414*37469Ssklower 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
415*37469Ssklower 								  * but lint complains about it
416*37469Ssklower 								  */
41736406Ssklower 	val_len = (*mp)->m_len;
41836406Ssklower 
41936406Ssklower 	switch (optname) {
42036406Ssklower 
42136406Ssklower 	case TPOPT_MY_TSEL:
42236406Ssklower 		if ( cmd == PRCO_GETOPT ) {
42336406Ssklower 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
424*37469Ssklower 			bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
42536406Ssklower 			(*mp)->m_len = tpcb->tp_lsuffixlen;
42636406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
42736406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
42836406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
42936406Ssklower 				error = EINVAL;
43036406Ssklower 			} else {
431*37469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
43236406Ssklower 				tpcb->tp_lsuffixlen = val_len;
43336406Ssklower 			}
43436406Ssklower 		}
43536406Ssklower 		break;
43636406Ssklower 
43736406Ssklower 	case TPOPT_PEER_TSEL:
43836406Ssklower 		if ( cmd == PRCO_GETOPT ) {
43936406Ssklower 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
440*37469Ssklower 			bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
44136406Ssklower 			(*mp)->m_len = tpcb->tp_fsuffixlen;
44236406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
44336406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
44436406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
44536406Ssklower 				error = EINVAL;
44636406Ssklower 			} else {
447*37469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
44836406Ssklower 				tpcb->tp_fsuffixlen = val_len;
44936406Ssklower 			}
45036406Ssklower 		}
45136406Ssklower 		break;
45236406Ssklower 
45336406Ssklower 	case TPOPT_FLAGS:
45436406Ssklower 		IFDEBUG(D_REQUEST)
45536406Ssklower 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
45636406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET",
45736406Ssklower 				value,
45836406Ssklower 				*value,
45936406Ssklower 				tpcb->tp_flags);
46036406Ssklower 		ENDDEBUG
46136406Ssklower 
46236406Ssklower 		if ( cmd == PRCO_GETOPT ) {
46336406Ssklower 			*(int *)value = (int)tpcb->tp_flags;
46436406Ssklower 			(*mp)->m_len = sizeof(u_int);
46536406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
46636406Ssklower 			error = EINVAL; goto done;
46736406Ssklower 		}
46836406Ssklower 		break;
46936406Ssklower 
47036406Ssklower 	case TPOPT_PARAMS:
47136406Ssklower 		/* This handles:
47236406Ssklower 		 * timer values,
47336406Ssklower 		 * class, use of transport expedited data,
47436406Ssklower 		 * max tpdu size, checksum, xtd format and
47536406Ssklower 		 * disconnect indications, and may get rid of connect/disc data
47636406Ssklower 		 */
47736406Ssklower 		IFDEBUG(D_SETPARAMS)
47836406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
47936406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
48036406Ssklower 		ENDDEBUG
48136406Ssklower 		IFDEBUG(D_REQUEST)
48236406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
48336406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
48436406Ssklower 		ENDDEBUG
48536406Ssklower 
48636406Ssklower 		if ( cmd == PRCO_GETOPT ) {
48736406Ssklower 			*(struct tp_conn_param *)value = tpcb->_tp_param;
48836406Ssklower 			(*mp)->m_len = sizeof(tpcb->_tp_param);
48936406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
49036406Ssklower 			if( (error =
49136406Ssklower 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
49236406Ssklower 								(struct tp_conn_param *)value))==0) {
49336406Ssklower 				/*
49436406Ssklower 				 * tp_consistency doesn't copy the whole set of params
49536406Ssklower 				 */
49636406Ssklower 				tpcb->_tp_param = *(struct tp_conn_param *)value;
49736406Ssklower 				(*mp)->m_len = sizeof(tpcb->_tp_param);
49836406Ssklower 			}
49936406Ssklower 		}
50036406Ssklower 		break;
50136406Ssklower 
50236406Ssklower 	case TPOPT_PSTATISTICS:
50336406Ssklower #ifdef TP_PERF_MEAS
50436406Ssklower 		if (cmd == PRCO_SETOPT) {
50536406Ssklower 			error = EINVAL; goto done;
50636406Ssklower 		}
50736406Ssklower 		IFPERF(tpcb)
508*37469Ssklower 			if (*mp) {
509*37469Ssklower 				struct mbuf * n;
510*37469Ssklower 				do {
511*37469Ssklower 					MFREE(*mp, n);
512*37469Ssklower 					*mp = n;
513*37469Ssklower 				} while (n);
514*37469Ssklower 			}
515*37469Ssklower 			*mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
51636406Ssklower 		ENDPERF
51736406Ssklower 		else {
51836406Ssklower 			error = EINVAL; goto done;
51936406Ssklower 		}
52036406Ssklower 		break;
52136406Ssklower #else
52236406Ssklower 		error = EOPNOTSUPP;
52336406Ssklower 		goto done;
52436406Ssklower #endif TP_PERF_MEAS
52536406Ssklower 
52636406Ssklower 	case TPOPT_CDDATA_CLEAR:
52736406Ssklower 		if (cmd == PRCO_GETOPT) {
52836406Ssklower 			error = EINVAL;
52936406Ssklower 		} else {
530*37469Ssklower 			if (tpcb->tp_ucddata) {
531*37469Ssklower 					m_freem(tpcb->tp_ucddata);
532*37469Ssklower 					tpcb->tp_ucddata = 0;
53336406Ssklower 			}
53436406Ssklower 		}
53536406Ssklower 		break;
53636406Ssklower 
53736406Ssklower 	case TPOPT_DISC_DATA:
53836406Ssklower 		/* drop through */
53936406Ssklower 	/* sending is for debugging purposes only -- we don't pretend
54036406Ssklower 	 * to support * data on connect or disconnect fully. It's a
54136406Ssklower 	 * kludge at best.
54236406Ssklower 	 * This data-on-connect is only for the active side.  It's sort of
54336406Ssklower 	 * meaningless on the passive side (because
54436406Ssklower 	 * you can't reject a connect request based on the data
54536406Ssklower 	 * arriving w/ the CR, this, and because you'd have to
54636406Ssklower 	 * do this setsockopt system call for each accept).
54736406Ssklower 	 * but you can use it if you want.
54836406Ssklower 	 */
54936406Ssklower 	case TPOPT_CONN_DATA:
55036406Ssklower 		if( tpcb->tp_class == TP_CLASS_0 ) {
55136406Ssklower 			error = EOPNOTSUPP;
55236406Ssklower 			break;
55336406Ssklower 		}
55436406Ssklower 		IFDEBUG(D_REQUEST)
55536406Ssklower 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
55636406Ssklower 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
55736406Ssklower 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
55836406Ssklower 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
55936406Ssklower 		ENDDEBUG
56036406Ssklower 		if (cmd == PRCO_SETOPT) {
561*37469Ssklower 			int len = tpcb->tp_ucddata ?  tpcb->tp_ucddata->m_len : 0;
56236406Ssklower 			/* can append connect data in several calls */
563*37469Ssklower 			if (len + val_len >
56436406Ssklower 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
56536406Ssklower 				error = EMSGSIZE; goto done;
56636406Ssklower 			}
56736406Ssklower 			(*mp)->m_next = MNULL;
56836406Ssklower 			(*mp)->m_act = 0;
569*37469Ssklower 			if (tpcb->tp_ucddata)
570*37469Ssklower 				m_cat(tpcb->tp_ucddata, *mp);
571*37469Ssklower 			else
572*37469Ssklower 				tpcb->tp_ucddata = *mp;
57336406Ssklower 		IFDEBUG(D_REQUEST)
574*37469Ssklower 			dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
57536406Ssklower 		ENDDEBUG
57636406Ssklower 			IFTRACE(D_REQUEST)
57736406Ssklower 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
57836406Ssklower 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
57936406Ssklower 			ENDTRACE
58036406Ssklower 			*mp = MNULL; /* prevent sosetopt from freeing it! */
58136406Ssklower 		}
58236406Ssklower 		break;
58336406Ssklower 
58436406Ssklower 	case TPOPT_PERF_MEAS:
58536406Ssklower #ifdef TP_PERF_MEAS
58636406Ssklower 		if (cmd == PRCO_GETOPT) {
58736406Ssklower 			*value = (u_int)tpcb->tp_perf_on;
58836406Ssklower 			(*mp)->m_len = sizeof(u_int);
58936406Ssklower 		} else if (cmd == PRCO_SETOPT) {
59036406Ssklower 			(*mp)->m_len = 0;
59136406Ssklower 			if ((*value) != 0 && (*value) != 1 )
59236406Ssklower 				error = EINVAL;
59336406Ssklower 			else  tpcb->tp_perf_on = (*value);
59436406Ssklower 		}
59536406Ssklower 		if( tpcb->tp_perf_on )
59636406Ssklower 			error = tp_setup_perf(tpcb);
59736406Ssklower #else  TP_PERF_MEAS
59836406Ssklower 		error = EOPNOTSUPP;
59936406Ssklower #endif TP_PERF_MEAS
60036406Ssklower 		break;
60136406Ssklower 
60236406Ssklower 	default:
60336406Ssklower 		error = EOPNOTSUPP;
60436406Ssklower 	}
60536406Ssklower 
60636406Ssklower done:
60736406Ssklower 	IFDEBUG(D_REQUEST)
60836406Ssklower 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
60936406Ssklower 		dump_mbuf(*mp, "tp_ctloutput *mp");
61036406Ssklower 	ENDDEBUG
61136406Ssklower 	/*
61236406Ssklower 	 * sigh: getsockopt looks only at m_len : all output data must
61336406Ssklower 	 * reside in the first mbuf
61436406Ssklower 	 */
61536406Ssklower 	if ( error  && (*mp) != MNULL )
61636406Ssklower 		(*mp)->m_len = 0;
61736406Ssklower 	if( (*mp) != MNULL ) {
61836406Ssklower 		ASSERT ( m_compress(*mp, mp) <= MLEN );
61936406Ssklower 		IFDEBUG(D_REQUEST)
62036406Ssklower 			dump_mbuf(*mp, "tp_ctloutput *mp after compress");
62136406Ssklower 		ENDDEBUG
62236406Ssklower 	}
62336406Ssklower 
62436406Ssklower 	splx(s);
62536406Ssklower 	return error;
62636406Ssklower }
627