xref: /csrg-svn/sys/netiso/tp_output.c (revision 53688)
149268Sbostic /*-
249268Sbostic  * Copyright (c) 1991 The Regents of the University of California.
349268Sbostic  * All rights reserved.
449268Sbostic  *
549268Sbostic  * %sccs.include.redist.c%
649268Sbostic  *
7*53688Ssklower  *	@(#)tp_output.c	7.16 (Berkeley) 05/27/92
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("use_csum 0x%x\n",  param->p_use_checksum );
15436406Ssklower 		printf("xtd_format 0x%x\n",  param->p_xtd_format );
15536406Ssklower 		printf("xpd_service 0x%x\n",  param->p_xpd_service );
15636406Ssklower 		printf("tpdusize 0x%x\n",  param->p_tpdusize );
15736406Ssklower 		printf("tpcb->flags 0x%x\n",  tpcb->tp_flags );
15836406Ssklower 	ENDDEBUG
15936406Ssklower 	switch( class_to_use ) {
16036406Ssklower 
16136406Ssklower 	case 0:
16236406Ssklower 		/* do not use checksums, xtd format, or XPD */
16336406Ssklower 
16436406Ssklower 		if( param->p_use_checksum | param->p_xtd_format | param->p_xpd_service ) {
16536406Ssklower 			if(cmd & TP_STRICT) {
16636406Ssklower 				error = EINVAL;
16736406Ssklower 			} else {
16836406Ssklower 				param->p_use_checksum = 0;
16936406Ssklower 				param->p_xtd_format = 0;
17036406Ssklower 				param->p_xpd_service = 0;
17136406Ssklower 			}
17236406Ssklower 			break;
17336406Ssklower 		}
17436406Ssklower 
17536406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
17636406Ssklower 			if(cmd & TP_STRICT) {
17736406Ssklower 				error = EINVAL;
17836406Ssklower 			} else {
17936406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
18036406Ssklower 			}
18136406Ssklower 			break;
18236406Ssklower 		}
18336406Ssklower 		if (param->p_tpdusize > TP0_TPDUSIZE)  {
18436406Ssklower 			if (cmd & TP_STRICT) {
18536406Ssklower 				error = EINVAL;
18636406Ssklower 			} else {
18736406Ssklower 				param->p_tpdusize = TP0_TPDUSIZE;
18836406Ssklower 			}
18936406Ssklower 			break;
19036406Ssklower 		}
19136406Ssklower 
19236406Ssklower 		/* connect/disc data not allowed for class 0 */
19337469Ssklower 		if (tpcb->tp_ucddata) {
19436406Ssklower 			if(cmd & TP_STRICT) {
19536406Ssklower 				error = EINVAL;
19636406Ssklower 			} else if(cmd & TP_FORCE) {
19737469Ssklower 				m_freem(tpcb->tp_ucddata);
19837469Ssklower 				tpcb->tp_ucddata = 0;
19936406Ssklower 			}
20036406Ssklower 		}
20136406Ssklower 		break;
20236406Ssklower 
20336406Ssklower 	case 4:
20436406Ssklower 		IFDEBUG(D_SETPARAMS)
20536406Ssklower 			printf("dt_ticks 0x%x\n",  param->p_dt_ticks );
20636406Ssklower 			printf("x_ticks 0x%x\n",  param->p_x_ticks );
20736406Ssklower 			printf("dr_ticks 0x%x\n",  param->p_dr_ticks );
20836406Ssklower 			printf("keepalive 0x%x\n",  param->p_keepalive_ticks );
20936406Ssklower 			printf("sendack 0x%x\n",  param->p_sendack_ticks );
21036406Ssklower 			printf("inact 0x%x\n",  param->p_inact_ticks );
21136406Ssklower 			printf("ref 0x%x\n",  param->p_ref_ticks );
21236406Ssklower 		ENDDEBUG
21336406Ssklower 		if( (param->p_class & TP_CLASS_4 ) && (
21436406Ssklower 			  (param->p_dt_ticks < 1) || (param->p_dr_ticks < 1) ||
21536406Ssklower 			  (param->p_x_ticks < 1)	|| (param->p_keepalive_ticks < 1) ||
21636406Ssklower 			  (param->p_sendack_ticks < 1) || (param->p_ref_ticks < 1) ||
21736406Ssklower 			  (param->p_inact_ticks < 1) ) ) {
21836406Ssklower 				error = EINVAL;
21936406Ssklower 				break;
22036406Ssklower 		}
22136406Ssklower 		IFDEBUG(D_SETPARAMS)
22236406Ssklower 			printf("rx_strat 0x%x\n",  param->p_rx_strat );
22336406Ssklower 		ENDDEBUG
22436406Ssklower 		if(param->p_rx_strat >
22536406Ssklower 			( TPRX_USE_CW | TPRX_EACH | TPRX_FASTSTART) ) {
22636406Ssklower 				if(cmd & TP_STRICT) {
22736406Ssklower 					error = EINVAL;
22836406Ssklower 				} else {
22936406Ssklower 					param->p_rx_strat = TPRX_USE_CW;
23036406Ssklower 				}
23136406Ssklower 				break;
23236406Ssklower 		}
23336406Ssklower 		IFDEBUG(D_SETPARAMS)
23436406Ssklower 			printf("ack_strat 0x%x\n",  param->p_ack_strat );
23536406Ssklower 		ENDDEBUG
23636406Ssklower 		if((param->p_ack_strat != 0) && (param->p_ack_strat != 1)) {
23736406Ssklower 			if(cmd & TP_STRICT) {
23836406Ssklower 				error = EINVAL;
23936406Ssklower 			} else {
24036406Ssklower 				param->p_ack_strat = TPACK_WINDOW;
24136406Ssklower 			}
24236406Ssklower 			break;
24336406Ssklower 		}
24436406Ssklower 		if (param->p_tpdusize < TP_MIN_TPDUSIZE) {
24536406Ssklower 			if(cmd & TP_STRICT) {
24636406Ssklower 				error = EINVAL;
24736406Ssklower 			} else {
24836406Ssklower 				param->p_tpdusize = TP_MIN_TPDUSIZE;
24936406Ssklower 			}
25036406Ssklower 			break;
25136406Ssklower 		}
25236406Ssklower 		if (param->p_tpdusize > TP_TPDUSIZE)  {
25336406Ssklower 			if(cmd & TP_STRICT) {
25436406Ssklower 				error = EINVAL;
25536406Ssklower 			} else {
25636406Ssklower 				param->p_tpdusize = TP_TPDUSIZE;
25736406Ssklower 			}
25836406Ssklower 			break;
25936406Ssklower 		}
26036406Ssklower 		break;
26136406Ssklower 	}
26236406Ssklower 
26336406Ssklower 	if ((error==0) && (cmd & TP_FORCE)) {
26451996Ssklower 		long dusize = ((long)param->p_ptpdusize) << 7;
26547282Ssklower 		/* Enforce Negotation rules below */
26636406Ssklower 		tpcb->tp_class = param->p_class;
26747282Ssklower 		if (tpcb->tp_use_checksum || param->p_use_checksum)
26847282Ssklower 			tpcb->tp_use_checksum = 1;
26947282Ssklower 		if (!tpcb->tp_xpd_service || !param->p_xpd_service)
27047282Ssklower 			tpcb->tp_xpd_service = 0;
27147282Ssklower 		if (!tpcb->tp_xtd_format || !param->p_xtd_format)
27247282Ssklower 			tpcb->tp_xtd_format = 0;
27351996Ssklower 		if (dusize) {
27451996Ssklower 			if (tpcb->tp_l_tpdusize > dusize)
27551996Ssklower 				tpcb->tp_l_tpdusize = dusize;
27651996Ssklower 			if (tpcb->tp_ptpdusize == 0 ||
27751996Ssklower 				tpcb->tp_ptpdusize > param->p_ptpdusize)
27851996Ssklower 				tpcb->tp_ptpdusize = param->p_ptpdusize;
27951996Ssklower 		} else {
28051996Ssklower 			if (param->p_tpdusize != 0 &&
28151996Ssklower 				tpcb->tp_tpdusize > param->p_tpdusize)
28251996Ssklower 				tpcb->tp_tpdusize = param->p_tpdusize;
28351996Ssklower 			tpcb->tp_l_tpdusize = 1 << tpcb->tp_tpdusize;
28451996Ssklower 		}
28536406Ssklower 	}
28636406Ssklower done:
28736406Ssklower 
28836406Ssklower 	IFTRACE(D_CONN)
28936406Ssklower 		tptrace(TPPTmisc, "tp_consist returns class xtdfmt cmd",
29036406Ssklower 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
29136406Ssklower 	ENDTRACE
29236406Ssklower 	IFDEBUG(D_CONN)
29336406Ssklower 		printf(
29436406Ssklower 		"tp_consist rtns 0x%x class 0x%x xtd_fmt 0x%x cmd 0x%x\n",
29536406Ssklower 			error, tpcb->tp_class, tpcb->tp_xtd_format, cmd);
29636406Ssklower 	ENDDEBUG
29736406Ssklower 	return error;
29836406Ssklower }
29936406Ssklower 
30036406Ssklower /*
30136406Ssklower  * NAME: 	tp_ctloutput()
30236406Ssklower  *
30336406Ssklower  * CALLED FROM:
30436406Ssklower  * 	[sg]etsockopt(), via so[sg]etopt().
30536406Ssklower  *
30636406Ssklower  * FUNCTION and ARGUMENTS:
30736406Ssklower  * 	Implements the socket options at transport level.
30837536Smckusick  * 	(cmd) is either PRCO_SETOPT or PRCO_GETOPT (see ../sys/protosw.h).
30936406Ssklower  * 	(so) is the socket.
31037536Smckusick  * 	(level) is SOL_TRANSPORT (see ../sys/socket.h)
31136406Ssklower  * 	(optname) is the particular command or option to be set.
31236406Ssklower  * 	(**mp) is an mbuf structure.
31336406Ssklower  *
31436406Ssklower  * RETURN VALUE:
31536406Ssklower  * 	ENOTSOCK if the socket hasn't got an associated tpcb
31636406Ssklower  *  EINVAL if
31736406Ssklower  * 		trying to set window too big
31836406Ssklower  * 		trying to set illegal max tpdu size
31936406Ssklower  * 		trying to set illegal credit fraction
32036406Ssklower  * 		trying to use unknown or unimplemented class of TP
32136406Ssklower  *		structure passed to set timer values is wrong size
32236406Ssklower  *  	illegal combination of command/GET-SET option,
32336406Ssklower  *			e.g., GET w/ TPOPT_CDDATA_CLEAR:
32436406Ssklower  *  EOPNOTSUPP if the level isn't transport, or command is neither GET nor SET
32536406Ssklower  *   or if the transport-specific command is not implemented
32636406Ssklower  *  EISCONN if trying a command that isn't allowed after a connection
32736406Ssklower  *   is established
32836406Ssklower  *  ENOTCONN if trying a command that is allowed only if a connection is
32936406Ssklower  *   established
33036406Ssklower  *  EMSGSIZE if trying to give too much data on connect/disconnect
33136406Ssklower  *
33236406Ssklower  * SIDE EFFECTS:
33336406Ssklower  *
33436406Ssklower  * NOTES:
33536406Ssklower  */
33636406Ssklower ProtoHook
33736406Ssklower tp_ctloutput(cmd, so, level, optname, mp)
33836406Ssklower 	int 			cmd, level, optname;
33936406Ssklower 	struct socket	*so;
34036406Ssklower 	struct mbuf 	**mp;
34136406Ssklower {
34236406Ssklower 	struct		tp_pcb	*tpcb = sototpcb(so);
34336406Ssklower 	int 		s = splnet();
34437469Ssklower 	caddr_t		value;
34537469Ssklower 	unsigned	val_len;
34636406Ssklower 	int			error = 0;
34736406Ssklower 
34836406Ssklower 	IFTRACE(D_REQUEST)
34936406Ssklower 		tptrace(TPPTmisc, "tp_ctloutput cmd so optname mp",
35036406Ssklower 			cmd, so, optname, mp);
35136406Ssklower 	ENDTRACE
35236406Ssklower 	IFDEBUG(D_REQUEST)
35336406Ssklower 		printf(
35436406Ssklower 	"tp_ctloutput so 0x%x cmd 0x%x optname 0x%x, mp 0x%x *mp 0x%x tpcb 0x%x\n",
35536406Ssklower 			so, cmd, optname, mp, mp?*mp:0, tpcb);
35636406Ssklower 	ENDDEBUG
35736406Ssklower 	if( tpcb == (struct tp_pcb *)0 ) {
35836406Ssklower 		error = ENOTSOCK; goto done;
35936406Ssklower 	}
36036406Ssklower 	if(*mp == MNULL) {
36136406Ssklower 		register struct mbuf *m;
36236406Ssklower 
36336406Ssklower 		MGET(m, M_DONTWAIT, TPMT_SONAME); /* does off, type, next */
36436406Ssklower 		if (m == NULL) {
36536406Ssklower 			splx(s);
36636406Ssklower 			return ENOBUFS;
36736406Ssklower 		}
36836406Ssklower 		m->m_len = 0;
36936406Ssklower 		m->m_act = 0;
37036406Ssklower 		*mp = m;
37136406Ssklower 	}
37236406Ssklower 
37336406Ssklower 	/*
37436406Ssklower 	 *	Hook so one can set network options via a tp socket.
37536406Ssklower 	 */
37636406Ssklower 	if ( level == SOL_NETWORK ) {
37736406Ssklower 		if ((tpcb->tp_nlproto == NULL) || (tpcb->tp_npcb == NULL))
37836406Ssklower 			error = ENOTSOCK;
37936406Ssklower 		else if (tpcb->tp_nlproto->nlp_ctloutput == NULL)
38036406Ssklower 			error = EOPNOTSUPP;
38136406Ssklower 		else
38250974Ssklower 			return ((tpcb->tp_nlproto->nlp_ctloutput)(cmd, optname,
38350974Ssklower 				tpcb->tp_npcb, *mp));
38436406Ssklower 		goto done;
38550974Ssklower 	} else if ( level == SOL_SOCKET) {
38650974Ssklower 		if (optname == SO_RCVBUF && cmd == PRCO_SETOPT) {
387*53688Ssklower 			u_long old_credit = tpcb->tp_maxlcredit;
38850974Ssklower 			tp_rsyset(tpcb);
389*53688Ssklower 			if (tpcb->tp_rhiwat != so->so_rcv.sb_hiwat &&
390*53688Ssklower 			    tpcb->tp_state == TP_OPEN &&
391*53688Ssklower 			    (old_credit < tpcb->tp_maxlcredit))
392*53688Ssklower 				tp_emit(AK_TPDU_type, tpcb,
393*53688Ssklower 					tpcb->tp_rcvnxt, 0, MNULL);
394*53688Ssklower 			tpcb->tp_rhiwat = so->so_rcv.sb_hiwat;
39550974Ssklower 		}
39650974Ssklower 		goto done;
39736406Ssklower 	} else if ( level !=  SOL_TRANSPORT ) {
39836406Ssklower 		error = EOPNOTSUPP; goto done;
39936406Ssklower 	}
40036406Ssklower 	if (cmd != PRCO_GETOPT && cmd != PRCO_SETOPT) {
40136406Ssklower 		error = EOPNOTSUPP; goto done;
40236406Ssklower 	}
40336406Ssklower 	if ( so->so_error ) {
40436406Ssklower 		error = so->so_error; goto done;
40536406Ssklower 	}
40636406Ssklower 
40736406Ssklower 	/* The only options allowed after connection is established
40836406Ssklower 	 * are GET (anything) and SET DISC DATA and SET PERF MEAS
40936406Ssklower 	 */
41036406Ssklower 	if ( ((so->so_state & SS_ISCONNECTING)||(so->so_state & SS_ISCONNECTED))
41136406Ssklower 		&&
41236406Ssklower 		(cmd == PRCO_SETOPT  &&
41336406Ssklower 			optname != TPOPT_DISC_DATA &&
41438841Ssklower 			optname != TPOPT_CFRM_DATA &&
41536406Ssklower 			optname != TPOPT_PERF_MEAS &&
41636406Ssklower 			optname != TPOPT_CDDATA_CLEAR ) ) {
41736406Ssklower 		error = EISCONN; goto done;
41836406Ssklower 	}
41936406Ssklower 	/* The only options allowed after disconnection are GET DISC DATA,
42036406Ssklower 	 * and TPOPT_PSTATISTICS
42136406Ssklower 	 * and they're not allowed if the ref timer has gone off, because
42236406Ssklower 	 * the tpcb is gone
42336406Ssklower 	 */
42438841Ssklower 	if ((so->so_state & (SS_ISCONNECTED | SS_ISCONFIRMING)) ==  0) {
42550435Ssklower 		if ( so->so_pcb == (caddr_t)0 ) {
42636406Ssklower 			error = ENOTCONN; goto done;
42736406Ssklower 		}
42836406Ssklower 		if ( (tpcb->tp_state == TP_REFWAIT || tpcb->tp_state == TP_CLOSING) &&
42936406Ssklower 				(optname != TPOPT_DISC_DATA && optname != TPOPT_PSTATISTICS)) {
43036406Ssklower 			error = ENOTCONN; goto done;
43136406Ssklower 		}
43236406Ssklower 	}
43336406Ssklower 
43437469Ssklower 	value = mtod(*mp, caddr_t);  /* it's aligned, don't worry,
43537469Ssklower 								  * but lint complains about it
43637469Ssklower 								  */
43736406Ssklower 	val_len = (*mp)->m_len;
43836406Ssklower 
43936406Ssklower 	switch (optname) {
44036406Ssklower 
44144424Ssklower 	case TPOPT_INTERCEPT:
44250648Ssklower #define INA(t) (((struct inpcb *)(t->tp_npcb))->inp_laddr.s_addr)
44350648Ssklower #define ISOA(t) (((struct isopcb *)(t->tp_npcb))->isop_laddr->siso_addr)
44450648Ssklower 
44550236Ssklower 		if ((so->so_state & SS_PRIV) == 0) {
44650236Ssklower 			error = EPERM;
44750648Ssklower 		} else if (cmd != PRCO_SETOPT || tpcb->tp_state != TP_CLOSED ||
44850648Ssklower 					(tpcb->tp_flags & TPF_GENERAL_ADDR) ||
44950648Ssklower 					tpcb->tp_next == 0)
45044424Ssklower 			error = EINVAL;
45144424Ssklower 		else {
45250648Ssklower 			register struct tp_pcb *t;
45350648Ssklower 			error = EADDRINUSE;
45450648Ssklower 			for (t = tp_listeners; t; t = t->tp_nextlisten)
45550648Ssklower 				if ((t->tp_flags & TPF_GENERAL_ADDR) == 0 &&
45650648Ssklower 						t->tp_domain == tpcb->tp_domain)
45750648Ssklower 					switch (tpcb->tp_domain) {
45850648Ssklower 					default:
45950648Ssklower 						goto done;
46050648Ssklower #ifdef	INET
46150648Ssklower 					case AF_INET:
46250648Ssklower 						if (INA(t) == INA(tpcb))
46350648Ssklower 							goto done;
46450648Ssklower 						continue;
46550648Ssklower #endif
46650648Ssklower #ifdef ISO
46750648Ssklower 					case AF_ISO:
46850648Ssklower 						if (bcmp(ISOA(t).isoa_genaddr, ISOA(tpcb).isoa_genaddr,
46950648Ssklower 										ISOA(t).isoa_len) == 0)
47050648Ssklower 							goto done;
47150648Ssklower 						continue;
47250648Ssklower #endif
47350648Ssklower 					}
47450648Ssklower 			tpcb->tp_lsuffixlen = 0;
47550648Ssklower 			tpcb->tp_state = TP_LISTENING;
47650648Ssklower 			error = 0;
47750648Ssklower 			remque(tpcb);
47850648Ssklower 			tpcb->tp_next = tpcb->tp_prev = tpcb;
47950648Ssklower 			tpcb->tp_nextlisten = tp_listeners;
48050648Ssklower 			tp_listeners = tpcb;
48144424Ssklower 		}
48244424Ssklower 		break;
48344424Ssklower 
48436406Ssklower 	case TPOPT_MY_TSEL:
48536406Ssklower 		if ( cmd == PRCO_GETOPT ) {
48636406Ssklower 			ASSERT( tpcb->tp_lsuffixlen <= MAX_TSAP_SEL_LEN );
48737469Ssklower 			bcopy((caddr_t)tpcb->tp_lsuffix, value, tpcb->tp_lsuffixlen);
48836406Ssklower 			(*mp)->m_len = tpcb->tp_lsuffixlen;
48936406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
49036406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
49136406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
49236406Ssklower 				error = EINVAL;
49336406Ssklower 			} else {
49437469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_lsuffix, val_len);
49536406Ssklower 				tpcb->tp_lsuffixlen = val_len;
49636406Ssklower 			}
49736406Ssklower 		}
49836406Ssklower 		break;
49936406Ssklower 
50036406Ssklower 	case TPOPT_PEER_TSEL:
50136406Ssklower 		if ( cmd == PRCO_GETOPT ) {
50236406Ssklower 			ASSERT( tpcb->tp_fsuffixlen <= MAX_TSAP_SEL_LEN );
50337469Ssklower 			bcopy((caddr_t)tpcb->tp_fsuffix, value, tpcb->tp_fsuffixlen);
50436406Ssklower 			(*mp)->m_len = tpcb->tp_fsuffixlen;
50536406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
50636406Ssklower 			if( (val_len > MAX_TSAP_SEL_LEN) || (val_len <= 0 )) {
50736406Ssklower 				printf("val_len 0x%x (*mp)->m_len 0x%x\n", val_len, (*mp));
50836406Ssklower 				error = EINVAL;
50936406Ssklower 			} else {
51037469Ssklower 				bcopy(value, (caddr_t)tpcb->tp_fsuffix, val_len);
51136406Ssklower 				tpcb->tp_fsuffixlen = val_len;
51236406Ssklower 			}
51336406Ssklower 		}
51436406Ssklower 		break;
51536406Ssklower 
51636406Ssklower 	case TPOPT_FLAGS:
51736406Ssklower 		IFDEBUG(D_REQUEST)
51836406Ssklower 			printf("%s TPOPT_FLAGS value 0x%x *value 0x%x, flags 0x%x \n",
51936406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET",
52036406Ssklower 				value,
52136406Ssklower 				*value,
52236406Ssklower 				tpcb->tp_flags);
52336406Ssklower 		ENDDEBUG
52436406Ssklower 
52536406Ssklower 		if ( cmd == PRCO_GETOPT ) {
52636406Ssklower 			*(int *)value = (int)tpcb->tp_flags;
52736406Ssklower 			(*mp)->m_len = sizeof(u_int);
52836406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
52936406Ssklower 			error = EINVAL; goto done;
53036406Ssklower 		}
53136406Ssklower 		break;
53236406Ssklower 
53336406Ssklower 	case TPOPT_PARAMS:
53436406Ssklower 		/* This handles:
53536406Ssklower 		 * timer values,
53636406Ssklower 		 * class, use of transport expedited data,
53736406Ssklower 		 * max tpdu size, checksum, xtd format and
53836406Ssklower 		 * disconnect indications, and may get rid of connect/disc data
53936406Ssklower 		 */
54036406Ssklower 		IFDEBUG(D_SETPARAMS)
54136406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
54236406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
54336406Ssklower 		ENDDEBUG
54436406Ssklower 		IFDEBUG(D_REQUEST)
54536406Ssklower 			printf("TPOPT_PARAMS value 0x%x, cmd %s \n", value,
54636406Ssklower 				cmd==PRCO_GETOPT?"GET":"SET");
54736406Ssklower 		ENDDEBUG
54836406Ssklower 
54936406Ssklower 		if ( cmd == PRCO_GETOPT ) {
55036406Ssklower 			*(struct tp_conn_param *)value = tpcb->_tp_param;
55136406Ssklower 			(*mp)->m_len = sizeof(tpcb->_tp_param);
55236406Ssklower 		} else /* cmd == PRCO_SETOPT  */ {
55336406Ssklower 			if( (error =
55436406Ssklower 				tp_consistency(tpcb, TP_STRICT | TP_FORCE,
55536406Ssklower 								(struct tp_conn_param *)value))==0) {
55636406Ssklower 				/*
55736406Ssklower 				 * tp_consistency doesn't copy the whole set of params
55836406Ssklower 				 */
55936406Ssklower 				tpcb->_tp_param = *(struct tp_conn_param *)value;
56036406Ssklower 				(*mp)->m_len = sizeof(tpcb->_tp_param);
56136406Ssklower 			}
56236406Ssklower 		}
56336406Ssklower 		break;
56436406Ssklower 
56536406Ssklower 	case TPOPT_PSTATISTICS:
56636406Ssklower #ifdef TP_PERF_MEAS
56736406Ssklower 		if (cmd == PRCO_SETOPT) {
56836406Ssklower 			error = EINVAL; goto done;
56936406Ssklower 		}
57036406Ssklower 		IFPERF(tpcb)
57137469Ssklower 			if (*mp) {
57237469Ssklower 				struct mbuf * n;
57337469Ssklower 				do {
57437469Ssklower 					MFREE(*mp, n);
57537469Ssklower 					*mp = n;
57637469Ssklower 				} while (n);
57737469Ssklower 			}
57837469Ssklower 			*mp = m_copym(tpcb->tp_p_mbuf, (int)M_COPYALL, M_WAITOK);
57936406Ssklower 		ENDPERF
58036406Ssklower 		else {
58136406Ssklower 			error = EINVAL; goto done;
58236406Ssklower 		}
58336406Ssklower 		break;
58436406Ssklower #else
58536406Ssklower 		error = EOPNOTSUPP;
58636406Ssklower 		goto done;
58736406Ssklower #endif TP_PERF_MEAS
58836406Ssklower 
58936406Ssklower 	case TPOPT_CDDATA_CLEAR:
59036406Ssklower 		if (cmd == PRCO_GETOPT) {
59136406Ssklower 			error = EINVAL;
59236406Ssklower 		} else {
59337469Ssklower 			if (tpcb->tp_ucddata) {
59438841Ssklower 				m_freem(tpcb->tp_ucddata);
59538841Ssklower 				tpcb->tp_ucddata = 0;
59636406Ssklower 			}
59736406Ssklower 		}
59836406Ssklower 		break;
59936406Ssklower 
60038841Ssklower 	case TPOPT_CFRM_DATA:
60136406Ssklower 	case TPOPT_DISC_DATA:
60236406Ssklower 	case TPOPT_CONN_DATA:
60336406Ssklower 		if( tpcb->tp_class == TP_CLASS_0 ) {
60436406Ssklower 			error = EOPNOTSUPP;
60536406Ssklower 			break;
60636406Ssklower 		}
60736406Ssklower 		IFDEBUG(D_REQUEST)
60836406Ssklower 			printf("%s\n", optname==TPOPT_DISC_DATA?"DISC data":"CONN data");
60936406Ssklower 			printf("m_len 0x%x, vallen 0x%x so_snd.cc 0x%x\n",
61036406Ssklower 				(*mp)->m_len, val_len, so->so_snd.sb_cc);
61136406Ssklower 			dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput: sosnd ");
61236406Ssklower 		ENDDEBUG
61336406Ssklower 		if (cmd == PRCO_SETOPT) {
61437469Ssklower 			int len = tpcb->tp_ucddata ?  tpcb->tp_ucddata->m_len : 0;
61536406Ssklower 			/* can append connect data in several calls */
61637469Ssklower 			if (len + val_len >
61736406Ssklower 				(optname==TPOPT_CONN_DATA?TP_MAX_CR_DATA:TP_MAX_DR_DATA) ) {
61836406Ssklower 				error = EMSGSIZE; goto done;
61936406Ssklower 			}
62036406Ssklower 			(*mp)->m_next = MNULL;
62136406Ssklower 			(*mp)->m_act = 0;
62237469Ssklower 			if (tpcb->tp_ucddata)
62337469Ssklower 				m_cat(tpcb->tp_ucddata, *mp);
62437469Ssklower 			else
62537469Ssklower 				tpcb->tp_ucddata = *mp;
62640241Ssklower 			IFDEBUG(D_REQUEST)
62740241Ssklower 				dump_mbuf(tpcb->tp_ucddata, "tp_ctloutput after CONN_DATA");
62840241Ssklower 			ENDDEBUG
62936406Ssklower 			IFTRACE(D_REQUEST)
63036406Ssklower 				tptrace(TPPTmisc,"C/D DATA: flags snd.sbcc val_len",
63136406Ssklower 					tpcb->tp_flags, so->so_snd.sb_cc,val_len,0);
63236406Ssklower 			ENDTRACE
63350974Ssklower 			*mp = MNULL;
63440241Ssklower 			if (optname == TPOPT_CFRM_DATA && (so->so_state & SS_ISCONFIRMING))
63540241Ssklower 				(void) tp_confirm(tpcb);
63636406Ssklower 		}
63736406Ssklower 		break;
63836406Ssklower 
63936406Ssklower 	case TPOPT_PERF_MEAS:
64036406Ssklower #ifdef TP_PERF_MEAS
64136406Ssklower 		if (cmd == PRCO_GETOPT) {
64236406Ssklower 			*value = (u_int)tpcb->tp_perf_on;
64336406Ssklower 			(*mp)->m_len = sizeof(u_int);
64436406Ssklower 		} else if (cmd == PRCO_SETOPT) {
64536406Ssklower 			(*mp)->m_len = 0;
64636406Ssklower 			if ((*value) != 0 && (*value) != 1 )
64736406Ssklower 				error = EINVAL;
64836406Ssklower 			else  tpcb->tp_perf_on = (*value);
64936406Ssklower 		}
65036406Ssklower 		if( tpcb->tp_perf_on )
65136406Ssklower 			error = tp_setup_perf(tpcb);
65236406Ssklower #else  TP_PERF_MEAS
65336406Ssklower 		error = EOPNOTSUPP;
65436406Ssklower #endif TP_PERF_MEAS
65536406Ssklower 		break;
65636406Ssklower 
65736406Ssklower 	default:
65836406Ssklower 		error = EOPNOTSUPP;
65936406Ssklower 	}
66036406Ssklower 
66136406Ssklower done:
66236406Ssklower 	IFDEBUG(D_REQUEST)
66336406Ssklower 		dump_mbuf(so->so_snd.sb_mb, "tp_ctloutput sosnd at end");
66436406Ssklower 		dump_mbuf(*mp, "tp_ctloutput *mp");
66536406Ssklower 	ENDDEBUG
66636406Ssklower 	/*
66736406Ssklower 	 * sigh: getsockopt looks only at m_len : all output data must
66836406Ssklower 	 * reside in the first mbuf
66936406Ssklower 	 */
67050974Ssklower 	if (*mp) {
671*53688Ssklower 		if (cmd == PRCO_SETOPT) {
67250974Ssklower 			m_freem(*mp);
673*53688Ssklower 			*mp = MNULL;
674*53688Ssklower 		} else {
67550974Ssklower 			ASSERT ( m_compress(*mp, mp) <= MLEN );
67650974Ssklower 			if (error)
67750974Ssklower 				(*mp)->m_len = 0;
67850974Ssklower 			IFDEBUG(D_REQUEST)
67950974Ssklower 				dump_mbuf(*mp, "tp_ctloutput *mp after compress");
68050974Ssklower 			ENDDEBUG
68150974Ssklower 		}
68236406Ssklower 	}
68336406Ssklower 	splx(s);
68436406Ssklower 	return error;
68536406Ssklower }
686