xref: /illumos-gate/usr/src/uts/common/inet/optcom.c (revision 7c478bd9)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
27*7c478bd9Sstevel@tonic-gate 
28*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
29*7c478bd9Sstevel@tonic-gate 
30*7c478bd9Sstevel@tonic-gate /*
31*7c478bd9Sstevel@tonic-gate  * This file contains common code for handling Options Management requests.
32*7c478bd9Sstevel@tonic-gate  */
33*7c478bd9Sstevel@tonic-gate 
34*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/stream.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
40*7c478bd9Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
41*7c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/timod.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/socket.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>		/* for ASSERT */
47*7c478bd9Sstevel@tonic-gate #include <sys/policy.h>
48*7c478bd9Sstevel@tonic-gate 
49*7c478bd9Sstevel@tonic-gate #include <inet/common.h>
50*7c478bd9Sstevel@tonic-gate #include <inet/mi.h>
51*7c478bd9Sstevel@tonic-gate #include <inet/nd.h>
52*7c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
53*7c478bd9Sstevel@tonic-gate #include <inet/ip.h>
54*7c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
55*7c478bd9Sstevel@tonic-gate #include <netinet/in.h>
56*7c478bd9Sstevel@tonic-gate #include <netinet/tcp.h>
57*7c478bd9Sstevel@tonic-gate #include <netinet/ip_mroute.h>
58*7c478bd9Sstevel@tonic-gate #include "optcom.h"
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate #include <inet/optcom.h>
61*7c478bd9Sstevel@tonic-gate 
62*7c478bd9Sstevel@tonic-gate /*
63*7c478bd9Sstevel@tonic-gate  * Function prototypes
64*7c478bd9Sstevel@tonic-gate  */
65*7c478bd9Sstevel@tonic-gate static t_scalar_t process_topthdrs_first_pass(mblk_t *, cred_t *, optdb_obj_t *,
66*7c478bd9Sstevel@tonic-gate     boolean_t *, size_t *);
67*7c478bd9Sstevel@tonic-gate static t_scalar_t do_options_second_pass(queue_t *q, mblk_t *reqmp,
68*7c478bd9Sstevel@tonic-gate     mblk_t *ack_mp, cred_t *, optdb_obj_t *dbobjp,
69*7c478bd9Sstevel@tonic-gate     mblk_t *first_mp, boolean_t is_restart, boolean_t *queued_statusp);
70*7c478bd9Sstevel@tonic-gate static t_uscalar_t get_worst_status(t_uscalar_t, t_uscalar_t);
71*7c478bd9Sstevel@tonic-gate static int do_opt_default(queue_t *, struct T_opthdr *, uchar_t **,
72*7c478bd9Sstevel@tonic-gate     t_uscalar_t *, cred_t *, optdb_obj_t *);
73*7c478bd9Sstevel@tonic-gate static void do_opt_current(queue_t *, struct T_opthdr *, uchar_t **,
74*7c478bd9Sstevel@tonic-gate     t_uscalar_t *, cred_t *cr, optdb_obj_t *);
75*7c478bd9Sstevel@tonic-gate static int do_opt_check_or_negotiate(queue_t *q, struct T_opthdr *reqopt,
76*7c478bd9Sstevel@tonic-gate     uint_t optset_context, uchar_t **resptrp, t_uscalar_t *worst_statusp,
77*7c478bd9Sstevel@tonic-gate     cred_t *, optdb_obj_t *dbobjp, mblk_t *first_mp);
78*7c478bd9Sstevel@tonic-gate static opdes_t *opt_chk_lookup(t_uscalar_t, t_uscalar_t, opdes_t *, uint_t);
79*7c478bd9Sstevel@tonic-gate static boolean_t opt_level_valid(t_uscalar_t, optlevel_t *, uint_t);
80*7c478bd9Sstevel@tonic-gate static size_t opt_level_allopts_lengths(t_uscalar_t, opdes_t *, uint_t);
81*7c478bd9Sstevel@tonic-gate static boolean_t opt_length_ok(opdes_t *, struct T_opthdr *);
82*7c478bd9Sstevel@tonic-gate static t_uscalar_t optcom_max_optbuf_len(opdes_t *, uint_t);
83*7c478bd9Sstevel@tonic-gate static boolean_t opt_bloated_maxsize(opdes_t *);
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate extern optdb_obj_t tcp_opt_obj;
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate /* Common code for sending back a T_ERROR_ACK. */
88*7c478bd9Sstevel@tonic-gate void
89*7c478bd9Sstevel@tonic-gate optcom_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error, int sys_error)
90*7c478bd9Sstevel@tonic-gate {
91*7c478bd9Sstevel@tonic-gate 	if ((mp = mi_tpi_err_ack_alloc(mp, t_error, sys_error)) != NULL)
92*7c478bd9Sstevel@tonic-gate 		qreply(q, mp);
93*7c478bd9Sstevel@tonic-gate }
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate /*
96*7c478bd9Sstevel@tonic-gate  * The option management routines svr4_optcom_req() and tpi_optcom_req() use
97*7c478bd9Sstevel@tonic-gate  * callback functions as arguments. Here is the expected interfaces
98*7c478bd9Sstevel@tonic-gate  * assumed from the callback functions
99*7c478bd9Sstevel@tonic-gate  *
100*7c478bd9Sstevel@tonic-gate  *
101*7c478bd9Sstevel@tonic-gate  * (1) deffn(q, optlevel, optname, optvalp)
102*7c478bd9Sstevel@tonic-gate  *
103*7c478bd9Sstevel@tonic-gate  *	- Function only called when default value comes from protocol
104*7c478bd9Sstevel@tonic-gate  *	 specific code and not the option database table (indicated by
105*7c478bd9Sstevel@tonic-gate  *	  OP_DEF_FN property in option database.)
106*7c478bd9Sstevel@tonic-gate  *	- Error return is -1. Valid returns are >=0.
107*7c478bd9Sstevel@tonic-gate  *	- When valid, the return value represents the length used for storing
108*7c478bd9Sstevel@tonic-gate  *		the default value of the option.
109*7c478bd9Sstevel@tonic-gate  *      - Error return implies the called routine did not recognize this
110*7c478bd9Sstevel@tonic-gate  *              option. Something downstream could so input is left unchanged
111*7c478bd9Sstevel@tonic-gate  *              in request buffer.
112*7c478bd9Sstevel@tonic-gate  *
113*7c478bd9Sstevel@tonic-gate  * (2) getfn(q, optlevel, optname, optvalp)
114*7c478bd9Sstevel@tonic-gate  *
115*7c478bd9Sstevel@tonic-gate  *	- Error return is -1. Valid returns are >=0.
116*7c478bd9Sstevel@tonic-gate  *	- When valid, the return value represents the length used for storing
117*7c478bd9Sstevel@tonic-gate  *		the actual value of the option.
118*7c478bd9Sstevel@tonic-gate  *      - Error return implies the called routine did not recognize this
119*7c478bd9Sstevel@tonic-gate  *              option. Something downstream could so input is left unchanged
120*7c478bd9Sstevel@tonic-gate  *              in request buffer.
121*7c478bd9Sstevel@tonic-gate  *
122*7c478bd9Sstevel@tonic-gate  * (3) setfn(q, optset_context, optlevel, optname, inlen, invalp,
123*7c478bd9Sstevel@tonic-gate  *	outlenp, outvalp, attrp, cr);
124*7c478bd9Sstevel@tonic-gate  *
125*7c478bd9Sstevel@tonic-gate  *	- OK return is 0, Error code is returned as a non-zero argument.
126*7c478bd9Sstevel@tonic-gate  *      - If negative it is ignored by svr4_optcom_req(). If positive, error
127*7c478bd9Sstevel@tonic-gate  *        is returned. A negative return implies that option, while handled on
128*7c478bd9Sstevel@tonic-gate  *	  this stack is not handled at this level and will be handled further
129*7c478bd9Sstevel@tonic-gate  *	  downstream.
130*7c478bd9Sstevel@tonic-gate  *	- Both negative and positive errors are treats as errors in an
131*7c478bd9Sstevel@tonic-gate  *	  identical manner by tpi_optcom_req(). The errors affect "status"
132*7c478bd9Sstevel@tonic-gate  *	  field of each option's T_opthdr. If sucessfull, an appropriate sucess
133*7c478bd9Sstevel@tonic-gate  *	  result is carried. If error, it instantiated to "failure" at the
134*7c478bd9Sstevel@tonic-gate  *	  topmost level and left unchanged at other levels. (This "failure" can
135*7c478bd9Sstevel@tonic-gate  *	  turn to a success at another level).
136*7c478bd9Sstevel@tonic-gate  *	- optset_context passed for tpi_optcom_req(). It is interpreted as:
137*7c478bd9Sstevel@tonic-gate  *        - SETFN_OPTCOM_CHECKONLY
138*7c478bd9Sstevel@tonic-gate  *		semantics are to pretend to set the value and report
139*7c478bd9Sstevel@tonic-gate  *		back if it would be successful.
140*7c478bd9Sstevel@tonic-gate  *		This is used with T_CHECK semantics in XTI
141*7c478bd9Sstevel@tonic-gate  *        - SETFN_OPTCOM_NEGOTIATE
142*7c478bd9Sstevel@tonic-gate  *		set the value. Call from option management primitive
143*7c478bd9Sstevel@tonic-gate  *		T_OPTMGMT_REQ when T_NEGOTIATE flags is used.
144*7c478bd9Sstevel@tonic-gate  *	  - SETFN_UD_NEGOTIATE
145*7c478bd9Sstevel@tonic-gate  *		option request came riding on UNITDATA primitive most often
146*7c478bd9Sstevel@tonic-gate  *		has  "this datagram" semantics to influence prpoerties
147*7c478bd9Sstevel@tonic-gate  *		affecting an outgoig datagram or associated with recived
148*7c478bd9Sstevel@tonic-gate  *		datagram
149*7c478bd9Sstevel@tonic-gate  *		[ Note: XTI permits this use outside of "this datagram"
150*7c478bd9Sstevel@tonic-gate  *		semantics also and permits setting "management related"
151*7c478bd9Sstevel@tonic-gate  *		options in this	context and its test suite enforces it ]
152*7c478bd9Sstevel@tonic-gate  *	  - SETFN_CONN_NEGOTATE
153*7c478bd9Sstevel@tonic-gate  *		option request came riding on CONN_REQ/RES primitive and
154*7c478bd9Sstevel@tonic-gate  *		most often has "this connection" (negotiation during
155*7c478bd9Sstevel@tonic-gate  *		"connection estblishment") semantics.
156*7c478bd9Sstevel@tonic-gate  *		[ Note: XTI permits use of these outside of "this connection"
157*7c478bd9Sstevel@tonic-gate  *		semantics and permits "management related" options in this
158*7c478bd9Sstevel@tonic-gate  *		context and its test suite enforces it. ]
159*7c478bd9Sstevel@tonic-gate  *
160*7c478bd9Sstevel@tonic-gate  *	- inlen, invalp is the option length,value requested to be set.
161*7c478bd9Sstevel@tonic-gate  *	- outlenp, outvalp represent return parameters which contain the
162*7c478bd9Sstevel@tonic-gate  *	  value set and it might be different from one passed on input.
163*7c478bd9Sstevel@tonic-gate  *	- attrp points to a data structure that's used by v6 modules to
164*7c478bd9Sstevel@tonic-gate  *	  store ancillary data options or sticky options.
165*7c478bd9Sstevel@tonic-gate  *	- cr points to the caller's credentials
166*7c478bd9Sstevel@tonic-gate  *	- the caller might pass same buffers for input and output and the
167*7c478bd9Sstevel@tonic-gate  *	  routine should protect against this case by not updating output
168*7c478bd9Sstevel@tonic-gate  *	  buffers until it is done referencing input buffers and any other
169*7c478bd9Sstevel@tonic-gate  *	  issues (e.g. not use bcopy() if we do not trust what it does).
170*7c478bd9Sstevel@tonic-gate  *      - If option is not known, it returns error. We randomly pick EINVAL.
171*7c478bd9Sstevel@tonic-gate  *        It can however get called with options that are handled downstream
172*7c478bd9Sstevel@tonic-gate  *        opr upstream so for svr4_optcom_req(), it does not return error for
173*7c478bd9Sstevel@tonic-gate  *        negative return values.
174*7c478bd9Sstevel@tonic-gate  *
175*7c478bd9Sstevel@tonic-gate  */
176*7c478bd9Sstevel@tonic-gate 
177*7c478bd9Sstevel@tonic-gate /*
178*7c478bd9Sstevel@tonic-gate  * Upper Level Protocols call this routine when they receive
179*7c478bd9Sstevel@tonic-gate  * a T_SVR4_OPTMGMT_REQ message.  They supply callback functions
180*7c478bd9Sstevel@tonic-gate  * for setting a new value for a single options, getting the
181*7c478bd9Sstevel@tonic-gate  * current value for a single option, and checking for support
182*7c478bd9Sstevel@tonic-gate  * of a single option.  svr4_optcom_req validates the option management
183*7c478bd9Sstevel@tonic-gate  * buffer passed in, and calls the appropriate routines to do the
184*7c478bd9Sstevel@tonic-gate  * job requested.
185*7c478bd9Sstevel@tonic-gate  * XXX Code below needs some restructuring after we have some more
186*7c478bd9Sstevel@tonic-gate  * macros to support 'struct opthdr' in the headers.
187*7c478bd9Sstevel@tonic-gate  *
188*7c478bd9Sstevel@tonic-gate  * IP-MT notes: The option management framework functions svr4_optcom_req() and
189*7c478bd9Sstevel@tonic-gate  * tpi_optcom_req() allocate and prepend an M_CTL mblk to the actual
190*7c478bd9Sstevel@tonic-gate  * T_optmgmt_req mblk and pass the chain as an additional parameter to the
191*7c478bd9Sstevel@tonic-gate  * protocol set functions. If a protocol set function (such as ip_opt_set)
192*7c478bd9Sstevel@tonic-gate  * cannot process the option immediately it can return EINPROGRESS. ip_opt_set
193*7c478bd9Sstevel@tonic-gate  * enqueues the message in the appropriate sq and returns EINPROGRESS. Later
194*7c478bd9Sstevel@tonic-gate  * the sq framework arranges to restart this operation and passes control to
195*7c478bd9Sstevel@tonic-gate  * the restart function ip_restart_optmgmt() which in turn calls
196*7c478bd9Sstevel@tonic-gate  * svr4_optcom_req() or tpi_optcom_req() to restart the option processing.
197*7c478bd9Sstevel@tonic-gate  */
198*7c478bd9Sstevel@tonic-gate int
199*7c478bd9Sstevel@tonic-gate svr4_optcom_req(queue_t *q, mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp)
200*7c478bd9Sstevel@tonic-gate {
201*7c478bd9Sstevel@tonic-gate 	pfi_t	deffn = dbobjp->odb_deffn;
202*7c478bd9Sstevel@tonic-gate 	pfi_t	getfn = dbobjp->odb_getfn;
203*7c478bd9Sstevel@tonic-gate 	opt_set_fn setfn = dbobjp->odb_setfn;
204*7c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
205*7c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
206*7c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
207*7c478bd9Sstevel@tonic-gate 	opt_restart_t *or;
208*7c478bd9Sstevel@tonic-gate 	struct opthdr *restart_opt;
209*7c478bd9Sstevel@tonic-gate 	boolean_t is_restart = B_FALSE;
210*7c478bd9Sstevel@tonic-gate 	mblk_t	*first_mp;
211*7c478bd9Sstevel@tonic-gate 
212*7c478bd9Sstevel@tonic-gate 	t_uscalar_t max_optbuf_len;
213*7c478bd9Sstevel@tonic-gate 	int len;
214*7c478bd9Sstevel@tonic-gate 	mblk_t	*mp1 = NULL;
215*7c478bd9Sstevel@tonic-gate 	struct opthdr *next_opt;
216*7c478bd9Sstevel@tonic-gate 	struct opthdr *opt;
217*7c478bd9Sstevel@tonic-gate 	struct opthdr *opt1;
218*7c478bd9Sstevel@tonic-gate 	struct opthdr *opt_end;
219*7c478bd9Sstevel@tonic-gate 	struct opthdr *opt_start;
220*7c478bd9Sstevel@tonic-gate 	opdes_t	*optd;
221*7c478bd9Sstevel@tonic-gate 	boolean_t	pass_to_next = B_FALSE;
222*7c478bd9Sstevel@tonic-gate 	boolean_t	pass_to_ip = B_FALSE;
223*7c478bd9Sstevel@tonic-gate 	struct T_optmgmt_ack *toa;
224*7c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor;
225*7c478bd9Sstevel@tonic-gate 
226*7c478bd9Sstevel@tonic-gate 	/*
227*7c478bd9Sstevel@tonic-gate 	 * Allocate M_CTL and prepend to the packet for restarting this
228*7c478bd9Sstevel@tonic-gate 	 * option if needed. IP may need to queue and restart the option
229*7c478bd9Sstevel@tonic-gate 	 * if it cannot obtain exclusive conditions immediately. Please see
230*7c478bd9Sstevel@tonic-gate 	 * IP-MT notes before the start of svr4_optcom_req
231*7c478bd9Sstevel@tonic-gate 	 */
232*7c478bd9Sstevel@tonic-gate 	if (mp->b_datap->db_type == M_CTL) {
233*7c478bd9Sstevel@tonic-gate 		is_restart = B_TRUE;
234*7c478bd9Sstevel@tonic-gate 		first_mp = mp;
235*7c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
236*7c478bd9Sstevel@tonic-gate 		ASSERT(mp->b_wptr - mp->b_rptr >=
237*7c478bd9Sstevel@tonic-gate 		    sizeof (struct T_optmgmt_req));
238*7c478bd9Sstevel@tonic-gate 		tor = (struct T_optmgmt_req *)mp->b_rptr;
239*7c478bd9Sstevel@tonic-gate 		ASSERT(tor->MGMT_flags == T_NEGOTIATE);
240*7c478bd9Sstevel@tonic-gate 
241*7c478bd9Sstevel@tonic-gate 		or = (opt_restart_t *)first_mp->b_rptr;
242*7c478bd9Sstevel@tonic-gate 		opt_start = or->or_start;
243*7c478bd9Sstevel@tonic-gate 		opt_end = or->or_end;
244*7c478bd9Sstevel@tonic-gate 		restart_opt = or->or_ropt;
245*7c478bd9Sstevel@tonic-gate 		goto restart;
246*7c478bd9Sstevel@tonic-gate 	}
247*7c478bd9Sstevel@tonic-gate 
248*7c478bd9Sstevel@tonic-gate 	tor = (struct T_optmgmt_req *)mp->b_rptr;
249*7c478bd9Sstevel@tonic-gate 	/* Verify message integrity. */
250*7c478bd9Sstevel@tonic-gate 	if (mp->b_wptr - mp->b_rptr < sizeof (struct T_optmgmt_req))
251*7c478bd9Sstevel@tonic-gate 		goto bad_opt;
252*7c478bd9Sstevel@tonic-gate 	/* Verify MGMT_flags legal */
253*7c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
254*7c478bd9Sstevel@tonic-gate 	case T_DEFAULT:
255*7c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
256*7c478bd9Sstevel@tonic-gate 	case T_CURRENT:
257*7c478bd9Sstevel@tonic-gate 	case T_CHECK:
258*7c478bd9Sstevel@tonic-gate 		/* OK - legal request flags */
259*7c478bd9Sstevel@tonic-gate 		break;
260*7c478bd9Sstevel@tonic-gate 	default:
261*7c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
262*7c478bd9Sstevel@tonic-gate 		return (0);
263*7c478bd9Sstevel@tonic-gate 	}
264*7c478bd9Sstevel@tonic-gate 	if (tor->MGMT_flags == T_DEFAULT) {
265*7c478bd9Sstevel@tonic-gate 		/* Is it a request for default option settings? */
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 		/*
268*7c478bd9Sstevel@tonic-gate 		 * Note: XXX TLI and TPI specification was unclear about
269*7c478bd9Sstevel@tonic-gate 		 * semantics of T_DEFAULT and the following historical note
270*7c478bd9Sstevel@tonic-gate 		 * and its interpretation is incorrect (it implies a request
271*7c478bd9Sstevel@tonic-gate 		 * for default values of only the identified options not all.
272*7c478bd9Sstevel@tonic-gate 		 * The semantics have been explained better in XTI spec.)
273*7c478bd9Sstevel@tonic-gate 		 * However, we do not modify (comment or code) here to keep
274*7c478bd9Sstevel@tonic-gate 		 * compatibility.
275*7c478bd9Sstevel@tonic-gate 		 * We can rethink this if it ever becomes an issue.
276*7c478bd9Sstevel@tonic-gate 		 * ----historical comment start------
277*7c478bd9Sstevel@tonic-gate 		 * As we understand it, the input buffer is meaningless
278*7c478bd9Sstevel@tonic-gate 		 * so we ditch the message.  A T_DEFAULT request is a
279*7c478bd9Sstevel@tonic-gate 		 * request to obtain a buffer containing defaults for
280*7c478bd9Sstevel@tonic-gate 		 * all supported options, so we allocate a maximum length
281*7c478bd9Sstevel@tonic-gate 		 * reply.
282*7c478bd9Sstevel@tonic-gate 		 * ----historical comment end -------
283*7c478bd9Sstevel@tonic-gate 		 */
284*7c478bd9Sstevel@tonic-gate 		/* T_DEFAULT not passed down */
285*7c478bd9Sstevel@tonic-gate 		ASSERT(topmost_tpiprovider == B_TRUE);
286*7c478bd9Sstevel@tonic-gate 		freemsg(mp);
287*7c478bd9Sstevel@tonic-gate 		max_optbuf_len = optcom_max_optbuf_len(opt_arr,
288*7c478bd9Sstevel@tonic-gate 		    opt_arr_cnt);
289*7c478bd9Sstevel@tonic-gate 		mp = allocb(max_optbuf_len, BPRI_MED);
290*7c478bd9Sstevel@tonic-gate 		if (!mp) {
291*7c478bd9Sstevel@tonic-gate no_mem:;
292*7c478bd9Sstevel@tonic-gate 			optcom_err_ack(q, mp, TSYSERR, ENOMEM);
293*7c478bd9Sstevel@tonic-gate 			return (0);
294*7c478bd9Sstevel@tonic-gate 		}
295*7c478bd9Sstevel@tonic-gate 
296*7c478bd9Sstevel@tonic-gate 		/* Initialize the T_optmgmt_ack header. */
297*7c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)mp->b_rptr;
298*7c478bd9Sstevel@tonic-gate 		bzero((char *)toa, max_optbuf_len);
299*7c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_ACK;
300*7c478bd9Sstevel@tonic-gate 		toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
301*7c478bd9Sstevel@tonic-gate 		/* TODO: Is T_DEFAULT the right thing to put in MGMT_flags? */
302*7c478bd9Sstevel@tonic-gate 		toa->MGMT_flags = T_DEFAULT;
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 		/* Now walk the table of options passed in */
305*7c478bd9Sstevel@tonic-gate 		opt = (struct opthdr *)&toa[1];
306*7c478bd9Sstevel@tonic-gate 		for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
307*7c478bd9Sstevel@tonic-gate 			/*
308*7c478bd9Sstevel@tonic-gate 			 * All the options in the table of options passed
309*7c478bd9Sstevel@tonic-gate 			 * in are by definition supported by the protocol
310*7c478bd9Sstevel@tonic-gate 			 * calling this function.
311*7c478bd9Sstevel@tonic-gate 			 */
312*7c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr))
313*7c478bd9Sstevel@tonic-gate 				continue;
314*7c478bd9Sstevel@tonic-gate 			opt->level = optd->opdes_level;
315*7c478bd9Sstevel@tonic-gate 			opt->name = optd->opdes_name;
316*7c478bd9Sstevel@tonic-gate 			if (!(optd->opdes_props & OP_DEF_FN) ||
317*7c478bd9Sstevel@tonic-gate 			    ((len = (*deffn)(q, opt->level,
318*7c478bd9Sstevel@tonic-gate 				opt->name, (uchar_t *)&opt[1])) < 0)) {
319*7c478bd9Sstevel@tonic-gate 				/*
320*7c478bd9Sstevel@tonic-gate 				 * Fill length and value from table.
321*7c478bd9Sstevel@tonic-gate 				 *
322*7c478bd9Sstevel@tonic-gate 				 * Default value not instantiated from function
323*7c478bd9Sstevel@tonic-gate 				 * (or the protocol specific function failed it;
324*7c478bd9Sstevel@tonic-gate 				 * In this interpretation of T_DEFAULT, this is
325*7c478bd9Sstevel@tonic-gate 				 * the best we can do)
326*7c478bd9Sstevel@tonic-gate 				 */
327*7c478bd9Sstevel@tonic-gate 				switch (optd->opdes_size) {
328*7c478bd9Sstevel@tonic-gate 				/*
329*7c478bd9Sstevel@tonic-gate 				 * Since options are guaranteed aligned only
330*7c478bd9Sstevel@tonic-gate 				 * on a 4 byte boundary (t_scalar_t) any
331*7c478bd9Sstevel@tonic-gate 				 * option that is greater in size will default
332*7c478bd9Sstevel@tonic-gate 				 * to the bcopy below
333*7c478bd9Sstevel@tonic-gate 				 */
334*7c478bd9Sstevel@tonic-gate 				case sizeof (int32_t):
335*7c478bd9Sstevel@tonic-gate 					*(int32_t *)&opt[1] =
336*7c478bd9Sstevel@tonic-gate 					    (int32_t)optd->opdes_default;
337*7c478bd9Sstevel@tonic-gate 					break;
338*7c478bd9Sstevel@tonic-gate 				case sizeof (int16_t):
339*7c478bd9Sstevel@tonic-gate 					*(int16_t *)&opt[1] =
340*7c478bd9Sstevel@tonic-gate 					    (int16_t)optd->opdes_default;
341*7c478bd9Sstevel@tonic-gate 					break;
342*7c478bd9Sstevel@tonic-gate 				case sizeof (int8_t):
343*7c478bd9Sstevel@tonic-gate 					*(int8_t *)&opt[1] =
344*7c478bd9Sstevel@tonic-gate 					    (int8_t)optd->opdes_default;
345*7c478bd9Sstevel@tonic-gate 					break;
346*7c478bd9Sstevel@tonic-gate 				default:
347*7c478bd9Sstevel@tonic-gate 					/*
348*7c478bd9Sstevel@tonic-gate 					 * other length but still assume
349*7c478bd9Sstevel@tonic-gate 					 * fixed - use bcopy
350*7c478bd9Sstevel@tonic-gate 					 */
351*7c478bd9Sstevel@tonic-gate 					bcopy(optd->opdes_defbuf,
352*7c478bd9Sstevel@tonic-gate 					    &opt[1], optd->opdes_size);
353*7c478bd9Sstevel@tonic-gate 					break;
354*7c478bd9Sstevel@tonic-gate 				}
355*7c478bd9Sstevel@tonic-gate 				opt->len = optd->opdes_size;
356*7c478bd9Sstevel@tonic-gate 			}
357*7c478bd9Sstevel@tonic-gate 			else
358*7c478bd9Sstevel@tonic-gate 				opt->len = (t_uscalar_t)len;
359*7c478bd9Sstevel@tonic-gate 			opt = (struct opthdr *)((char *)&opt[1] +
360*7c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt->len));
361*7c478bd9Sstevel@tonic-gate 		}
362*7c478bd9Sstevel@tonic-gate 
363*7c478bd9Sstevel@tonic-gate 		/* Now record the final length. */
364*7c478bd9Sstevel@tonic-gate 		toa->OPT_length = (t_scalar_t)((char *)opt - (char *)&toa[1]);
365*7c478bd9Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)opt;
366*7c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PCPROTO;
367*7c478bd9Sstevel@tonic-gate 		/* Ship it back. */
368*7c478bd9Sstevel@tonic-gate 		qreply(q, mp);
369*7c478bd9Sstevel@tonic-gate 		return (0);
370*7c478bd9Sstevel@tonic-gate 	}
371*7c478bd9Sstevel@tonic-gate 	/* T_DEFAULT processing complete - no more T_DEFAULT */
372*7c478bd9Sstevel@tonic-gate 
373*7c478bd9Sstevel@tonic-gate 	/*
374*7c478bd9Sstevel@tonic-gate 	 * For T_NEGOTIATE, T_CURRENT, and T_CHECK requests, we make a
375*7c478bd9Sstevel@tonic-gate 	 * pass through the input buffer validating the details and
376*7c478bd9Sstevel@tonic-gate 	 * making sure each option is supported by the protocol.
377*7c478bd9Sstevel@tonic-gate 	 */
378*7c478bd9Sstevel@tonic-gate 	if ((opt_start = (struct opthdr *)mi_offset_param(mp,
379*7c478bd9Sstevel@tonic-gate 	    tor->OPT_offset, tor->OPT_length)) == NULL)
380*7c478bd9Sstevel@tonic-gate 		goto bad_opt;
381*7c478bd9Sstevel@tonic-gate 	if (!__TPI_OPT_ISALIGNED(opt_start))
382*7c478bd9Sstevel@tonic-gate 		goto bad_opt;
383*7c478bd9Sstevel@tonic-gate 
384*7c478bd9Sstevel@tonic-gate 	opt_end = (struct opthdr *)((uchar_t *)opt_start +
385*7c478bd9Sstevel@tonic-gate 	    tor->OPT_length);
386*7c478bd9Sstevel@tonic-gate 
387*7c478bd9Sstevel@tonic-gate 	for (opt = opt_start; opt < opt_end; opt = next_opt) {
388*7c478bd9Sstevel@tonic-gate 		/*
389*7c478bd9Sstevel@tonic-gate 		 * Verify we have room to reference the option header
390*7c478bd9Sstevel@tonic-gate 		 * fields in the option buffer.
391*7c478bd9Sstevel@tonic-gate 		 */
392*7c478bd9Sstevel@tonic-gate 		if ((uchar_t *)opt + sizeof (struct opthdr) >
393*7c478bd9Sstevel@tonic-gate 		    (uchar_t *)opt_end)
394*7c478bd9Sstevel@tonic-gate 			goto bad_opt;
395*7c478bd9Sstevel@tonic-gate 		/*
396*7c478bd9Sstevel@tonic-gate 		 * We now compute pointer to next option in buffer 'next_opt'
397*7c478bd9Sstevel@tonic-gate 		 * The next_opt computation above below 'opt->len' initialized
398*7c478bd9Sstevel@tonic-gate 		 * by application which cannot be trusted. The usual value
399*7c478bd9Sstevel@tonic-gate 		 * too large will be captured by the loop termination condition
400*7c478bd9Sstevel@tonic-gate 		 * above. We check for the following which it will miss.
401*7c478bd9Sstevel@tonic-gate 		 * 	-pointer space wraparound arithmetic overflow
402*7c478bd9Sstevel@tonic-gate 		 *	-last option in buffer with 'opt->len' being too large
403*7c478bd9Sstevel@tonic-gate 		 *	 (only reason 'next_opt' should equal or exceed
404*7c478bd9Sstevel@tonic-gate 		 *	 'opt_end' for last option is roundup unless length is
405*7c478bd9Sstevel@tonic-gate 		 *	 too-large/invalid)
406*7c478bd9Sstevel@tonic-gate 		 */
407*7c478bd9Sstevel@tonic-gate 		next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
408*7c478bd9Sstevel@tonic-gate 		    _TPI_ALIGN_OPT(opt->len));
409*7c478bd9Sstevel@tonic-gate 
410*7c478bd9Sstevel@tonic-gate 		if ((uchar_t *)next_opt < (uchar_t *)&opt[1] ||
411*7c478bd9Sstevel@tonic-gate 		    ((next_opt >= opt_end) &&
412*7c478bd9Sstevel@tonic-gate 			(((uchar_t *)next_opt - (uchar_t *)opt_end) >=
413*7c478bd9Sstevel@tonic-gate 			    __TPI_ALIGN_SIZE)))
414*7c478bd9Sstevel@tonic-gate 			goto bad_opt;
415*7c478bd9Sstevel@tonic-gate 
416*7c478bd9Sstevel@tonic-gate 		/* sanity check */
417*7c478bd9Sstevel@tonic-gate 		if (opt->name == T_ALLOPT)
418*7c478bd9Sstevel@tonic-gate 			goto bad_opt;
419*7c478bd9Sstevel@tonic-gate 
420*7c478bd9Sstevel@tonic-gate 		/* Find the option in the opt_arr. */
421*7c478bd9Sstevel@tonic-gate 		if ((optd = opt_chk_lookup(opt->level, opt->name,
422*7c478bd9Sstevel@tonic-gate 		    opt_arr, opt_arr_cnt)) == NULL) {
423*7c478bd9Sstevel@tonic-gate 			/*
424*7c478bd9Sstevel@tonic-gate 			 * Not found, that is a bad thing if
425*7c478bd9Sstevel@tonic-gate 			 * the caller is a tpi provider
426*7c478bd9Sstevel@tonic-gate 			 */
427*7c478bd9Sstevel@tonic-gate 			if (topmost_tpiprovider)
428*7c478bd9Sstevel@tonic-gate 				goto bad_opt;
429*7c478bd9Sstevel@tonic-gate 			else
430*7c478bd9Sstevel@tonic-gate 				continue; /* skip unmodified */
431*7c478bd9Sstevel@tonic-gate 		}
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 		/* Additional checks dependent on operation. */
434*7c478bd9Sstevel@tonic-gate 		switch (tor->MGMT_flags) {
435*7c478bd9Sstevel@tonic-gate 		case T_NEGOTIATE:
436*7c478bd9Sstevel@tonic-gate 			if (!OA_WRITE_OR_EXECUTE(optd, cr)) {
437*7c478bd9Sstevel@tonic-gate 				/* can't negotiate option */
438*7c478bd9Sstevel@tonic-gate 				if (!(OA_MATCHED_PRIV(optd, cr)) &&
439*7c478bd9Sstevel@tonic-gate 				    OA_WX_ANYPRIV(optd)) {
440*7c478bd9Sstevel@tonic-gate 					/*
441*7c478bd9Sstevel@tonic-gate 					 * not privileged but privilege
442*7c478bd9Sstevel@tonic-gate 					 * will help negotiate option.
443*7c478bd9Sstevel@tonic-gate 					 */
444*7c478bd9Sstevel@tonic-gate 					optcom_err_ack(q, mp, TACCES, 0);
445*7c478bd9Sstevel@tonic-gate 					return (0);
446*7c478bd9Sstevel@tonic-gate 				} else
447*7c478bd9Sstevel@tonic-gate 					goto bad_opt;
448*7c478bd9Sstevel@tonic-gate 			}
449*7c478bd9Sstevel@tonic-gate 			/*
450*7c478bd9Sstevel@tonic-gate 			 * Verify size for options
451*7c478bd9Sstevel@tonic-gate 			 * Note: For retaining compatibility with historical
452*7c478bd9Sstevel@tonic-gate 			 * behavior, variable lengths options will have their
453*7c478bd9Sstevel@tonic-gate 			 * length verified in the setfn() processing.
454*7c478bd9Sstevel@tonic-gate 			 * In order to be compatible with SunOS 4.X we return
455*7c478bd9Sstevel@tonic-gate 			 * EINVAL errors for bad lengths.
456*7c478bd9Sstevel@tonic-gate 			 */
457*7c478bd9Sstevel@tonic-gate 			if (!(optd->opdes_props & OP_VARLEN)) {
458*7c478bd9Sstevel@tonic-gate 				/* fixed length - size must match */
459*7c478bd9Sstevel@tonic-gate 				if (opt->len != optd->opdes_size) {
460*7c478bd9Sstevel@tonic-gate 					optcom_err_ack(q, mp, TSYSERR, EINVAL);
461*7c478bd9Sstevel@tonic-gate 					return (0);
462*7c478bd9Sstevel@tonic-gate 				}
463*7c478bd9Sstevel@tonic-gate 			}
464*7c478bd9Sstevel@tonic-gate 			break;
465*7c478bd9Sstevel@tonic-gate 
466*7c478bd9Sstevel@tonic-gate 		case T_CHECK:
467*7c478bd9Sstevel@tonic-gate 			if (!OA_RWX_ANYPRIV(optd))
468*7c478bd9Sstevel@tonic-gate 				/* any of "rwx" permission but not not none */
469*7c478bd9Sstevel@tonic-gate 				goto bad_opt;
470*7c478bd9Sstevel@tonic-gate 			/*
471*7c478bd9Sstevel@tonic-gate 			 * XXX Since T_CURRENT was not there in TLI and the
472*7c478bd9Sstevel@tonic-gate 			 * official TLI inspired TPI standard, getsockopt()
473*7c478bd9Sstevel@tonic-gate 			 * API uses T_CHECK (for T_CURRENT semantics)
474*7c478bd9Sstevel@tonic-gate 			 * The following fallthru makes sense because of its
475*7c478bd9Sstevel@tonic-gate 			 * historical use as semantic equivalent to T_CURRENT.
476*7c478bd9Sstevel@tonic-gate 			 */
477*7c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
478*7c478bd9Sstevel@tonic-gate 		case T_CURRENT:
479*7c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr)) {
480*7c478bd9Sstevel@tonic-gate 				/* can't read option value */
481*7c478bd9Sstevel@tonic-gate 				if (!(OA_MATCHED_PRIV(optd, cr)) &&
482*7c478bd9Sstevel@tonic-gate 				    OA_R_ANYPRIV(optd)) {
483*7c478bd9Sstevel@tonic-gate 					/*
484*7c478bd9Sstevel@tonic-gate 					 * not privileged but privilege
485*7c478bd9Sstevel@tonic-gate 					 * will help in reading option value.
486*7c478bd9Sstevel@tonic-gate 					 */
487*7c478bd9Sstevel@tonic-gate 					optcom_err_ack(q, mp, TACCES, 0);
488*7c478bd9Sstevel@tonic-gate 					return (0);
489*7c478bd9Sstevel@tonic-gate 				} else
490*7c478bd9Sstevel@tonic-gate 					goto bad_opt;
491*7c478bd9Sstevel@tonic-gate 			}
492*7c478bd9Sstevel@tonic-gate 			break;
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate 		default:
495*7c478bd9Sstevel@tonic-gate 			optcom_err_ack(q, mp, TBADFLAG, 0);
496*7c478bd9Sstevel@tonic-gate 			return (0);
497*7c478bd9Sstevel@tonic-gate 		}
498*7c478bd9Sstevel@tonic-gate 		/* We liked it.  Keep going. */
499*7c478bd9Sstevel@tonic-gate 	} /* end for loop scanning option buffer */
500*7c478bd9Sstevel@tonic-gate 
501*7c478bd9Sstevel@tonic-gate 	/* Now complete the operation as required. */
502*7c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
503*7c478bd9Sstevel@tonic-gate 	case T_CHECK:
504*7c478bd9Sstevel@tonic-gate 		/*
505*7c478bd9Sstevel@tonic-gate 		 * Historically used same as T_CURRENT (which was added to
506*7c478bd9Sstevel@tonic-gate 		 * standard later). Code retained for compatibility.
507*7c478bd9Sstevel@tonic-gate 		 */
508*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
509*7c478bd9Sstevel@tonic-gate 	case T_CURRENT:
510*7c478bd9Sstevel@tonic-gate 		/*
511*7c478bd9Sstevel@tonic-gate 		 * Allocate a maximum size reply.  Perhaps we are supposed to
512*7c478bd9Sstevel@tonic-gate 		 * assume that the input buffer includes space for the answers
513*7c478bd9Sstevel@tonic-gate 		 * as well as the opthdrs, but we don't know that for sure.
514*7c478bd9Sstevel@tonic-gate 		 * So, instead, we create a new output buffer, using the
515*7c478bd9Sstevel@tonic-gate 		 * input buffer only as a list of options.
516*7c478bd9Sstevel@tonic-gate 		 */
517*7c478bd9Sstevel@tonic-gate 		max_optbuf_len = optcom_max_optbuf_len(opt_arr,
518*7c478bd9Sstevel@tonic-gate 		    opt_arr_cnt);
519*7c478bd9Sstevel@tonic-gate 		mp1 = allocb_cred(max_optbuf_len, cr);
520*7c478bd9Sstevel@tonic-gate 		if (!mp1)
521*7c478bd9Sstevel@tonic-gate 			goto no_mem;
522*7c478bd9Sstevel@tonic-gate 		/* Initialize the header. */
523*7c478bd9Sstevel@tonic-gate 		mp1->b_datap->db_type = M_PCPROTO;
524*7c478bd9Sstevel@tonic-gate 		mp1->b_wptr = &mp1->b_rptr[sizeof (struct T_optmgmt_ack)];
525*7c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)mp1->b_rptr;
526*7c478bd9Sstevel@tonic-gate 		toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
527*7c478bd9Sstevel@tonic-gate 		toa->MGMT_flags = tor->MGMT_flags;
528*7c478bd9Sstevel@tonic-gate 		/*
529*7c478bd9Sstevel@tonic-gate 		 * Walk through the input buffer again, this time adding
530*7c478bd9Sstevel@tonic-gate 		 * entries to the output buffer for each option requested.
531*7c478bd9Sstevel@tonic-gate 		 * Note, sanity of option header, last option etc, verified
532*7c478bd9Sstevel@tonic-gate 		 * in first pass.
533*7c478bd9Sstevel@tonic-gate 		 */
534*7c478bd9Sstevel@tonic-gate 		opt1 = (struct opthdr *)&toa[1];
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 		for (opt = opt_start; opt < opt_end; opt = next_opt) {
537*7c478bd9Sstevel@tonic-gate 
538*7c478bd9Sstevel@tonic-gate 		    next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
539*7c478bd9Sstevel@tonic-gate 			_TPI_ALIGN_OPT(opt->len));
540*7c478bd9Sstevel@tonic-gate 
541*7c478bd9Sstevel@tonic-gate 			opt1->name = opt->name;
542*7c478bd9Sstevel@tonic-gate 			opt1->level = opt->level;
543*7c478bd9Sstevel@tonic-gate 			len = (*getfn)(q, opt->level,
544*7c478bd9Sstevel@tonic-gate 			    opt->name, (uchar_t *)&opt1[1]);
545*7c478bd9Sstevel@tonic-gate 			/*
546*7c478bd9Sstevel@tonic-gate 			 * Failure means option is not recognized. Copy input
547*7c478bd9Sstevel@tonic-gate 			 * buffer as is
548*7c478bd9Sstevel@tonic-gate 			 */
549*7c478bd9Sstevel@tonic-gate 			if (len < 0) {
550*7c478bd9Sstevel@tonic-gate 				opt1->len = opt->len;
551*7c478bd9Sstevel@tonic-gate 				bcopy(&opt[1], &opt1[1], opt->len);
552*7c478bd9Sstevel@tonic-gate 				/*
553*7c478bd9Sstevel@tonic-gate 				 * Pass the option down to IP only if
554*7c478bd9Sstevel@tonic-gate 				 * TCP hasn't processed it.
555*7c478bd9Sstevel@tonic-gate 				 */
556*7c478bd9Sstevel@tonic-gate 				if (dbobjp == &tcp_opt_obj)
557*7c478bd9Sstevel@tonic-gate 					pass_to_ip = B_TRUE;
558*7c478bd9Sstevel@tonic-gate 			}
559*7c478bd9Sstevel@tonic-gate 			else
560*7c478bd9Sstevel@tonic-gate 				opt1->len = (t_uscalar_t)len;
561*7c478bd9Sstevel@tonic-gate 			opt1 = (struct opthdr *)((uchar_t *)&opt1[1] +
562*7c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt1->len));
563*7c478bd9Sstevel@tonic-gate 		} /* end for loop */
564*7c478bd9Sstevel@tonic-gate 
565*7c478bd9Sstevel@tonic-gate 		/* Record the final length. */
566*7c478bd9Sstevel@tonic-gate 		toa->OPT_length = (t_scalar_t)((uchar_t *)opt1 -
567*7c478bd9Sstevel@tonic-gate 		    (uchar_t *)&toa[1]);
568*7c478bd9Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)opt1;
569*7c478bd9Sstevel@tonic-gate 		/* Ditch the input buffer. */
570*7c478bd9Sstevel@tonic-gate 		freemsg(mp);
571*7c478bd9Sstevel@tonic-gate 		mp = mp1;
572*7c478bd9Sstevel@tonic-gate 		/* Always let the next module look at the option. */
573*7c478bd9Sstevel@tonic-gate 		pass_to_next = B_TRUE;
574*7c478bd9Sstevel@tonic-gate 		break;
575*7c478bd9Sstevel@tonic-gate 
576*7c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
577*7c478bd9Sstevel@tonic-gate 		first_mp = allocb(sizeof (opt_restart_t), BPRI_LO);
578*7c478bd9Sstevel@tonic-gate 		if (first_mp == NULL) {
579*7c478bd9Sstevel@tonic-gate 			optcom_err_ack(q, mp, TSYSERR, ENOMEM);
580*7c478bd9Sstevel@tonic-gate 			return (0);
581*7c478bd9Sstevel@tonic-gate 		}
582*7c478bd9Sstevel@tonic-gate 		first_mp->b_datap->db_type = M_CTL;
583*7c478bd9Sstevel@tonic-gate 		or = (opt_restart_t *)first_mp->b_rptr;
584*7c478bd9Sstevel@tonic-gate 		or->or_start = opt_start;
585*7c478bd9Sstevel@tonic-gate 		or->or_end =  opt_end;
586*7c478bd9Sstevel@tonic-gate 		or->or_type = T_SVR4_OPTMGMT_REQ;
587*7c478bd9Sstevel@tonic-gate 		or->or_private = 0;
588*7c478bd9Sstevel@tonic-gate 		first_mp->b_cont = mp;
589*7c478bd9Sstevel@tonic-gate restart:
590*7c478bd9Sstevel@tonic-gate 		/*
591*7c478bd9Sstevel@tonic-gate 		 * Here we are expecting that the response buffer is exactly
592*7c478bd9Sstevel@tonic-gate 		 * the same size as the input buffer.  We pass each opthdr
593*7c478bd9Sstevel@tonic-gate 		 * to the protocol's set function.  If the protocol doesn't
594*7c478bd9Sstevel@tonic-gate 		 * like it, it can update the value in it return argument.
595*7c478bd9Sstevel@tonic-gate 		 */
596*7c478bd9Sstevel@tonic-gate 		/*
597*7c478bd9Sstevel@tonic-gate 		 * Pass each negotiated option through the protocol set
598*7c478bd9Sstevel@tonic-gate 		 * function.
599*7c478bd9Sstevel@tonic-gate 		 * Note: sanity check on option header values done in first
600*7c478bd9Sstevel@tonic-gate 		 * pass and not repeated here.
601*7c478bd9Sstevel@tonic-gate 		 */
602*7c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)tor;
603*7c478bd9Sstevel@tonic-gate 
604*7c478bd9Sstevel@tonic-gate 		for (opt = is_restart ? restart_opt: opt_start; opt < opt_end;
605*7c478bd9Sstevel@tonic-gate 		    opt = next_opt) {
606*7c478bd9Sstevel@tonic-gate 			int error;
607*7c478bd9Sstevel@tonic-gate 
608*7c478bd9Sstevel@tonic-gate 			/*
609*7c478bd9Sstevel@tonic-gate 			 * Point to the current option in or, in case this
610*7c478bd9Sstevel@tonic-gate 			 * option has to be restarted later on
611*7c478bd9Sstevel@tonic-gate 			 */
612*7c478bd9Sstevel@tonic-gate 			or->or_ropt = opt;
613*7c478bd9Sstevel@tonic-gate 			next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
614*7c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt->len));
615*7c478bd9Sstevel@tonic-gate 
616*7c478bd9Sstevel@tonic-gate 			error = (*setfn)(q, SETFN_OPTCOM_NEGOTIATE,
617*7c478bd9Sstevel@tonic-gate 			    opt->level, opt->name,
618*7c478bd9Sstevel@tonic-gate 			    opt->len, (uchar_t *)&opt[1],
619*7c478bd9Sstevel@tonic-gate 			    &opt->len, (uchar_t *)&opt[1], NULL, cr, first_mp);
620*7c478bd9Sstevel@tonic-gate 			/*
621*7c478bd9Sstevel@tonic-gate 			 * Treat positive "errors" as real.
622*7c478bd9Sstevel@tonic-gate 			 * Note: negative errors are to be treated as
623*7c478bd9Sstevel@tonic-gate 			 * non-fatal by svr4_optcom_req() and are
624*7c478bd9Sstevel@tonic-gate 			 * returned by setfn() when it is passed an
625*7c478bd9Sstevel@tonic-gate 			 * option it does not handle. Since the option
626*7c478bd9Sstevel@tonic-gate 			 * passed opt_chk_lookup(), it is implied that
627*7c478bd9Sstevel@tonic-gate 			 * it is valid but was either handled upstream
628*7c478bd9Sstevel@tonic-gate 			 * or will be handled downstream.
629*7c478bd9Sstevel@tonic-gate 			 */
630*7c478bd9Sstevel@tonic-gate 			if (error == EINPROGRESS) {
631*7c478bd9Sstevel@tonic-gate 				/*
632*7c478bd9Sstevel@tonic-gate 				 * The message is queued and will be
633*7c478bd9Sstevel@tonic-gate 				 * reprocessed later. Typically ip queued
634*7c478bd9Sstevel@tonic-gate 				 * the message to get some exclusive conditions
635*7c478bd9Sstevel@tonic-gate 				 * and later on calls this func again.
636*7c478bd9Sstevel@tonic-gate 				 */
637*7c478bd9Sstevel@tonic-gate 				return (EINPROGRESS);
638*7c478bd9Sstevel@tonic-gate 			} else if (error > 0) {
639*7c478bd9Sstevel@tonic-gate 				optcom_err_ack(q, mp, TSYSERR, error);
640*7c478bd9Sstevel@tonic-gate 				freeb(first_mp);
641*7c478bd9Sstevel@tonic-gate 				return (0);
642*7c478bd9Sstevel@tonic-gate 			} else if (error < 0 && dbobjp == &tcp_opt_obj) {
643*7c478bd9Sstevel@tonic-gate 				/*
644*7c478bd9Sstevel@tonic-gate 				 * Pass the option down to IP only if
645*7c478bd9Sstevel@tonic-gate 				 * TCP hasn't processed it.
646*7c478bd9Sstevel@tonic-gate 				 */
647*7c478bd9Sstevel@tonic-gate 				pass_to_ip = B_TRUE;
648*7c478bd9Sstevel@tonic-gate 			}
649*7c478bd9Sstevel@tonic-gate 		}
650*7c478bd9Sstevel@tonic-gate 		/* Done with the restart control mp. */
651*7c478bd9Sstevel@tonic-gate 		freeb(first_mp);
652*7c478bd9Sstevel@tonic-gate 		pass_to_next = B_TRUE;
653*7c478bd9Sstevel@tonic-gate 		break;
654*7c478bd9Sstevel@tonic-gate 	default:
655*7c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
656*7c478bd9Sstevel@tonic-gate 		return (0);
657*7c478bd9Sstevel@tonic-gate 	}
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate 	if (pass_to_next && (q->q_next != NULL || pass_to_ip)) {
660*7c478bd9Sstevel@tonic-gate 		/* Send it down to the next module and let it reply */
661*7c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_SVR4_OPTMGMT_REQ; /* Changed by IP to ACK */
662*7c478bd9Sstevel@tonic-gate 		if (q->q_next != NULL)
663*7c478bd9Sstevel@tonic-gate 			putnext(q, mp);
664*7c478bd9Sstevel@tonic-gate 		else
665*7c478bd9Sstevel@tonic-gate 			ip_output(Q_TO_CONN(q), mp, q, IP_WPUT);
666*7c478bd9Sstevel@tonic-gate 	} else {
667*7c478bd9Sstevel@tonic-gate 		/* Set common fields in the header. */
668*7c478bd9Sstevel@tonic-gate 		toa->MGMT_flags = T_SUCCESS;
669*7c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PCPROTO;
670*7c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_ACK;
671*7c478bd9Sstevel@tonic-gate 		qreply(q, mp);
672*7c478bd9Sstevel@tonic-gate 	}
673*7c478bd9Sstevel@tonic-gate 	return (0);
674*7c478bd9Sstevel@tonic-gate bad_opt:;
675*7c478bd9Sstevel@tonic-gate 	optcom_err_ack(q, mp, TBADOPT, 0);
676*7c478bd9Sstevel@tonic-gate 	return (0);
677*7c478bd9Sstevel@tonic-gate }
678*7c478bd9Sstevel@tonic-gate 
679*7c478bd9Sstevel@tonic-gate /*
680*7c478bd9Sstevel@tonic-gate  * New optcom_req inspired by TPI/XTI semantics
681*7c478bd9Sstevel@tonic-gate  */
682*7c478bd9Sstevel@tonic-gate int
683*7c478bd9Sstevel@tonic-gate tpi_optcom_req(queue_t *q, mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp)
684*7c478bd9Sstevel@tonic-gate {
685*7c478bd9Sstevel@tonic-gate 	t_scalar_t t_error;
686*7c478bd9Sstevel@tonic-gate 	mblk_t *toa_mp;
687*7c478bd9Sstevel@tonic-gate 	boolean_t pass_to_next;
688*7c478bd9Sstevel@tonic-gate 	size_t toa_len;
689*7c478bd9Sstevel@tonic-gate 	struct T_optmgmt_ack *toa;
690*7c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor =
691*7c478bd9Sstevel@tonic-gate 	    (struct T_optmgmt_req *)mp->b_rptr;
692*7c478bd9Sstevel@tonic-gate 
693*7c478bd9Sstevel@tonic-gate 	opt_restart_t *or;
694*7c478bd9Sstevel@tonic-gate 	boolean_t is_restart = B_FALSE;
695*7c478bd9Sstevel@tonic-gate 	mblk_t	*first_mp = NULL;
696*7c478bd9Sstevel@tonic-gate 	t_uscalar_t worst_status;
697*7c478bd9Sstevel@tonic-gate 	boolean_t queued_status;
698*7c478bd9Sstevel@tonic-gate 
699*7c478bd9Sstevel@tonic-gate 	/*
700*7c478bd9Sstevel@tonic-gate 	 * Allocate M_CTL and prepend to the packet for restarting this
701*7c478bd9Sstevel@tonic-gate 	 * option if needed. IP may need to queue and restart the option
702*7c478bd9Sstevel@tonic-gate 	 * if it cannot obtain exclusive conditions immediately. Please see
703*7c478bd9Sstevel@tonic-gate 	 * IP-MT notes before the start of svr4_optcom_req
704*7c478bd9Sstevel@tonic-gate 	 */
705*7c478bd9Sstevel@tonic-gate 	if (mp->b_datap->db_type == M_CTL) {
706*7c478bd9Sstevel@tonic-gate 		is_restart = B_TRUE;
707*7c478bd9Sstevel@tonic-gate 		first_mp = mp;
708*7c478bd9Sstevel@tonic-gate 		toa_mp = mp->b_cont;
709*7c478bd9Sstevel@tonic-gate 		mp = toa_mp->b_cont;
710*7c478bd9Sstevel@tonic-gate 		ASSERT(mp->b_wptr - mp->b_rptr >=
711*7c478bd9Sstevel@tonic-gate 		    sizeof (struct T_optmgmt_req));
712*7c478bd9Sstevel@tonic-gate 		tor = (struct T_optmgmt_req *)mp->b_rptr;
713*7c478bd9Sstevel@tonic-gate 		ASSERT(tor->MGMT_flags == T_NEGOTIATE);
714*7c478bd9Sstevel@tonic-gate 
715*7c478bd9Sstevel@tonic-gate 		or = (opt_restart_t *)first_mp->b_rptr;
716*7c478bd9Sstevel@tonic-gate 		goto restart;
717*7c478bd9Sstevel@tonic-gate 	}
718*7c478bd9Sstevel@tonic-gate 
719*7c478bd9Sstevel@tonic-gate 	/* Verify message integrity. */
720*7c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (struct T_optmgmt_req)) {
721*7c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADOPT, 0);
722*7c478bd9Sstevel@tonic-gate 		return (0);
723*7c478bd9Sstevel@tonic-gate 	}
724*7c478bd9Sstevel@tonic-gate 
725*7c478bd9Sstevel@tonic-gate 	/* Verify MGMT_flags legal */
726*7c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
727*7c478bd9Sstevel@tonic-gate 	case T_DEFAULT:
728*7c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
729*7c478bd9Sstevel@tonic-gate 	case T_CURRENT:
730*7c478bd9Sstevel@tonic-gate 	case T_CHECK:
731*7c478bd9Sstevel@tonic-gate 		/* OK - legal request flags */
732*7c478bd9Sstevel@tonic-gate 		break;
733*7c478bd9Sstevel@tonic-gate 	default:
734*7c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
735*7c478bd9Sstevel@tonic-gate 		return (0);
736*7c478bd9Sstevel@tonic-gate 	}
737*7c478bd9Sstevel@tonic-gate 
738*7c478bd9Sstevel@tonic-gate 	/*
739*7c478bd9Sstevel@tonic-gate 	 * In this design, there are two passes required on the input buffer
740*7c478bd9Sstevel@tonic-gate 	 * mostly to accomodate variable length options and "T_ALLOPT" option
741*7c478bd9Sstevel@tonic-gate 	 * which has the semantics "all options of the specified level".
742*7c478bd9Sstevel@tonic-gate 	 *
743*7c478bd9Sstevel@tonic-gate 	 * For T_DEFAULT, T_NEGOTIATE, T_CURRENT, and T_CHECK requests, we make
744*7c478bd9Sstevel@tonic-gate 	 * a pass through the input buffer validating the details and making
745*7c478bd9Sstevel@tonic-gate 	 * sure each option is supported by the protocol. We also determine the
746*7c478bd9Sstevel@tonic-gate 	 * length of the option buffer to return. (Variable length options and
747*7c478bd9Sstevel@tonic-gate 	 * T_ALLOPT mean that length can be different for output buffer).
748*7c478bd9Sstevel@tonic-gate 	 */
749*7c478bd9Sstevel@tonic-gate 
750*7c478bd9Sstevel@tonic-gate 	pass_to_next = B_FALSE;	/* initial value */
751*7c478bd9Sstevel@tonic-gate 	toa_len = 0;		/* initial value */
752*7c478bd9Sstevel@tonic-gate 
753*7c478bd9Sstevel@tonic-gate 	/*
754*7c478bd9Sstevel@tonic-gate 	 * First pass, we do the following
755*7c478bd9Sstevel@tonic-gate 	 *	- estimate cumulative length needed for results
756*7c478bd9Sstevel@tonic-gate 	 *	- set "status" field based on permissions, option header check
757*7c478bd9Sstevel@tonic-gate 	 *	  etc.
758*7c478bd9Sstevel@tonic-gate 	 *	- determine "pass_to_next" whether we need to send request to
759*7c478bd9Sstevel@tonic-gate 	 *	  downstream module/driver.
760*7c478bd9Sstevel@tonic-gate 	 */
761*7c478bd9Sstevel@tonic-gate 	if ((t_error = process_topthdrs_first_pass(mp, cr, dbobjp,
762*7c478bd9Sstevel@tonic-gate 	    &pass_to_next, &toa_len)) != 0) {
763*7c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, t_error, 0);
764*7c478bd9Sstevel@tonic-gate 		return (0);
765*7c478bd9Sstevel@tonic-gate 	}
766*7c478bd9Sstevel@tonic-gate 
767*7c478bd9Sstevel@tonic-gate 	/*
768*7c478bd9Sstevel@tonic-gate 	 * A validation phase of the input buffer is done. We have also
769*7c478bd9Sstevel@tonic-gate 	 * obtained the length requirement and and other details about the
770*7c478bd9Sstevel@tonic-gate 	 * input and we liked input buffer so far.  We make another scan
771*7c478bd9Sstevel@tonic-gate 	 * through the input now and generate the output necessary to complete
772*7c478bd9Sstevel@tonic-gate 	 * the operation.
773*7c478bd9Sstevel@tonic-gate 	 */
774*7c478bd9Sstevel@tonic-gate 
775*7c478bd9Sstevel@tonic-gate 	toa_mp = allocb_cred(toa_len, cr);
776*7c478bd9Sstevel@tonic-gate 	if (!toa_mp) {
777*7c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TSYSERR, ENOMEM);
778*7c478bd9Sstevel@tonic-gate 		return (0);
779*7c478bd9Sstevel@tonic-gate 	}
780*7c478bd9Sstevel@tonic-gate 
781*7c478bd9Sstevel@tonic-gate 	first_mp = allocb(sizeof (opt_restart_t), BPRI_LO);
782*7c478bd9Sstevel@tonic-gate 	if (first_mp == NULL) {
783*7c478bd9Sstevel@tonic-gate 		freeb(toa_mp);
784*7c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TSYSERR, ENOMEM);
785*7c478bd9Sstevel@tonic-gate 		return (0);
786*7c478bd9Sstevel@tonic-gate 	}
787*7c478bd9Sstevel@tonic-gate 	first_mp->b_datap->db_type = M_CTL;
788*7c478bd9Sstevel@tonic-gate 	or = (opt_restart_t *)first_mp->b_rptr;
789*7c478bd9Sstevel@tonic-gate 	/*
790*7c478bd9Sstevel@tonic-gate 	 * Set initial values for generating output.
791*7c478bd9Sstevel@tonic-gate 	 */
792*7c478bd9Sstevel@tonic-gate 	or->or_worst_status = T_SUCCESS;
793*7c478bd9Sstevel@tonic-gate 	or->or_type = T_OPTMGMT_REQ;
794*7c478bd9Sstevel@tonic-gate 	or->or_private = 0;
795*7c478bd9Sstevel@tonic-gate 	/* remaining fields fileed in do_options_second_pass */
796*7c478bd9Sstevel@tonic-gate 
797*7c478bd9Sstevel@tonic-gate restart:
798*7c478bd9Sstevel@tonic-gate 	/*
799*7c478bd9Sstevel@tonic-gate 	 * This routine makes another pass through the option buffer this
800*7c478bd9Sstevel@tonic-gate 	 * time acting on the request based on "status" result in the
801*7c478bd9Sstevel@tonic-gate 	 * first pass. It also performs "expansion" of T_ALLOPT into
802*7c478bd9Sstevel@tonic-gate 	 * all options of a certain level and acts on each for this request.
803*7c478bd9Sstevel@tonic-gate 	 */
804*7c478bd9Sstevel@tonic-gate 	if ((t_error = do_options_second_pass(q, mp, toa_mp, cr, dbobjp,
805*7c478bd9Sstevel@tonic-gate 	    first_mp, is_restart, &queued_status)) != 0) {
806*7c478bd9Sstevel@tonic-gate 		freemsg(toa_mp);
807*7c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, t_error, 0);
808*7c478bd9Sstevel@tonic-gate 		return (0);
809*7c478bd9Sstevel@tonic-gate 	}
810*7c478bd9Sstevel@tonic-gate 	if (queued_status) {
811*7c478bd9Sstevel@tonic-gate 		/* Option will be restarted */
812*7c478bd9Sstevel@tonic-gate 		return (EINPROGRESS);
813*7c478bd9Sstevel@tonic-gate 	}
814*7c478bd9Sstevel@tonic-gate 	worst_status = or->or_worst_status;
815*7c478bd9Sstevel@tonic-gate 	/* Done with the first mp */
816*7c478bd9Sstevel@tonic-gate 	freeb(first_mp);
817*7c478bd9Sstevel@tonic-gate 	toa_mp->b_cont = NULL;
818*7c478bd9Sstevel@tonic-gate 
819*7c478bd9Sstevel@tonic-gate 	/*
820*7c478bd9Sstevel@tonic-gate 	 * Following code relies on the coincidence that T_optmgmt_req
821*7c478bd9Sstevel@tonic-gate 	 * and T_optmgmt_ack are identical in binary representation
822*7c478bd9Sstevel@tonic-gate 	 */
823*7c478bd9Sstevel@tonic-gate 	toa = (struct T_optmgmt_ack *)toa_mp->b_rptr;
824*7c478bd9Sstevel@tonic-gate 	toa->OPT_length = (t_scalar_t)(toa_mp->b_wptr - (toa_mp->b_rptr +
825*7c478bd9Sstevel@tonic-gate 	    sizeof (struct T_optmgmt_ack)));
826*7c478bd9Sstevel@tonic-gate 	toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
827*7c478bd9Sstevel@tonic-gate 
828*7c478bd9Sstevel@tonic-gate 	toa->MGMT_flags = tor->MGMT_flags;
829*7c478bd9Sstevel@tonic-gate 
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate 	freemsg(mp);		/* free input mblk */
832*7c478bd9Sstevel@tonic-gate 
833*7c478bd9Sstevel@tonic-gate 	/*
834*7c478bd9Sstevel@tonic-gate 	 * If there is atleast one option that requires a downstream
835*7c478bd9Sstevel@tonic-gate 	 * forwarding and if it is possible, we forward the message
836*7c478bd9Sstevel@tonic-gate 	 * downstream. Else we ack it.
837*7c478bd9Sstevel@tonic-gate 	 */
838*7c478bd9Sstevel@tonic-gate 	if (pass_to_next && (q->q_next != NULL || dbobjp == &tcp_opt_obj)) {
839*7c478bd9Sstevel@tonic-gate 		/*
840*7c478bd9Sstevel@tonic-gate 		 * We pass it down as T_OPTMGMT_REQ. This code relies
841*7c478bd9Sstevel@tonic-gate 		 * on the happy coincidence that T_optmgmt_req and
842*7c478bd9Sstevel@tonic-gate 		 * T_optmgmt_ack are identical data structures
843*7c478bd9Sstevel@tonic-gate 		 * at the binary representation level.
844*7c478bd9Sstevel@tonic-gate 		 */
845*7c478bd9Sstevel@tonic-gate 		toa_mp->b_datap->db_type = M_PROTO;
846*7c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_REQ;
847*7c478bd9Sstevel@tonic-gate 		if (q->q_next != NULL)
848*7c478bd9Sstevel@tonic-gate 			putnext(q, toa_mp);
849*7c478bd9Sstevel@tonic-gate 		else
850*7c478bd9Sstevel@tonic-gate 			ip_output(Q_TO_CONN(q), toa_mp, q, IP_WPUT);
851*7c478bd9Sstevel@tonic-gate 	} else {
852*7c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_ACK;
853*7c478bd9Sstevel@tonic-gate 		toa_mp->b_datap->db_type = M_PCPROTO;
854*7c478bd9Sstevel@tonic-gate 		toa->MGMT_flags |= worst_status; /* XXX "worst" or "OR" TPI ? */
855*7c478bd9Sstevel@tonic-gate 		qreply(q, toa_mp);
856*7c478bd9Sstevel@tonic-gate 	}
857*7c478bd9Sstevel@tonic-gate 	return (0);
858*7c478bd9Sstevel@tonic-gate }
859*7c478bd9Sstevel@tonic-gate 
860*7c478bd9Sstevel@tonic-gate 
861*7c478bd9Sstevel@tonic-gate /*
862*7c478bd9Sstevel@tonic-gate  * Following routine makes a pass through option buffer in mp and performs the
863*7c478bd9Sstevel@tonic-gate  * following tasks.
864*7c478bd9Sstevel@tonic-gate  *	- estimate cumulative length needed for results
865*7c478bd9Sstevel@tonic-gate  *	- set "status" field based on permissions, option header check
866*7c478bd9Sstevel@tonic-gate  *	  etc.
867*7c478bd9Sstevel@tonic-gate  *	- determine "pass_to_next" whether we need to send request to
868*7c478bd9Sstevel@tonic-gate  *	  downstream module/driver.
869*7c478bd9Sstevel@tonic-gate  */
870*7c478bd9Sstevel@tonic-gate 
871*7c478bd9Sstevel@tonic-gate static t_scalar_t
872*7c478bd9Sstevel@tonic-gate process_topthdrs_first_pass(mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp,
873*7c478bd9Sstevel@tonic-gate     boolean_t *pass_to_nextp, size_t *toa_lenp)
874*7c478bd9Sstevel@tonic-gate {
875*7c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
876*7c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
877*7c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
878*7c478bd9Sstevel@tonic-gate 	optlevel_t *valid_level_arr = dbobjp->odb_valid_levels_arr;
879*7c478bd9Sstevel@tonic-gate 	uint_t valid_level_arr_cnt = dbobjp->odb_valid_levels_arr_cnt;
880*7c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt;
881*7c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt_start, *opt_end;
882*7c478bd9Sstevel@tonic-gate 	opdes_t	*optd;
883*7c478bd9Sstevel@tonic-gate 	size_t allopt_len;
884*7c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor =
885*7c478bd9Sstevel@tonic-gate 	    (struct T_optmgmt_req *)mp->b_rptr;
886*7c478bd9Sstevel@tonic-gate 
887*7c478bd9Sstevel@tonic-gate 	*toa_lenp = sizeof (struct T_optmgmt_ack); /* initial value */
888*7c478bd9Sstevel@tonic-gate 
889*7c478bd9Sstevel@tonic-gate 	if ((opt_start = (struct T_opthdr *)
890*7c478bd9Sstevel@tonic-gate 	    mi_offset_param(mp, tor->OPT_offset, tor->OPT_length)) == NULL) {
891*7c478bd9Sstevel@tonic-gate 		return (TBADOPT);
892*7c478bd9Sstevel@tonic-gate 	}
893*7c478bd9Sstevel@tonic-gate 	if (!__TPI_TOPT_ISALIGNED(opt_start))
894*7c478bd9Sstevel@tonic-gate 		return (TBADOPT);
895*7c478bd9Sstevel@tonic-gate 
896*7c478bd9Sstevel@tonic-gate 	opt_end = (struct T_opthdr *)((uchar_t *)opt_start + tor->OPT_length);
897*7c478bd9Sstevel@tonic-gate 
898*7c478bd9Sstevel@tonic-gate 	for (opt = opt_start; opt && (opt < opt_end);
899*7c478bd9Sstevel@tonic-gate 	    opt = _TPI_TOPT_NEXTHDR(opt_start, tor->OPT_length, opt)) {
900*7c478bd9Sstevel@tonic-gate 		/*
901*7c478bd9Sstevel@tonic-gate 		 * Validate the option for length and alignment
902*7c478bd9Sstevel@tonic-gate 		 * before accessing anything in it.
903*7c478bd9Sstevel@tonic-gate 		 */
904*7c478bd9Sstevel@tonic-gate 		if (!(_TPI_TOPT_VALID(opt, opt_start, opt_end)))
905*7c478bd9Sstevel@tonic-gate 			return (TBADOPT);
906*7c478bd9Sstevel@tonic-gate 
907*7c478bd9Sstevel@tonic-gate 		/* Find the option in the opt_arr. */
908*7c478bd9Sstevel@tonic-gate 		if (opt->name != T_ALLOPT) {
909*7c478bd9Sstevel@tonic-gate 			optd = opt_chk_lookup(opt->level, opt->name,
910*7c478bd9Sstevel@tonic-gate 			    opt_arr, opt_arr_cnt);
911*7c478bd9Sstevel@tonic-gate 			if (optd == NULL) {
912*7c478bd9Sstevel@tonic-gate 				/*
913*7c478bd9Sstevel@tonic-gate 				 * Option not found
914*7c478bd9Sstevel@tonic-gate 				 *
915*7c478bd9Sstevel@tonic-gate 				 * Verify if level is "valid" or not.
916*7c478bd9Sstevel@tonic-gate 				 * Note: This check is required by XTI
917*7c478bd9Sstevel@tonic-gate 				 *
918*7c478bd9Sstevel@tonic-gate 				 * TPI provider always initializes
919*7c478bd9Sstevel@tonic-gate 				 * the "not supported" (or whatever) status
920*7c478bd9Sstevel@tonic-gate 				 * for the options. Other levels leave status
921*7c478bd9Sstevel@tonic-gate 				 * unchanged if they do not understand an
922*7c478bd9Sstevel@tonic-gate 				 * option.
923*7c478bd9Sstevel@tonic-gate 				 */
924*7c478bd9Sstevel@tonic-gate 				if (topmost_tpiprovider) {
925*7c478bd9Sstevel@tonic-gate 					if (!opt_level_valid(opt->level,
926*7c478bd9Sstevel@tonic-gate 					    valid_level_arr,
927*7c478bd9Sstevel@tonic-gate 					    valid_level_arr_cnt))
928*7c478bd9Sstevel@tonic-gate 						return (TBADOPT);
929*7c478bd9Sstevel@tonic-gate 					/*
930*7c478bd9Sstevel@tonic-gate 					 * level is valid - initialize
931*7c478bd9Sstevel@tonic-gate 					 * option as not supported
932*7c478bd9Sstevel@tonic-gate 					 */
933*7c478bd9Sstevel@tonic-gate 					opt->status = T_NOTSUPPORT;
934*7c478bd9Sstevel@tonic-gate 				}
935*7c478bd9Sstevel@tonic-gate 
936*7c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
937*7c478bd9Sstevel@tonic-gate 				continue;
938*7c478bd9Sstevel@tonic-gate 			}
939*7c478bd9Sstevel@tonic-gate 		} else {
940*7c478bd9Sstevel@tonic-gate 			/*
941*7c478bd9Sstevel@tonic-gate 			 * Handle T_ALLOPT case as a special case.
942*7c478bd9Sstevel@tonic-gate 			 * Note: T_ALLOPT does not mean anything
943*7c478bd9Sstevel@tonic-gate 			 * for T_CHECK operation.
944*7c478bd9Sstevel@tonic-gate 			 */
945*7c478bd9Sstevel@tonic-gate 			allopt_len = 0;
946*7c478bd9Sstevel@tonic-gate 			if (tor->MGMT_flags == T_CHECK ||
947*7c478bd9Sstevel@tonic-gate 			    !topmost_tpiprovider ||
948*7c478bd9Sstevel@tonic-gate 			    ((allopt_len = opt_level_allopts_lengths(opt->level,
949*7c478bd9Sstevel@tonic-gate 				opt_arr, opt_arr_cnt)) == 0)) {
950*7c478bd9Sstevel@tonic-gate 				/*
951*7c478bd9Sstevel@tonic-gate 				 * This is confusing but correct !
952*7c478bd9Sstevel@tonic-gate 				 * It is not valid to to use T_ALLOPT with
953*7c478bd9Sstevel@tonic-gate 				 * T_CHECK flag.
954*7c478bd9Sstevel@tonic-gate 				 *
955*7c478bd9Sstevel@tonic-gate 				 * T_ALLOPT is assumed "expanded" at the
956*7c478bd9Sstevel@tonic-gate 				 * topmost_tpiprovider level so it should not
957*7c478bd9Sstevel@tonic-gate 				 * be there as an "option name" if this is not
958*7c478bd9Sstevel@tonic-gate 				 * a topmost_tpiprovider call and we fail it.
959*7c478bd9Sstevel@tonic-gate 				 *
960*7c478bd9Sstevel@tonic-gate 				 * opt_level_allopts_lengths() is used to verify
961*7c478bd9Sstevel@tonic-gate 				 * that "level" associated with the T_ALLOPT is
962*7c478bd9Sstevel@tonic-gate 				 * supported.
963*7c478bd9Sstevel@tonic-gate 				 *
964*7c478bd9Sstevel@tonic-gate 				 */
965*7c478bd9Sstevel@tonic-gate 				opt->status = T_FAILURE;
966*7c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
967*7c478bd9Sstevel@tonic-gate 				continue;
968*7c478bd9Sstevel@tonic-gate 			}
969*7c478bd9Sstevel@tonic-gate 			ASSERT(allopt_len != 0); /* remove ? */
970*7c478bd9Sstevel@tonic-gate 
971*7c478bd9Sstevel@tonic-gate 			*toa_lenp += allopt_len;
972*7c478bd9Sstevel@tonic-gate 			opt->status = T_SUCCESS;
973*7c478bd9Sstevel@tonic-gate 			/* XXX - always set T_ALLOPT 'pass_to_next' for now */
974*7c478bd9Sstevel@tonic-gate 			*pass_to_nextp = B_TRUE;
975*7c478bd9Sstevel@tonic-gate 			continue;
976*7c478bd9Sstevel@tonic-gate 		}
977*7c478bd9Sstevel@tonic-gate 		/*
978*7c478bd9Sstevel@tonic-gate 		 * Check if option wants to flow downstream
979*7c478bd9Sstevel@tonic-gate 		 */
980*7c478bd9Sstevel@tonic-gate 		if (optd->opdes_props & OP_PASSNEXT)
981*7c478bd9Sstevel@tonic-gate 			*pass_to_nextp = B_TRUE;
982*7c478bd9Sstevel@tonic-gate 
983*7c478bd9Sstevel@tonic-gate 		/* Additional checks dependent on operation. */
984*7c478bd9Sstevel@tonic-gate 		switch (tor->MGMT_flags) {
985*7c478bd9Sstevel@tonic-gate 		case T_DEFAULT:
986*7c478bd9Sstevel@tonic-gate 		case T_CURRENT:
987*7c478bd9Sstevel@tonic-gate 
988*7c478bd9Sstevel@tonic-gate 			/*
989*7c478bd9Sstevel@tonic-gate 			 * The opt_chk_lookup() routine call above approved of
990*7c478bd9Sstevel@tonic-gate 			 * this option so we can work on the status for it
991*7c478bd9Sstevel@tonic-gate 			 * based on the permissions for the operation. (This
992*7c478bd9Sstevel@tonic-gate 			 * can override any status for it set at higher levels)
993*7c478bd9Sstevel@tonic-gate 			 * We assume this override is OK since chkfn at this
994*7c478bd9Sstevel@tonic-gate 			 * level approved of this option.
995*7c478bd9Sstevel@tonic-gate 			 *
996*7c478bd9Sstevel@tonic-gate 			 * T_CURRENT semantics:
997*7c478bd9Sstevel@tonic-gate 			 * The read access is required. Else option
998*7c478bd9Sstevel@tonic-gate 			 * status is T_NOTSUPPORT.
999*7c478bd9Sstevel@tonic-gate 			 *
1000*7c478bd9Sstevel@tonic-gate 			 * T_DEFAULT semantics:
1001*7c478bd9Sstevel@tonic-gate 			 * Note: specification is not clear on this but we
1002*7c478bd9Sstevel@tonic-gate 			 * interpret T_DEFAULT semantics such that access to
1003*7c478bd9Sstevel@tonic-gate 			 * read value is required for access even the default
1004*7c478bd9Sstevel@tonic-gate 			 * value. Otherwise the option status is T_NOTSUPPORT.
1005*7c478bd9Sstevel@tonic-gate 			 */
1006*7c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr)) {
1007*7c478bd9Sstevel@tonic-gate 				opt->status = T_NOTSUPPORT;
1008*7c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
1009*7c478bd9Sstevel@tonic-gate 				/* skip to next */
1010*7c478bd9Sstevel@tonic-gate 				continue;
1011*7c478bd9Sstevel@tonic-gate 			}
1012*7c478bd9Sstevel@tonic-gate 
1013*7c478bd9Sstevel@tonic-gate 			/*
1014*7c478bd9Sstevel@tonic-gate 			 * T_DEFAULT/T_CURRENT semantics:
1015*7c478bd9Sstevel@tonic-gate 			 * We know that read access is set. If no other access
1016*7c478bd9Sstevel@tonic-gate 			 * is set, then status is T_READONLY.
1017*7c478bd9Sstevel@tonic-gate 			 */
1018*7c478bd9Sstevel@tonic-gate 			if (OA_READONLY_PERMISSION(optd, cr))
1019*7c478bd9Sstevel@tonic-gate 				opt->status = T_READONLY;
1020*7c478bd9Sstevel@tonic-gate 			else
1021*7c478bd9Sstevel@tonic-gate 				opt->status = T_SUCCESS;
1022*7c478bd9Sstevel@tonic-gate 			/*
1023*7c478bd9Sstevel@tonic-gate 			 * Option passes all checks. Make room for it in the
1024*7c478bd9Sstevel@tonic-gate 			 * ack. Note: size stored in table does not include
1025*7c478bd9Sstevel@tonic-gate 			 * space for option header.
1026*7c478bd9Sstevel@tonic-gate 			 */
1027*7c478bd9Sstevel@tonic-gate 			*toa_lenp += sizeof (struct T_opthdr) +
1028*7c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_TOPT(optd->opdes_size);
1029*7c478bd9Sstevel@tonic-gate 			break;
1030*7c478bd9Sstevel@tonic-gate 
1031*7c478bd9Sstevel@tonic-gate 		case T_CHECK:
1032*7c478bd9Sstevel@tonic-gate 		case T_NEGOTIATE:
1033*7c478bd9Sstevel@tonic-gate 
1034*7c478bd9Sstevel@tonic-gate 			/*
1035*7c478bd9Sstevel@tonic-gate 			 * T_NEGOTIATE semantics:
1036*7c478bd9Sstevel@tonic-gate 			 * If for fixed length option value on input is not the
1037*7c478bd9Sstevel@tonic-gate 			 * same as value supplied, then status is T_FAILURE.
1038*7c478bd9Sstevel@tonic-gate 			 *
1039*7c478bd9Sstevel@tonic-gate 			 * T_CHECK semantics:
1040*7c478bd9Sstevel@tonic-gate 			 * If value is supplied, semantics same as T_NEGOTIATE.
1041*7c478bd9Sstevel@tonic-gate 			 * It is however ok not to supply a value with T_CHECK.
1042*7c478bd9Sstevel@tonic-gate 			 */
1043*7c478bd9Sstevel@tonic-gate 
1044*7c478bd9Sstevel@tonic-gate 			if (tor->MGMT_flags == T_NEGOTIATE ||
1045*7c478bd9Sstevel@tonic-gate 			    (opt->len != sizeof (struct T_opthdr))) {
1046*7c478bd9Sstevel@tonic-gate 				/*
1047*7c478bd9Sstevel@tonic-gate 				 * Implies "value" is specified in T_CHECK or
1048*7c478bd9Sstevel@tonic-gate 				 * it is a T_NEGOTIATE request.
1049*7c478bd9Sstevel@tonic-gate 				 * Verify size.
1050*7c478bd9Sstevel@tonic-gate 				 * Note: This can override anything about this
1051*7c478bd9Sstevel@tonic-gate 				 * option request done at a higher level.
1052*7c478bd9Sstevel@tonic-gate 				 */
1053*7c478bd9Sstevel@tonic-gate 				if (!opt_length_ok(optd, opt)) {
1054*7c478bd9Sstevel@tonic-gate 					/* bad size */
1055*7c478bd9Sstevel@tonic-gate 					*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
1056*7c478bd9Sstevel@tonic-gate 					opt->status = T_FAILURE;
1057*7c478bd9Sstevel@tonic-gate 					continue;
1058*7c478bd9Sstevel@tonic-gate 				}
1059*7c478bd9Sstevel@tonic-gate 			}
1060*7c478bd9Sstevel@tonic-gate 			/*
1061*7c478bd9Sstevel@tonic-gate 			 * The opt_chk_lookup()  routine above() approved of
1062*7c478bd9Sstevel@tonic-gate 			 * this option so we can work on the status for it based
1063*7c478bd9Sstevel@tonic-gate 			 * on the permissions for the operation. (This can
1064*7c478bd9Sstevel@tonic-gate 			 * override anything set at a higher level).
1065*7c478bd9Sstevel@tonic-gate 			 *
1066*7c478bd9Sstevel@tonic-gate 			 * T_CHECK/T_NEGOTIATE semantics:
1067*7c478bd9Sstevel@tonic-gate 			 * Set status to T_READONLY if read is the only access
1068*7c478bd9Sstevel@tonic-gate 			 * permitted
1069*7c478bd9Sstevel@tonic-gate 			 */
1070*7c478bd9Sstevel@tonic-gate 			if (OA_READONLY_PERMISSION(optd, cr)) {
1071*7c478bd9Sstevel@tonic-gate 				opt->status = T_READONLY;
1072*7c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
1073*7c478bd9Sstevel@tonic-gate 				/* skip to next */
1074*7c478bd9Sstevel@tonic-gate 				continue;
1075*7c478bd9Sstevel@tonic-gate 			}
1076*7c478bd9Sstevel@tonic-gate 
1077*7c478bd9Sstevel@tonic-gate 			/*
1078*7c478bd9Sstevel@tonic-gate 			 * T_CHECK/T_NEGOTIATE semantics:
1079*7c478bd9Sstevel@tonic-gate 			 * If write (or execute) access is not set, then status
1080*7c478bd9Sstevel@tonic-gate 			 * is T_NOTSUPPORT.
1081*7c478bd9Sstevel@tonic-gate 			 */
1082*7c478bd9Sstevel@tonic-gate 			if (!OA_WRITE_OR_EXECUTE(optd, cr)) {
1083*7c478bd9Sstevel@tonic-gate 				opt->status = T_NOTSUPPORT;
1084*7c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
1085*7c478bd9Sstevel@tonic-gate 				/* skip to next option */
1086*7c478bd9Sstevel@tonic-gate 				continue;
1087*7c478bd9Sstevel@tonic-gate 			}
1088*7c478bd9Sstevel@tonic-gate 			/*
1089*7c478bd9Sstevel@tonic-gate 			 * Option passes all checks. Make room for it in the
1090*7c478bd9Sstevel@tonic-gate 			 * ack and set success in status.
1091*7c478bd9Sstevel@tonic-gate 			 * Note: size stored in table does not include header
1092*7c478bd9Sstevel@tonic-gate 			 * length.
1093*7c478bd9Sstevel@tonic-gate 			 */
1094*7c478bd9Sstevel@tonic-gate 			opt->status = T_SUCCESS;
1095*7c478bd9Sstevel@tonic-gate 			*toa_lenp += sizeof (struct T_opthdr) +
1096*7c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_TOPT(optd->opdes_size);
1097*7c478bd9Sstevel@tonic-gate 			break;
1098*7c478bd9Sstevel@tonic-gate 
1099*7c478bd9Sstevel@tonic-gate 		default:
1100*7c478bd9Sstevel@tonic-gate 			return (TBADFLAG);
1101*7c478bd9Sstevel@tonic-gate 		}
1102*7c478bd9Sstevel@tonic-gate 	} /* for loop scanning input buffer */
1103*7c478bd9Sstevel@tonic-gate 
1104*7c478bd9Sstevel@tonic-gate 	return (0);		/* OK return */
1105*7c478bd9Sstevel@tonic-gate }
1106*7c478bd9Sstevel@tonic-gate 
1107*7c478bd9Sstevel@tonic-gate /*
1108*7c478bd9Sstevel@tonic-gate  * This routine makes another pass through the option buffer this
1109*7c478bd9Sstevel@tonic-gate  * time acting on the request based on "status" result in the
1110*7c478bd9Sstevel@tonic-gate  * first pass. It also performs "expansion" of T_ALLOPT into
1111*7c478bd9Sstevel@tonic-gate  * all options of a certain level and acts on each for this request.
1112*7c478bd9Sstevel@tonic-gate  */
1113*7c478bd9Sstevel@tonic-gate static t_scalar_t
1114*7c478bd9Sstevel@tonic-gate do_options_second_pass(queue_t *q, mblk_t *reqmp, mblk_t *ack_mp, cred_t *cr,
1115*7c478bd9Sstevel@tonic-gate     optdb_obj_t *dbobjp, mblk_t *first_mp, boolean_t is_restart,
1116*7c478bd9Sstevel@tonic-gate     boolean_t *queued_statusp)
1117*7c478bd9Sstevel@tonic-gate {
1118*7c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
1119*7c478bd9Sstevel@tonic-gate 	int failed_option;
1120*7c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt;
1121*7c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt_start, *opt_end, *restart_opt;
1122*7c478bd9Sstevel@tonic-gate 	uchar_t *optr;
1123*7c478bd9Sstevel@tonic-gate 	uint_t optset_context;
1124*7c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor = (struct T_optmgmt_req *)reqmp->b_rptr;
1125*7c478bd9Sstevel@tonic-gate 	opt_restart_t	*or;
1126*7c478bd9Sstevel@tonic-gate 	t_uscalar_t	*worst_statusp;
1127*7c478bd9Sstevel@tonic-gate 	int	err;
1128*7c478bd9Sstevel@tonic-gate 
1129*7c478bd9Sstevel@tonic-gate 	*queued_statusp = B_FALSE;
1130*7c478bd9Sstevel@tonic-gate 	or = (opt_restart_t *)first_mp->b_rptr;
1131*7c478bd9Sstevel@tonic-gate 	worst_statusp = &or->or_worst_status;
1132*7c478bd9Sstevel@tonic-gate 
1133*7c478bd9Sstevel@tonic-gate 	optr = (uchar_t *)ack_mp->b_rptr +
1134*7c478bd9Sstevel@tonic-gate 	    sizeof (struct T_optmgmt_ack); /* assumed int32_t aligned */
1135*7c478bd9Sstevel@tonic-gate 
1136*7c478bd9Sstevel@tonic-gate 	/*
1137*7c478bd9Sstevel@tonic-gate 	 * Set initial values for scanning input
1138*7c478bd9Sstevel@tonic-gate 	 */
1139*7c478bd9Sstevel@tonic-gate 	if (is_restart) {
1140*7c478bd9Sstevel@tonic-gate 		opt_start = (struct T_opthdr *)or->or_start;
1141*7c478bd9Sstevel@tonic-gate 		opt_end = (struct T_opthdr *)or->or_end;
1142*7c478bd9Sstevel@tonic-gate 		restart_opt = (struct T_opthdr *)or->or_ropt;
1143*7c478bd9Sstevel@tonic-gate 	} else {
1144*7c478bd9Sstevel@tonic-gate 		opt_start = (struct T_opthdr *)mi_offset_param(reqmp,
1145*7c478bd9Sstevel@tonic-gate 		    tor->OPT_offset, tor->OPT_length);
1146*7c478bd9Sstevel@tonic-gate 		if (opt_start == NULL)
1147*7c478bd9Sstevel@tonic-gate 			return (TBADOPT);
1148*7c478bd9Sstevel@tonic-gate 		opt_end = (struct T_opthdr *)((uchar_t *)opt_start +
1149*7c478bd9Sstevel@tonic-gate 		    tor->OPT_length);
1150*7c478bd9Sstevel@tonic-gate 		or->or_start = (struct opthdr *)opt_start;
1151*7c478bd9Sstevel@tonic-gate 		or->or_end = (struct opthdr *)opt_end;
1152*7c478bd9Sstevel@tonic-gate 		/*
1153*7c478bd9Sstevel@tonic-gate 		 * construct the mp chain, in case the setfn needs to
1154*7c478bd9Sstevel@tonic-gate 		 * queue this and restart option processing later on.
1155*7c478bd9Sstevel@tonic-gate 		 */
1156*7c478bd9Sstevel@tonic-gate 		first_mp->b_cont = ack_mp;
1157*7c478bd9Sstevel@tonic-gate 		ack_mp->b_cont = reqmp;
1158*7c478bd9Sstevel@tonic-gate 	}
1159*7c478bd9Sstevel@tonic-gate 	ASSERT(__TPI_TOPT_ISALIGNED(opt_start)); /* verified in first pass */
1160*7c478bd9Sstevel@tonic-gate 
1161*7c478bd9Sstevel@tonic-gate 	for (opt = is_restart ? restart_opt : opt_start;
1162*7c478bd9Sstevel@tonic-gate 	    opt && (opt < opt_end);
1163*7c478bd9Sstevel@tonic-gate 	    opt = _TPI_TOPT_NEXTHDR(opt_start, tor->OPT_length, opt)) {
1164*7c478bd9Sstevel@tonic-gate 		or->or_ropt = (struct opthdr *)opt;
1165*7c478bd9Sstevel@tonic-gate 		/* verified in first pass */
1166*7c478bd9Sstevel@tonic-gate 		ASSERT(_TPI_TOPT_VALID(opt, opt_start, opt_end));
1167*7c478bd9Sstevel@tonic-gate 
1168*7c478bd9Sstevel@tonic-gate 		/*
1169*7c478bd9Sstevel@tonic-gate 		 * If the first pass in process_topthdrs_first_pass()
1170*7c478bd9Sstevel@tonic-gate 		 * has marked the option as a failure case for the MGMT_flags
1171*7c478bd9Sstevel@tonic-gate 		 * semantics then there is not much to do.
1172*7c478bd9Sstevel@tonic-gate 		 *
1173*7c478bd9Sstevel@tonic-gate 		 * Note: For all practical purposes, T_READONLY status is
1174*7c478bd9Sstevel@tonic-gate 		 * a "success" for T_DEFAULT/T_CURRENT and "failure" for
1175*7c478bd9Sstevel@tonic-gate 		 * T_CHECK/T_NEGOTIATE
1176*7c478bd9Sstevel@tonic-gate 		 */
1177*7c478bd9Sstevel@tonic-gate 		failed_option =
1178*7c478bd9Sstevel@tonic-gate 		    (opt->status == T_NOTSUPPORT) ||
1179*7c478bd9Sstevel@tonic-gate 		    (opt->status == T_FAILURE) ||
1180*7c478bd9Sstevel@tonic-gate 		    ((tor->MGMT_flags & (T_NEGOTIATE|T_CHECK)) &&
1181*7c478bd9Sstevel@tonic-gate 			(opt->status == T_READONLY));
1182*7c478bd9Sstevel@tonic-gate 
1183*7c478bd9Sstevel@tonic-gate 		if (failed_option) {
1184*7c478bd9Sstevel@tonic-gate 			/*
1185*7c478bd9Sstevel@tonic-gate 			 * According to T_DEFAULT/T_CURRENT semantics, the
1186*7c478bd9Sstevel@tonic-gate 			 * input values, even if present, are to be ignored.
1187*7c478bd9Sstevel@tonic-gate 			 * Note: Specification is not clear on this, but we
1188*7c478bd9Sstevel@tonic-gate 			 * interpret that even though we ignore the values, we
1189*7c478bd9Sstevel@tonic-gate 			 * can return them as is. So we process them similar to
1190*7c478bd9Sstevel@tonic-gate 			 * T_CHECK/T_NEGOTIATE case which has the semantics to
1191*7c478bd9Sstevel@tonic-gate 			 * return the values as is. XXX If interpretation is
1192*7c478bd9Sstevel@tonic-gate 			 * ever determined incorrect fill in appropriate code
1193*7c478bd9Sstevel@tonic-gate 			 * here to treat T_DEFAULT/T_CURRENT differently.
1194*7c478bd9Sstevel@tonic-gate 			 *
1195*7c478bd9Sstevel@tonic-gate 			 * According to T_CHECK/T_NEGOTIATE semantics,
1196*7c478bd9Sstevel@tonic-gate 			 * in the case of T_NOTSUPPORT/T_FAILURE/T_READONLY,
1197*7c478bd9Sstevel@tonic-gate 			 * the semantics are to return the "value" part of
1198*7c478bd9Sstevel@tonic-gate 			 * option untouched. So here we copy the option
1199*7c478bd9Sstevel@tonic-gate 			 * head including value part if any to output.
1200*7c478bd9Sstevel@tonic-gate 			 */
1201*7c478bd9Sstevel@tonic-gate 
1202*7c478bd9Sstevel@tonic-gate 			bcopy(opt, optr, opt->len);
1203*7c478bd9Sstevel@tonic-gate 			optr += _TPI_ALIGN_TOPT(opt->len);
1204*7c478bd9Sstevel@tonic-gate 
1205*7c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(opt->status,
1206*7c478bd9Sstevel@tonic-gate 			    *worst_statusp);
1207*7c478bd9Sstevel@tonic-gate 
1208*7c478bd9Sstevel@tonic-gate 			/* skip to process next option in buffer */
1209*7c478bd9Sstevel@tonic-gate 			continue;
1210*7c478bd9Sstevel@tonic-gate 
1211*7c478bd9Sstevel@tonic-gate 		} /* end if "failed option" */
1212*7c478bd9Sstevel@tonic-gate 		/*
1213*7c478bd9Sstevel@tonic-gate 		 * The status is T_SUCCESS or T_READONLY
1214*7c478bd9Sstevel@tonic-gate 		 * We process the value part here
1215*7c478bd9Sstevel@tonic-gate 		 */
1216*7c478bd9Sstevel@tonic-gate 		ASSERT(opt->status == T_SUCCESS || opt->status == T_READONLY);
1217*7c478bd9Sstevel@tonic-gate 		switch (tor->MGMT_flags) {
1218*7c478bd9Sstevel@tonic-gate 		case T_DEFAULT:
1219*7c478bd9Sstevel@tonic-gate 			/*
1220*7c478bd9Sstevel@tonic-gate 			 * We fill default value from table or protocol specific
1221*7c478bd9Sstevel@tonic-gate 			 * function. If this call fails, we pass input through.
1222*7c478bd9Sstevel@tonic-gate 			 */
1223*7c478bd9Sstevel@tonic-gate 			if (do_opt_default(q, opt, &optr, worst_statusp,
1224*7c478bd9Sstevel@tonic-gate 			    cr, dbobjp) < 0) {
1225*7c478bd9Sstevel@tonic-gate 				/* fail or pass transparently */
1226*7c478bd9Sstevel@tonic-gate 				if (topmost_tpiprovider)
1227*7c478bd9Sstevel@tonic-gate 					opt->status = T_FAILURE;
1228*7c478bd9Sstevel@tonic-gate 				bcopy(opt, optr, opt->len);
1229*7c478bd9Sstevel@tonic-gate 				optr += _TPI_ALIGN_TOPT(opt->len);
1230*7c478bd9Sstevel@tonic-gate 				*worst_statusp = get_worst_status(opt->status,
1231*7c478bd9Sstevel@tonic-gate 				    *worst_statusp);
1232*7c478bd9Sstevel@tonic-gate 			}
1233*7c478bd9Sstevel@tonic-gate 			break;
1234*7c478bd9Sstevel@tonic-gate 
1235*7c478bd9Sstevel@tonic-gate 		case T_CURRENT:
1236*7c478bd9Sstevel@tonic-gate 
1237*7c478bd9Sstevel@tonic-gate 			do_opt_current(q, opt, &optr, worst_statusp, cr,
1238*7c478bd9Sstevel@tonic-gate 			    dbobjp);
1239*7c478bd9Sstevel@tonic-gate 			break;
1240*7c478bd9Sstevel@tonic-gate 
1241*7c478bd9Sstevel@tonic-gate 		case T_CHECK:
1242*7c478bd9Sstevel@tonic-gate 		case T_NEGOTIATE:
1243*7c478bd9Sstevel@tonic-gate 			if (tor->MGMT_flags == T_CHECK)
1244*7c478bd9Sstevel@tonic-gate 				optset_context = SETFN_OPTCOM_CHECKONLY;
1245*7c478bd9Sstevel@tonic-gate 			else	/* T_NEGOTIATE */
1246*7c478bd9Sstevel@tonic-gate 				optset_context = SETFN_OPTCOM_NEGOTIATE;
1247*7c478bd9Sstevel@tonic-gate 			err = do_opt_check_or_negotiate(q, opt, optset_context,
1248*7c478bd9Sstevel@tonic-gate 			    &optr, worst_statusp, cr, dbobjp, first_mp);
1249*7c478bd9Sstevel@tonic-gate 			if (err == EINPROGRESS) {
1250*7c478bd9Sstevel@tonic-gate 				*queued_statusp = B_TRUE;
1251*7c478bd9Sstevel@tonic-gate 				return (0);
1252*7c478bd9Sstevel@tonic-gate 			}
1253*7c478bd9Sstevel@tonic-gate 			break;
1254*7c478bd9Sstevel@tonic-gate 		default:
1255*7c478bd9Sstevel@tonic-gate 			return (TBADFLAG);
1256*7c478bd9Sstevel@tonic-gate 		}
1257*7c478bd9Sstevel@tonic-gate 	} /* end for loop scanning option buffer */
1258*7c478bd9Sstevel@tonic-gate 
1259*7c478bd9Sstevel@tonic-gate 	ack_mp->b_wptr = optr;
1260*7c478bd9Sstevel@tonic-gate 	ASSERT(ack_mp->b_wptr <= ack_mp->b_datap->db_lim);
1261*7c478bd9Sstevel@tonic-gate 
1262*7c478bd9Sstevel@tonic-gate 	return (0);		/* OK return */
1263*7c478bd9Sstevel@tonic-gate }
1264*7c478bd9Sstevel@tonic-gate 
1265*7c478bd9Sstevel@tonic-gate 
1266*7c478bd9Sstevel@tonic-gate static t_uscalar_t
1267*7c478bd9Sstevel@tonic-gate get_worst_status(t_uscalar_t status, t_uscalar_t current_worst_status)
1268*7c478bd9Sstevel@tonic-gate {
1269*7c478bd9Sstevel@tonic-gate 	/*
1270*7c478bd9Sstevel@tonic-gate 	 * Return the "worst" among the arguments "status" and
1271*7c478bd9Sstevel@tonic-gate 	 * "current_worst_status".
1272*7c478bd9Sstevel@tonic-gate 	 *
1273*7c478bd9Sstevel@tonic-gate 	 * Note: Tracking "worst_status" can be made a bit simpler
1274*7c478bd9Sstevel@tonic-gate 	 * if we use the property that status codes are bitwise
1275*7c478bd9Sstevel@tonic-gate 	 * distinct.
1276*7c478bd9Sstevel@tonic-gate 	 *
1277*7c478bd9Sstevel@tonic-gate 	 * The pecking order is
1278*7c478bd9Sstevel@tonic-gate 	 *
1279*7c478bd9Sstevel@tonic-gate 	 * T_SUCCESS ..... best
1280*7c478bd9Sstevel@tonic-gate 	 * T_PARTSUCCESS
1281*7c478bd9Sstevel@tonic-gate 	 * T_FAILURE
1282*7c478bd9Sstevel@tonic-gate 	 * T_READONLY
1283*7c478bd9Sstevel@tonic-gate 	 * T_NOTSUPPORT... worst
1284*7c478bd9Sstevel@tonic-gate 	 */
1285*7c478bd9Sstevel@tonic-gate 	if (status == current_worst_status)
1286*7c478bd9Sstevel@tonic-gate 		return (current_worst_status);
1287*7c478bd9Sstevel@tonic-gate 	switch (current_worst_status) {
1288*7c478bd9Sstevel@tonic-gate 	case T_SUCCESS:
1289*7c478bd9Sstevel@tonic-gate 		if (status == T_PARTSUCCESS)
1290*7c478bd9Sstevel@tonic-gate 			return (T_PARTSUCCESS);
1291*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1292*7c478bd9Sstevel@tonic-gate 	case T_PARTSUCCESS:
1293*7c478bd9Sstevel@tonic-gate 		if (status == T_FAILURE)
1294*7c478bd9Sstevel@tonic-gate 			return (T_FAILURE);
1295*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1296*7c478bd9Sstevel@tonic-gate 	case T_FAILURE:
1297*7c478bd9Sstevel@tonic-gate 		if (status == T_READONLY)
1298*7c478bd9Sstevel@tonic-gate 			return (T_READONLY);
1299*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1300*7c478bd9Sstevel@tonic-gate 	case T_READONLY:
1301*7c478bd9Sstevel@tonic-gate 		if (status == T_NOTSUPPORT)
1302*7c478bd9Sstevel@tonic-gate 			return (T_NOTSUPPORT);
1303*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1304*7c478bd9Sstevel@tonic-gate 	case T_NOTSUPPORT:
1305*7c478bd9Sstevel@tonic-gate 	default:
1306*7c478bd9Sstevel@tonic-gate 		return (current_worst_status);
1307*7c478bd9Sstevel@tonic-gate 	}
1308*7c478bd9Sstevel@tonic-gate }
1309*7c478bd9Sstevel@tonic-gate 
1310*7c478bd9Sstevel@tonic-gate static int
1311*7c478bd9Sstevel@tonic-gate do_opt_default(queue_t *q, struct T_opthdr *reqopt, uchar_t **resptrp,
1312*7c478bd9Sstevel@tonic-gate     t_uscalar_t *worst_statusp, cred_t *cr, optdb_obj_t *dbobjp)
1313*7c478bd9Sstevel@tonic-gate {
1314*7c478bd9Sstevel@tonic-gate 	pfi_t	deffn = dbobjp->odb_deffn;
1315*7c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
1316*7c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
1317*7c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
1318*7c478bd9Sstevel@tonic-gate 
1319*7c478bd9Sstevel@tonic-gate 	struct T_opthdr *topth;
1320*7c478bd9Sstevel@tonic-gate 	opdes_t *optd;
1321*7c478bd9Sstevel@tonic-gate 
1322*7c478bd9Sstevel@tonic-gate 	if (reqopt->name != T_ALLOPT) {
1323*7c478bd9Sstevel@tonic-gate 		/*
1324*7c478bd9Sstevel@tonic-gate 		 * lookup the option in the table and fill default value
1325*7c478bd9Sstevel@tonic-gate 		 */
1326*7c478bd9Sstevel@tonic-gate 		optd = opt_chk_lookup(reqopt->level, reqopt->name,
1327*7c478bd9Sstevel@tonic-gate 		    opt_arr, opt_arr_cnt);
1328*7c478bd9Sstevel@tonic-gate 
1329*7c478bd9Sstevel@tonic-gate 		if (optd == NULL) {
1330*7c478bd9Sstevel@tonic-gate 			/*
1331*7c478bd9Sstevel@tonic-gate 			 * not found - fail this one. Should not happen
1332*7c478bd9Sstevel@tonic-gate 			 * for topmost_tpiprovider as calling routine
1333*7c478bd9Sstevel@tonic-gate 			 * should have verified it.
1334*7c478bd9Sstevel@tonic-gate 			 */
1335*7c478bd9Sstevel@tonic-gate 			ASSERT(!topmost_tpiprovider);
1336*7c478bd9Sstevel@tonic-gate 			return (-1);
1337*7c478bd9Sstevel@tonic-gate 		}
1338*7c478bd9Sstevel@tonic-gate 
1339*7c478bd9Sstevel@tonic-gate 		topth = (struct T_opthdr *)(*resptrp);
1340*7c478bd9Sstevel@tonic-gate 		topth->level = reqopt->level;
1341*7c478bd9Sstevel@tonic-gate 		topth->name = reqopt->name;
1342*7c478bd9Sstevel@tonic-gate 		topth->status = reqopt->status;
1343*7c478bd9Sstevel@tonic-gate 
1344*7c478bd9Sstevel@tonic-gate 		*worst_statusp = get_worst_status(reqopt->status,
1345*7c478bd9Sstevel@tonic-gate 		    *worst_statusp);
1346*7c478bd9Sstevel@tonic-gate 
1347*7c478bd9Sstevel@tonic-gate 		if (optd->opdes_props & OP_NODEFAULT) {
1348*7c478bd9Sstevel@tonic-gate 			/* header only, no default "value" part */
1349*7c478bd9Sstevel@tonic-gate 			topth->len = sizeof (struct T_opthdr);
1350*7c478bd9Sstevel@tonic-gate 			*resptrp += sizeof (struct T_opthdr);
1351*7c478bd9Sstevel@tonic-gate 		} else {
1352*7c478bd9Sstevel@tonic-gate 			int deflen;
1353*7c478bd9Sstevel@tonic-gate 
1354*7c478bd9Sstevel@tonic-gate 			if (optd->opdes_props & OP_DEF_FN) {
1355*7c478bd9Sstevel@tonic-gate 				deflen = (*deffn)(q, reqopt->level,
1356*7c478bd9Sstevel@tonic-gate 				    reqopt->name, _TPI_TOPT_DATA(topth));
1357*7c478bd9Sstevel@tonic-gate 				if (deflen >= 0) {
1358*7c478bd9Sstevel@tonic-gate 					topth->len = (t_uscalar_t)
1359*7c478bd9Sstevel@tonic-gate 					    (sizeof (struct T_opthdr) + deflen);
1360*7c478bd9Sstevel@tonic-gate 				} else {
1361*7c478bd9Sstevel@tonic-gate 					/*
1362*7c478bd9Sstevel@tonic-gate 					 * return error, this should 'pass
1363*7c478bd9Sstevel@tonic-gate 					 * through' the option and maybe some
1364*7c478bd9Sstevel@tonic-gate 					 * other level will fill it in or
1365*7c478bd9Sstevel@tonic-gate 					 * already did.
1366*7c478bd9Sstevel@tonic-gate 					 * (No change in 'resptrp' upto here)
1367*7c478bd9Sstevel@tonic-gate 					 */
1368*7c478bd9Sstevel@tonic-gate 					return (-1);
1369*7c478bd9Sstevel@tonic-gate 				}
1370*7c478bd9Sstevel@tonic-gate 			} else {
1371*7c478bd9Sstevel@tonic-gate 				/* fill length and value part */
1372*7c478bd9Sstevel@tonic-gate 				switch (optd->opdes_size) {
1373*7c478bd9Sstevel@tonic-gate 				/*
1374*7c478bd9Sstevel@tonic-gate 				 * Since options are guaranteed aligned only
1375*7c478bd9Sstevel@tonic-gate 				 * on a 4 byte boundary (t_scalar_t) any
1376*7c478bd9Sstevel@tonic-gate 				 * option that is greater in size will default
1377*7c478bd9Sstevel@tonic-gate 				 * to the bcopy below
1378*7c478bd9Sstevel@tonic-gate 				 */
1379*7c478bd9Sstevel@tonic-gate 				case sizeof (int32_t):
1380*7c478bd9Sstevel@tonic-gate 					*(int32_t *)_TPI_TOPT_DATA(topth) =
1381*7c478bd9Sstevel@tonic-gate 					    (int32_t)optd->opdes_default;
1382*7c478bd9Sstevel@tonic-gate 					break;
1383*7c478bd9Sstevel@tonic-gate 				case sizeof (int16_t):
1384*7c478bd9Sstevel@tonic-gate 					*(int16_t *)_TPI_TOPT_DATA(topth) =
1385*7c478bd9Sstevel@tonic-gate 					    (int16_t)optd->opdes_default;
1386*7c478bd9Sstevel@tonic-gate 					break;
1387*7c478bd9Sstevel@tonic-gate 				case sizeof (int8_t):
1388*7c478bd9Sstevel@tonic-gate 					*(int8_t *)_TPI_TOPT_DATA(topth) =
1389*7c478bd9Sstevel@tonic-gate 					    (int8_t)optd->opdes_default;
1390*7c478bd9Sstevel@tonic-gate 					break;
1391*7c478bd9Sstevel@tonic-gate 				default:
1392*7c478bd9Sstevel@tonic-gate 					/*
1393*7c478bd9Sstevel@tonic-gate 					 * other length but still assume
1394*7c478bd9Sstevel@tonic-gate 					 * fixed - use bcopy
1395*7c478bd9Sstevel@tonic-gate 					 */
1396*7c478bd9Sstevel@tonic-gate 					bcopy(optd->opdes_defbuf,
1397*7c478bd9Sstevel@tonic-gate 					    _TPI_TOPT_DATA(topth),
1398*7c478bd9Sstevel@tonic-gate 					    optd->opdes_size);
1399*7c478bd9Sstevel@tonic-gate 					break;
1400*7c478bd9Sstevel@tonic-gate 				}
1401*7c478bd9Sstevel@tonic-gate 				topth->len = (t_uscalar_t)(optd->opdes_size +
1402*7c478bd9Sstevel@tonic-gate 				    sizeof (struct T_opthdr));
1403*7c478bd9Sstevel@tonic-gate 			}
1404*7c478bd9Sstevel@tonic-gate 			*resptrp += _TPI_ALIGN_TOPT(topth->len);
1405*7c478bd9Sstevel@tonic-gate 		}
1406*7c478bd9Sstevel@tonic-gate 		return (0);	/* OK return */
1407*7c478bd9Sstevel@tonic-gate 	}
1408*7c478bd9Sstevel@tonic-gate 
1409*7c478bd9Sstevel@tonic-gate 	/*
1410*7c478bd9Sstevel@tonic-gate 	 * T_ALLOPT processing
1411*7c478bd9Sstevel@tonic-gate 	 *
1412*7c478bd9Sstevel@tonic-gate 	 * lookup and stuff default values of all the options of the
1413*7c478bd9Sstevel@tonic-gate 	 * level specified
1414*7c478bd9Sstevel@tonic-gate 	 * Note: This expansion of T_ALLOPT should happen in
1415*7c478bd9Sstevel@tonic-gate 	 * a topmost_tpiprovider.
1416*7c478bd9Sstevel@tonic-gate 	 */
1417*7c478bd9Sstevel@tonic-gate 	ASSERT(topmost_tpiprovider);
1418*7c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
1419*7c478bd9Sstevel@tonic-gate 		if (reqopt->level != optd->opdes_level)
1420*7c478bd9Sstevel@tonic-gate 			continue;
1421*7c478bd9Sstevel@tonic-gate 		/*
1422*7c478bd9Sstevel@tonic-gate 		 *
1423*7c478bd9Sstevel@tonic-gate 		 * T_DEFAULT semantics:
1424*7c478bd9Sstevel@tonic-gate 		 * XXX: we interpret T_DEFAULT semantics such that access to
1425*7c478bd9Sstevel@tonic-gate 		 * read value is required for access even the default value.
1426*7c478bd9Sstevel@tonic-gate 		 * Else option is ignored for T_ALLOPT request.
1427*7c478bd9Sstevel@tonic-gate 		 */
1428*7c478bd9Sstevel@tonic-gate 		if (!OA_READ_PERMISSION(optd, cr))
1429*7c478bd9Sstevel@tonic-gate 			/* skip this one */
1430*7c478bd9Sstevel@tonic-gate 			continue;
1431*7c478bd9Sstevel@tonic-gate 
1432*7c478bd9Sstevel@tonic-gate 		/*
1433*7c478bd9Sstevel@tonic-gate 		 * Found option of same level as T_ALLOPT request
1434*7c478bd9Sstevel@tonic-gate 		 * that we can return.
1435*7c478bd9Sstevel@tonic-gate 		 */
1436*7c478bd9Sstevel@tonic-gate 
1437*7c478bd9Sstevel@tonic-gate 		topth = (struct T_opthdr *)(*resptrp);
1438*7c478bd9Sstevel@tonic-gate 		topth->level = optd->opdes_level;
1439*7c478bd9Sstevel@tonic-gate 		topth->name = optd->opdes_name;
1440*7c478bd9Sstevel@tonic-gate 
1441*7c478bd9Sstevel@tonic-gate 		/*
1442*7c478bd9Sstevel@tonic-gate 		 * T_DEFAULT semantics:
1443*7c478bd9Sstevel@tonic-gate 		 * We know that read access is set. If no other access is set,
1444*7c478bd9Sstevel@tonic-gate 		 * then status is T_READONLY
1445*7c478bd9Sstevel@tonic-gate 		 */
1446*7c478bd9Sstevel@tonic-gate 		if (OA_READONLY_PERMISSION(optd, cr)) {
1447*7c478bd9Sstevel@tonic-gate 			topth->status = T_READONLY;
1448*7c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(T_READONLY,
1449*7c478bd9Sstevel@tonic-gate 			    *worst_statusp);
1450*7c478bd9Sstevel@tonic-gate 		} else {
1451*7c478bd9Sstevel@tonic-gate 			topth->status = T_SUCCESS;
1452*7c478bd9Sstevel@tonic-gate 			/*
1453*7c478bd9Sstevel@tonic-gate 			 * Note: *worst_statusp has to be T_SUCCESS or
1454*7c478bd9Sstevel@tonic-gate 			 * worse so no need to adjust
1455*7c478bd9Sstevel@tonic-gate 			 */
1456*7c478bd9Sstevel@tonic-gate 		}
1457*7c478bd9Sstevel@tonic-gate 
1458*7c478bd9Sstevel@tonic-gate 		if (optd->opdes_props & OP_NODEFAULT) {
1459*7c478bd9Sstevel@tonic-gate 			/* header only, no value part */
1460*7c478bd9Sstevel@tonic-gate 			topth->len = sizeof (struct T_opthdr);
1461*7c478bd9Sstevel@tonic-gate 			*resptrp += sizeof (struct T_opthdr);
1462*7c478bd9Sstevel@tonic-gate 		} else {
1463*7c478bd9Sstevel@tonic-gate 			int deflen;
1464*7c478bd9Sstevel@tonic-gate 
1465*7c478bd9Sstevel@tonic-gate 			if (optd->opdes_props & OP_DEF_FN) {
1466*7c478bd9Sstevel@tonic-gate 				deflen = (*deffn)(q, reqopt->level,
1467*7c478bd9Sstevel@tonic-gate 				    reqopt->name, _TPI_TOPT_DATA(topth));
1468*7c478bd9Sstevel@tonic-gate 				if (deflen >= 0) {
1469*7c478bd9Sstevel@tonic-gate 					topth->len = (t_uscalar_t)(deflen +
1470*7c478bd9Sstevel@tonic-gate 					    sizeof (struct T_opthdr));
1471*7c478bd9Sstevel@tonic-gate 				} else {
1472*7c478bd9Sstevel@tonic-gate 					/*
1473*7c478bd9Sstevel@tonic-gate 					 * deffn failed.
1474*7c478bd9Sstevel@tonic-gate 					 * return just the header as T_ALLOPT
1475*7c478bd9Sstevel@tonic-gate 					 * expansion.
1476*7c478bd9Sstevel@tonic-gate 					 * Some other level deffn may
1477*7c478bd9Sstevel@tonic-gate 					 * supply value part.
1478*7c478bd9Sstevel@tonic-gate 					 */
1479*7c478bd9Sstevel@tonic-gate 					topth->len = sizeof (struct T_opthdr);
1480*7c478bd9Sstevel@tonic-gate 					topth->status = T_FAILURE;
1481*7c478bd9Sstevel@tonic-gate 					*worst_statusp =
1482*7c478bd9Sstevel@tonic-gate 					    get_worst_status(T_FAILURE,
1483*7c478bd9Sstevel@tonic-gate 						*worst_statusp);
1484*7c478bd9Sstevel@tonic-gate 				}
1485*7c478bd9Sstevel@tonic-gate 			} else {
1486*7c478bd9Sstevel@tonic-gate 				/*
1487*7c478bd9Sstevel@tonic-gate 				 * fill length and value part from
1488*7c478bd9Sstevel@tonic-gate 				 * table
1489*7c478bd9Sstevel@tonic-gate 				 */
1490*7c478bd9Sstevel@tonic-gate 				switch (optd->opdes_size) {
1491*7c478bd9Sstevel@tonic-gate 				/*
1492*7c478bd9Sstevel@tonic-gate 				 * Since options are guaranteed aligned only
1493*7c478bd9Sstevel@tonic-gate 				 * on a 4 byte boundary (t_scalar_t) any
1494*7c478bd9Sstevel@tonic-gate 				 * option that is greater in size will default
1495*7c478bd9Sstevel@tonic-gate 				 * to the bcopy below
1496*7c478bd9Sstevel@tonic-gate 				 */
1497*7c478bd9Sstevel@tonic-gate 				case sizeof (int32_t):
1498*7c478bd9Sstevel@tonic-gate 					*(int32_t *)_TPI_TOPT_DATA(topth) =
1499*7c478bd9Sstevel@tonic-gate 					    (int32_t)optd->opdes_default;
1500*7c478bd9Sstevel@tonic-gate 					break;
1501*7c478bd9Sstevel@tonic-gate 				case sizeof (int16_t):
1502*7c478bd9Sstevel@tonic-gate 					*(int16_t *)_TPI_TOPT_DATA(topth) =
1503*7c478bd9Sstevel@tonic-gate 					    (int16_t)optd->opdes_default;
1504*7c478bd9Sstevel@tonic-gate 					break;
1505*7c478bd9Sstevel@tonic-gate 				case sizeof (int8_t):
1506*7c478bd9Sstevel@tonic-gate 					*(int8_t *)_TPI_TOPT_DATA(topth) =
1507*7c478bd9Sstevel@tonic-gate 					    (int8_t)optd->opdes_default;
1508*7c478bd9Sstevel@tonic-gate 					break;
1509*7c478bd9Sstevel@tonic-gate 				default:
1510*7c478bd9Sstevel@tonic-gate 					/*
1511*7c478bd9Sstevel@tonic-gate 					 * other length but still assume
1512*7c478bd9Sstevel@tonic-gate 					 * fixed - use bcopy
1513*7c478bd9Sstevel@tonic-gate 					 */
1514*7c478bd9Sstevel@tonic-gate 					bcopy(optd->opdes_defbuf,
1515*7c478bd9Sstevel@tonic-gate 					    _TPI_TOPT_DATA(topth),
1516*7c478bd9Sstevel@tonic-gate 					    optd->opdes_size);
1517*7c478bd9Sstevel@tonic-gate 				}
1518*7c478bd9Sstevel@tonic-gate 				topth->len = (t_uscalar_t)(optd->opdes_size +
1519*7c478bd9Sstevel@tonic-gate 				    sizeof (struct T_opthdr));
1520*7c478bd9Sstevel@tonic-gate 			}
1521*7c478bd9Sstevel@tonic-gate 			*resptrp += _TPI_ALIGN_TOPT(topth->len);
1522*7c478bd9Sstevel@tonic-gate 		}
1523*7c478bd9Sstevel@tonic-gate 	}
1524*7c478bd9Sstevel@tonic-gate 	return (0);
1525*7c478bd9Sstevel@tonic-gate }
1526*7c478bd9Sstevel@tonic-gate 
1527*7c478bd9Sstevel@tonic-gate static void
1528*7c478bd9Sstevel@tonic-gate do_opt_current(queue_t *q, struct T_opthdr *reqopt, uchar_t **resptrp,
1529*7c478bd9Sstevel@tonic-gate     t_uscalar_t *worst_statusp, cred_t *cr, optdb_obj_t *dbobjp)
1530*7c478bd9Sstevel@tonic-gate {
1531*7c478bd9Sstevel@tonic-gate 	pfi_t	getfn = dbobjp->odb_getfn;
1532*7c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
1533*7c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
1534*7c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
1535*7c478bd9Sstevel@tonic-gate 
1536*7c478bd9Sstevel@tonic-gate 	struct T_opthdr *topth;
1537*7c478bd9Sstevel@tonic-gate 	opdes_t *optd;
1538*7c478bd9Sstevel@tonic-gate 	int optlen;
1539*7c478bd9Sstevel@tonic-gate 	uchar_t *initptr = *resptrp;
1540*7c478bd9Sstevel@tonic-gate 
1541*7c478bd9Sstevel@tonic-gate 	/*
1542*7c478bd9Sstevel@tonic-gate 	 * We call getfn to get the current value of an option. The call may
1543*7c478bd9Sstevel@tonic-gate 	 * fail in which case we copy the values from the input buffer. Maybe
1544*7c478bd9Sstevel@tonic-gate 	 * something downstream will fill it in or something upstream did.
1545*7c478bd9Sstevel@tonic-gate 	 */
1546*7c478bd9Sstevel@tonic-gate 
1547*7c478bd9Sstevel@tonic-gate 	if (reqopt->name != T_ALLOPT) {
1548*7c478bd9Sstevel@tonic-gate 		topth = (struct T_opthdr *)*resptrp;
1549*7c478bd9Sstevel@tonic-gate 		*resptrp += sizeof (struct T_opthdr);
1550*7c478bd9Sstevel@tonic-gate 		optlen = (*getfn)(q, reqopt->level, reqopt->name, *resptrp);
1551*7c478bd9Sstevel@tonic-gate 		if (optlen >= 0) {
1552*7c478bd9Sstevel@tonic-gate 			topth->len = (t_uscalar_t)(optlen +
1553*7c478bd9Sstevel@tonic-gate 			    sizeof (struct T_opthdr));
1554*7c478bd9Sstevel@tonic-gate 			topth->level = reqopt->level;
1555*7c478bd9Sstevel@tonic-gate 			topth->name = reqopt->name;
1556*7c478bd9Sstevel@tonic-gate 			topth->status = reqopt->status;
1557*7c478bd9Sstevel@tonic-gate 			*resptrp += _TPI_ALIGN_TOPT(optlen);
1558*7c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(topth->status,
1559*7c478bd9Sstevel@tonic-gate 			    *worst_statusp);
1560*7c478bd9Sstevel@tonic-gate 		} else {
1561*7c478bd9Sstevel@tonic-gate 			/* failed - reset "*resptrp" pointer */
1562*7c478bd9Sstevel@tonic-gate 			*resptrp -= sizeof (struct T_opthdr);
1563*7c478bd9Sstevel@tonic-gate 		}
1564*7c478bd9Sstevel@tonic-gate 	} else {		/* T_ALLOPT processing */
1565*7c478bd9Sstevel@tonic-gate 		ASSERT(topmost_tpiprovider == B_TRUE);
1566*7c478bd9Sstevel@tonic-gate 		/* scan and get all options */
1567*7c478bd9Sstevel@tonic-gate 		for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
1568*7c478bd9Sstevel@tonic-gate 			/* skip other levels */
1569*7c478bd9Sstevel@tonic-gate 			if (reqopt->level != optd->opdes_level)
1570*7c478bd9Sstevel@tonic-gate 				continue;
1571*7c478bd9Sstevel@tonic-gate 
1572*7c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr))
1573*7c478bd9Sstevel@tonic-gate 				/* skip this one */
1574*7c478bd9Sstevel@tonic-gate 				continue;
1575*7c478bd9Sstevel@tonic-gate 
1576*7c478bd9Sstevel@tonic-gate 			topth = (struct T_opthdr *)*resptrp;
1577*7c478bd9Sstevel@tonic-gate 			*resptrp += sizeof (struct T_opthdr);
1578*7c478bd9Sstevel@tonic-gate 
1579*7c478bd9Sstevel@tonic-gate 			/* get option of this level */
1580*7c478bd9Sstevel@tonic-gate 			optlen = (*getfn)(q, reqopt->level, optd->opdes_name,
1581*7c478bd9Sstevel@tonic-gate 			    *resptrp);
1582*7c478bd9Sstevel@tonic-gate 			if (optlen >= 0) {
1583*7c478bd9Sstevel@tonic-gate 				/* success */
1584*7c478bd9Sstevel@tonic-gate 				topth->len = (t_uscalar_t)(optlen +
1585*7c478bd9Sstevel@tonic-gate 				    sizeof (struct T_opthdr));
1586*7c478bd9Sstevel@tonic-gate 				topth->level = reqopt->level;
1587*7c478bd9Sstevel@tonic-gate 				topth->name = optd->opdes_name;
1588*7c478bd9Sstevel@tonic-gate 				if (OA_READONLY_PERMISSION(optd, cr))
1589*7c478bd9Sstevel@tonic-gate 					topth->status = T_READONLY;
1590*7c478bd9Sstevel@tonic-gate 				else
1591*7c478bd9Sstevel@tonic-gate 					topth->status = T_SUCCESS;
1592*7c478bd9Sstevel@tonic-gate 				*resptrp += _TPI_ALIGN_TOPT(optlen);
1593*7c478bd9Sstevel@tonic-gate 			} else {
1594*7c478bd9Sstevel@tonic-gate 				/*
1595*7c478bd9Sstevel@tonic-gate 				 * failed, return as T_FAILURE and null value
1596*7c478bd9Sstevel@tonic-gate 				 * part. Maybe something downstream will
1597*7c478bd9Sstevel@tonic-gate 				 * handle this one and fill in a value. Here
1598*7c478bd9Sstevel@tonic-gate 				 * it is just part of T_ALLOPT expansion.
1599*7c478bd9Sstevel@tonic-gate 				 */
1600*7c478bd9Sstevel@tonic-gate 				topth->len = sizeof (struct T_opthdr);
1601*7c478bd9Sstevel@tonic-gate 				topth->level = reqopt->level;
1602*7c478bd9Sstevel@tonic-gate 				topth->name = optd->opdes_name;
1603*7c478bd9Sstevel@tonic-gate 				topth->status = T_FAILURE;
1604*7c478bd9Sstevel@tonic-gate 			}
1605*7c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(topth->status,
1606*7c478bd9Sstevel@tonic-gate 			    *worst_statusp);
1607*7c478bd9Sstevel@tonic-gate 		} /* end for loop */
1608*7c478bd9Sstevel@tonic-gate 	}
1609*7c478bd9Sstevel@tonic-gate 	if (*resptrp == initptr) {
1610*7c478bd9Sstevel@tonic-gate 		/*
1611*7c478bd9Sstevel@tonic-gate 		 * getfn failed and does not want to handle this option. Maybe
1612*7c478bd9Sstevel@tonic-gate 		 * something downstream will or something upstream did. (If
1613*7c478bd9Sstevel@tonic-gate 		 * topmost_tpiprovider, initialize "status" to failure which
1614*7c478bd9Sstevel@tonic-gate 		 * can possibly change downstream). Copy the input "as is" from
1615*7c478bd9Sstevel@tonic-gate 		 * input option buffer if any to maintain transparency.
1616*7c478bd9Sstevel@tonic-gate 		 */
1617*7c478bd9Sstevel@tonic-gate 		if (topmost_tpiprovider)
1618*7c478bd9Sstevel@tonic-gate 			reqopt->status = T_FAILURE;
1619*7c478bd9Sstevel@tonic-gate 		bcopy(reqopt, *resptrp, reqopt->len);
1620*7c478bd9Sstevel@tonic-gate 		*resptrp += _TPI_ALIGN_TOPT(reqopt->len);
1621*7c478bd9Sstevel@tonic-gate 		*worst_statusp = get_worst_status(reqopt->status,
1622*7c478bd9Sstevel@tonic-gate 		    *worst_statusp);
1623*7c478bd9Sstevel@tonic-gate 	}
1624*7c478bd9Sstevel@tonic-gate }
1625*7c478bd9Sstevel@tonic-gate 
1626*7c478bd9Sstevel@tonic-gate 
1627*7c478bd9Sstevel@tonic-gate 
1628*7c478bd9Sstevel@tonic-gate static int
1629*7c478bd9Sstevel@tonic-gate do_opt_check_or_negotiate(queue_t *q, struct T_opthdr *reqopt,
1630*7c478bd9Sstevel@tonic-gate     uint_t optset_context, uchar_t **resptrp, t_uscalar_t *worst_statusp,
1631*7c478bd9Sstevel@tonic-gate     cred_t *cr, optdb_obj_t *dbobjp, mblk_t *first_mp)
1632*7c478bd9Sstevel@tonic-gate {
1633*7c478bd9Sstevel@tonic-gate 	pfi_t	deffn = dbobjp->odb_deffn;
1634*7c478bd9Sstevel@tonic-gate 	opt_set_fn setfn = dbobjp->odb_setfn;
1635*7c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
1636*7c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
1637*7c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
1638*7c478bd9Sstevel@tonic-gate 
1639*7c478bd9Sstevel@tonic-gate 	struct T_opthdr *topth;
1640*7c478bd9Sstevel@tonic-gate 	opdes_t *optd;
1641*7c478bd9Sstevel@tonic-gate 	int error;
1642*7c478bd9Sstevel@tonic-gate 	t_uscalar_t optlen;
1643*7c478bd9Sstevel@tonic-gate 	t_scalar_t optsize;
1644*7c478bd9Sstevel@tonic-gate 	uchar_t *initptr = *resptrp;
1645*7c478bd9Sstevel@tonic-gate 
1646*7c478bd9Sstevel@tonic-gate 	ASSERT(reqopt->status == T_SUCCESS);
1647*7c478bd9Sstevel@tonic-gate 
1648*7c478bd9Sstevel@tonic-gate 	if (reqopt->name != T_ALLOPT) {
1649*7c478bd9Sstevel@tonic-gate 		topth = (struct T_opthdr *)*resptrp;
1650*7c478bd9Sstevel@tonic-gate 		*resptrp += sizeof (struct T_opthdr);
1651*7c478bd9Sstevel@tonic-gate 		error = (*setfn)(q, optset_context, reqopt->level, reqopt->name,
1652*7c478bd9Sstevel@tonic-gate 		    reqopt->len - sizeof (struct T_opthdr),
1653*7c478bd9Sstevel@tonic-gate 		    _TPI_TOPT_DATA(reqopt), &optlen, _TPI_TOPT_DATA(topth),
1654*7c478bd9Sstevel@tonic-gate 		    NULL, cr, first_mp);
1655*7c478bd9Sstevel@tonic-gate 		if (error) {
1656*7c478bd9Sstevel@tonic-gate 			/* failed - reset "*resptrp" */
1657*7c478bd9Sstevel@tonic-gate 			*resptrp -= sizeof (struct T_opthdr);
1658*7c478bd9Sstevel@tonic-gate 			if (error == EINPROGRESS)
1659*7c478bd9Sstevel@tonic-gate 				return (error);
1660*7c478bd9Sstevel@tonic-gate 		} else {
1661*7c478bd9Sstevel@tonic-gate 			/*
1662*7c478bd9Sstevel@tonic-gate 			 * success - "value" already filled in setfn()
1663*7c478bd9Sstevel@tonic-gate 			 */
1664*7c478bd9Sstevel@tonic-gate 			topth->len = (t_uscalar_t)(optlen +
1665*7c478bd9Sstevel@tonic-gate 			    sizeof (struct T_opthdr));
1666*7c478bd9Sstevel@tonic-gate 			topth->level = reqopt->level;
1667*7c478bd9Sstevel@tonic-gate 			topth->name = reqopt->name;
1668*7c478bd9Sstevel@tonic-gate 			topth->status = reqopt->status;
1669*7c478bd9Sstevel@tonic-gate 			*resptrp += _TPI_ALIGN_TOPT(optlen);
1670*7c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(topth->status,
1671*7c478bd9Sstevel@tonic-gate 			    *worst_statusp);
1672*7c478bd9Sstevel@tonic-gate 		}
1673*7c478bd9Sstevel@tonic-gate 	} else {		/* T_ALLOPT processing */
1674*7c478bd9Sstevel@tonic-gate 		/* only for T_NEGOTIATE case */
1675*7c478bd9Sstevel@tonic-gate 		ASSERT(optset_context == SETFN_OPTCOM_NEGOTIATE);
1676*7c478bd9Sstevel@tonic-gate 		ASSERT(topmost_tpiprovider == B_TRUE);
1677*7c478bd9Sstevel@tonic-gate 
1678*7c478bd9Sstevel@tonic-gate 		/* scan and set all options to default value */
1679*7c478bd9Sstevel@tonic-gate 		for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
1680*7c478bd9Sstevel@tonic-gate 
1681*7c478bd9Sstevel@tonic-gate 			/* skip other levels */
1682*7c478bd9Sstevel@tonic-gate 			if (reqopt->level != optd->opdes_level)
1683*7c478bd9Sstevel@tonic-gate 				continue;
1684*7c478bd9Sstevel@tonic-gate 
1685*7c478bd9Sstevel@tonic-gate 			if (OA_EXECUTE_PERMISSION(optd, cr) ||
1686*7c478bd9Sstevel@tonic-gate 			    OA_NO_PERMISSION(optd, cr)) {
1687*7c478bd9Sstevel@tonic-gate 				/*
1688*7c478bd9Sstevel@tonic-gate 				 * skip this one too. Does not make sense to
1689*7c478bd9Sstevel@tonic-gate 				 * set anything to default value for "execute"
1690*7c478bd9Sstevel@tonic-gate 				 * options.
1691*7c478bd9Sstevel@tonic-gate 				 */
1692*7c478bd9Sstevel@tonic-gate 				continue;
1693*7c478bd9Sstevel@tonic-gate 			}
1694*7c478bd9Sstevel@tonic-gate 
1695*7c478bd9Sstevel@tonic-gate 			if (OA_READONLY_PERMISSION(optd, cr)) {
1696*7c478bd9Sstevel@tonic-gate 				/*
1697*7c478bd9Sstevel@tonic-gate 				 * Return with T_READONLY status (and no value
1698*7c478bd9Sstevel@tonic-gate 				 * part). Note: spec is not clear but
1699*7c478bd9Sstevel@tonic-gate 				 * XTI test suite needs this.
1700*7c478bd9Sstevel@tonic-gate 				 */
1701*7c478bd9Sstevel@tonic-gate 				topth = (struct T_opthdr *)*resptrp;
1702*7c478bd9Sstevel@tonic-gate 				topth->len = sizeof (struct T_opthdr);
1703*7c478bd9Sstevel@tonic-gate 				*resptrp += topth->len;
1704*7c478bd9Sstevel@tonic-gate 				topth->level = reqopt->level;
1705*7c478bd9Sstevel@tonic-gate 				topth->name = optd->opdes_name;
1706*7c478bd9Sstevel@tonic-gate 				topth->status = T_READONLY;
1707*7c478bd9Sstevel@tonic-gate 				*worst_statusp = get_worst_status(topth->status,
1708*7c478bd9Sstevel@tonic-gate 				    *worst_statusp);
1709*7c478bd9Sstevel@tonic-gate 				continue;
1710*7c478bd9Sstevel@tonic-gate 			}
1711*7c478bd9Sstevel@tonic-gate 
1712*7c478bd9Sstevel@tonic-gate 			/*
1713*7c478bd9Sstevel@tonic-gate 			 * It is not read only or execute type
1714*7c478bd9Sstevel@tonic-gate 			 * the it must have write permission
1715*7c478bd9Sstevel@tonic-gate 			 */
1716*7c478bd9Sstevel@tonic-gate 			ASSERT(OA_WRITE_PERMISSION(optd, cr));
1717*7c478bd9Sstevel@tonic-gate 
1718*7c478bd9Sstevel@tonic-gate 			topth = (struct T_opthdr *)*resptrp;
1719*7c478bd9Sstevel@tonic-gate 			*resptrp += sizeof (struct T_opthdr);
1720*7c478bd9Sstevel@tonic-gate 
1721*7c478bd9Sstevel@tonic-gate 			topth->len = sizeof (struct T_opthdr);
1722*7c478bd9Sstevel@tonic-gate 			topth->level = reqopt->level;
1723*7c478bd9Sstevel@tonic-gate 			topth->name = optd->opdes_name;
1724*7c478bd9Sstevel@tonic-gate 			if (optd->opdes_props & OP_NODEFAULT) {
1725*7c478bd9Sstevel@tonic-gate 				/*
1726*7c478bd9Sstevel@tonic-gate 				 * Option of "no default value" so it does not
1727*7c478bd9Sstevel@tonic-gate 				 * make sense to try to set it. We just return
1728*7c478bd9Sstevel@tonic-gate 				 * header with status of T_SUCCESS
1729*7c478bd9Sstevel@tonic-gate 				 * XXX should this be failure ?
1730*7c478bd9Sstevel@tonic-gate 				 */
1731*7c478bd9Sstevel@tonic-gate 				topth->status = T_SUCCESS;
1732*7c478bd9Sstevel@tonic-gate 				continue; /* skip setting */
1733*7c478bd9Sstevel@tonic-gate 			}
1734*7c478bd9Sstevel@tonic-gate 			if (optd->opdes_props & OP_DEF_FN) {
1735*7c478bd9Sstevel@tonic-gate 				if ((optd->opdes_props & OP_VARLEN) ||
1736*7c478bd9Sstevel@tonic-gate 				    ((optsize = (*deffn)(q, reqopt->level,
1737*7c478bd9Sstevel@tonic-gate 					optd->opdes_name,
1738*7c478bd9Sstevel@tonic-gate 					(uchar_t *)optd->opdes_defbuf)) < 0)) {
1739*7c478bd9Sstevel@tonic-gate 					/* XXX - skip these too */
1740*7c478bd9Sstevel@tonic-gate 					topth->status = T_SUCCESS;
1741*7c478bd9Sstevel@tonic-gate 					continue; /* skip setting */
1742*7c478bd9Sstevel@tonic-gate 				}
1743*7c478bd9Sstevel@tonic-gate 			} else {
1744*7c478bd9Sstevel@tonic-gate 				optsize = optd->opdes_size;
1745*7c478bd9Sstevel@tonic-gate 			}
1746*7c478bd9Sstevel@tonic-gate 
1747*7c478bd9Sstevel@tonic-gate 
1748*7c478bd9Sstevel@tonic-gate 			/* set option of this level */
1749*7c478bd9Sstevel@tonic-gate 			error = (*setfn)(q, SETFN_OPTCOM_NEGOTIATE,
1750*7c478bd9Sstevel@tonic-gate 			    reqopt->level, optd->opdes_name, optsize,
1751*7c478bd9Sstevel@tonic-gate 			    (uchar_t *)optd->opdes_defbuf, &optlen,
1752*7c478bd9Sstevel@tonic-gate 			    _TPI_TOPT_DATA(topth), NULL, cr, NULL);
1753*7c478bd9Sstevel@tonic-gate 			if (error) {
1754*7c478bd9Sstevel@tonic-gate 				/*
1755*7c478bd9Sstevel@tonic-gate 				 * failed, return as T_FAILURE and null value
1756*7c478bd9Sstevel@tonic-gate 				 * part. Maybe something downstream will
1757*7c478bd9Sstevel@tonic-gate 				 * handle this one and fill in a value. Here
1758*7c478bd9Sstevel@tonic-gate 				 * it is just part of T_ALLOPT expansion.
1759*7c478bd9Sstevel@tonic-gate 				 */
1760*7c478bd9Sstevel@tonic-gate 				topth->status = T_FAILURE;
1761*7c478bd9Sstevel@tonic-gate 				*worst_statusp = get_worst_status(topth->status,
1762*7c478bd9Sstevel@tonic-gate 				    *worst_statusp);
1763*7c478bd9Sstevel@tonic-gate 			} else {
1764*7c478bd9Sstevel@tonic-gate 				/* success */
1765*7c478bd9Sstevel@tonic-gate 				topth->len += optlen;
1766*7c478bd9Sstevel@tonic-gate 				topth->status = T_SUCCESS;
1767*7c478bd9Sstevel@tonic-gate 				*resptrp += _TPI_ALIGN_TOPT(optlen);
1768*7c478bd9Sstevel@tonic-gate 			}
1769*7c478bd9Sstevel@tonic-gate 		} /* end for loop */
1770*7c478bd9Sstevel@tonic-gate 		/* END T_ALLOPT */
1771*7c478bd9Sstevel@tonic-gate 	}
1772*7c478bd9Sstevel@tonic-gate 
1773*7c478bd9Sstevel@tonic-gate 	if (*resptrp == initptr) {
1774*7c478bd9Sstevel@tonic-gate 		/*
1775*7c478bd9Sstevel@tonic-gate 		 * setfn failed and does not want to handle this option. Maybe
1776*7c478bd9Sstevel@tonic-gate 		 * something downstream will or something upstream
1777*7c478bd9Sstevel@tonic-gate 		 * did. Copy the input as is from input option buffer if any to
1778*7c478bd9Sstevel@tonic-gate 		 * maintain transparency (maybe something at a level above
1779*7c478bd9Sstevel@tonic-gate 		 * did something.
1780*7c478bd9Sstevel@tonic-gate 		 */
1781*7c478bd9Sstevel@tonic-gate 		if (topmost_tpiprovider)
1782*7c478bd9Sstevel@tonic-gate 			reqopt->status = T_FAILURE;
1783*7c478bd9Sstevel@tonic-gate 		bcopy(reqopt, *resptrp, reqopt->len);
1784*7c478bd9Sstevel@tonic-gate 		*resptrp += _TPI_ALIGN_TOPT(reqopt->len);
1785*7c478bd9Sstevel@tonic-gate 		*worst_statusp = get_worst_status(reqopt->status,
1786*7c478bd9Sstevel@tonic-gate 		    *worst_statusp);
1787*7c478bd9Sstevel@tonic-gate 	}
1788*7c478bd9Sstevel@tonic-gate 	return (0);
1789*7c478bd9Sstevel@tonic-gate }
1790*7c478bd9Sstevel@tonic-gate 
1791*7c478bd9Sstevel@tonic-gate /*
1792*7c478bd9Sstevel@tonic-gate  * The following routines process options buffer passed with
1793*7c478bd9Sstevel@tonic-gate  * T_CONN_REQ, T_CONN_RES and T_UNITDATA_REQ.
1794*7c478bd9Sstevel@tonic-gate  * This routine does the consistency check applied to the
1795*7c478bd9Sstevel@tonic-gate  * sanity of formatting of multiple options packed in the
1796*7c478bd9Sstevel@tonic-gate  * buffer.
1797*7c478bd9Sstevel@tonic-gate  *
1798*7c478bd9Sstevel@tonic-gate  * XTI brain damage alert:
1799*7c478bd9Sstevel@tonic-gate  * XTI interface adopts the notion of an option being an
1800*7c478bd9Sstevel@tonic-gate  * "absolute requirement" from OSI transport service (but applies
1801*7c478bd9Sstevel@tonic-gate  * it to all transports including Internet transports).
1802*7c478bd9Sstevel@tonic-gate  * The main effect of that is action on failure to "negotiate" a
1803*7c478bd9Sstevel@tonic-gate  * requested option to the exact requested value
1804*7c478bd9Sstevel@tonic-gate  *
1805*7c478bd9Sstevel@tonic-gate  *          - if the option is an "absolute requirement", the primitive
1806*7c478bd9Sstevel@tonic-gate  *            is aborted (e.g T_DISCON_REQ or T_UDERR generated)
1807*7c478bd9Sstevel@tonic-gate  *          - if the option is NOT and "absolute requirement" it can
1808*7c478bd9Sstevel@tonic-gate  *            just be ignored.
1809*7c478bd9Sstevel@tonic-gate  *
1810*7c478bd9Sstevel@tonic-gate  * We would not support "negotiating" of options on connection
1811*7c478bd9Sstevel@tonic-gate  * primitives for Internet transports. However just in case we
1812*7c478bd9Sstevel@tonic-gate  * forced to in order to pass strange test suites, the design here
1813*7c478bd9Sstevel@tonic-gate  * tries to support these notions.
1814*7c478bd9Sstevel@tonic-gate  *
1815*7c478bd9Sstevel@tonic-gate  * tpi_optcom_buf(q, mp, opt_lenp, opt_offset, cred, dbobjp, thisdg_attrs,
1816*7c478bd9Sstevel@tonic-gate  *	*is_absreq_failurep)
1817*7c478bd9Sstevel@tonic-gate  *
1818*7c478bd9Sstevel@tonic-gate  * - Verify the option buffer, if formatted badly, return error 1
1819*7c478bd9Sstevel@tonic-gate  *
1820*7c478bd9Sstevel@tonic-gate  * - If it is a "permissions" failure (read-only), return error 2
1821*7c478bd9Sstevel@tonic-gate  *
1822*7c478bd9Sstevel@tonic-gate  * - Else, process the option "in place", the following can happen,
1823*7c478bd9Sstevel@tonic-gate  *	     - if a "privileged" option, mark it as "ignored".
1824*7c478bd9Sstevel@tonic-gate  *	     - if "not supported", mark "ignored"
1825*7c478bd9Sstevel@tonic-gate  *	     - if "supported" attempt negotiation and fill result in
1826*7c478bd9Sstevel@tonic-gate  *	       the outcome
1827*7c478bd9Sstevel@tonic-gate  *			- if "absolute requirement", set "*is_absreq_failurep"
1828*7c478bd9Sstevel@tonic-gate  *			- if NOT an "absolute requirement", then our
1829*7c478bd9Sstevel@tonic-gate  *			  interpretation is to mark is at ignored if
1830*7c478bd9Sstevel@tonic-gate  *			  negotiation fails (Spec allows partial success
1831*7c478bd9Sstevel@tonic-gate  *			  as in OSI protocols but not failure)
1832*7c478bd9Sstevel@tonic-gate  *
1833*7c478bd9Sstevel@tonic-gate  *   Then delete "ignored" options from option buffer and return success.
1834*7c478bd9Sstevel@tonic-gate  *
1835*7c478bd9Sstevel@tonic-gate  */
1836*7c478bd9Sstevel@tonic-gate 
1837*7c478bd9Sstevel@tonic-gate int
1838*7c478bd9Sstevel@tonic-gate tpi_optcom_buf(queue_t *q, mblk_t *mp, t_scalar_t *opt_lenp,
1839*7c478bd9Sstevel@tonic-gate     t_scalar_t opt_offset, cred_t *cr, optdb_obj_t *dbobjp,
1840*7c478bd9Sstevel@tonic-gate     void *thisdg_attrs, int *is_absreq_failurep)
1841*7c478bd9Sstevel@tonic-gate {
1842*7c478bd9Sstevel@tonic-gate 	opt_set_fn setfn = dbobjp->odb_setfn;
1843*7c478bd9Sstevel@tonic-gate 	opdes_t *opt_arr = dbobjp->odb_opt_des_arr;
1844*7c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
1845*7c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt, *opt_start, *opt_end;
1846*7c478bd9Sstevel@tonic-gate 	mblk_t  *copy_mp_head;
1847*7c478bd9Sstevel@tonic-gate 	uchar_t *optr, *init_optr;
1848*7c478bd9Sstevel@tonic-gate 	opdes_t *optd;
1849*7c478bd9Sstevel@tonic-gate 	uint_t optset_context;
1850*7c478bd9Sstevel@tonic-gate 	t_uscalar_t olen;
1851*7c478bd9Sstevel@tonic-gate 	int error = 0;
1852*7c478bd9Sstevel@tonic-gate 
1853*7c478bd9Sstevel@tonic-gate 	ASSERT((uchar_t *)opt_lenp > mp->b_rptr &&
1854*7c478bd9Sstevel@tonic-gate 	    (uchar_t *)opt_lenp < mp->b_wptr);
1855*7c478bd9Sstevel@tonic-gate 
1856*7c478bd9Sstevel@tonic-gate 	copy_mp_head = NULL;
1857*7c478bd9Sstevel@tonic-gate 	*is_absreq_failurep = 0;
1858*7c478bd9Sstevel@tonic-gate 	switch (((union T_primitives *)mp->b_rptr)->type) {
1859*7c478bd9Sstevel@tonic-gate 	case T_CONN_REQ:
1860*7c478bd9Sstevel@tonic-gate 	case T_CONN_RES:
1861*7c478bd9Sstevel@tonic-gate 		optset_context = SETFN_CONN_NEGOTIATE;
1862*7c478bd9Sstevel@tonic-gate 		break;
1863*7c478bd9Sstevel@tonic-gate 	case T_UNITDATA_REQ:
1864*7c478bd9Sstevel@tonic-gate 		optset_context = SETFN_UD_NEGOTIATE;
1865*7c478bd9Sstevel@tonic-gate 		break;
1866*7c478bd9Sstevel@tonic-gate 	default:
1867*7c478bd9Sstevel@tonic-gate 		/*
1868*7c478bd9Sstevel@tonic-gate 		 * should never get here, all possible TPI primitives
1869*7c478bd9Sstevel@tonic-gate 		 * where this can be called from should be accounted
1870*7c478bd9Sstevel@tonic-gate 		 * for in the cases above
1871*7c478bd9Sstevel@tonic-gate 		 */
1872*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1873*7c478bd9Sstevel@tonic-gate 	}
1874*7c478bd9Sstevel@tonic-gate 
1875*7c478bd9Sstevel@tonic-gate 	if ((opt_start = (struct T_opthdr *)
1876*7c478bd9Sstevel@tonic-gate 	    mi_offset_param(mp, opt_offset, *opt_lenp)) == NULL) {
1877*7c478bd9Sstevel@tonic-gate 		error = ENOPROTOOPT;
1878*7c478bd9Sstevel@tonic-gate 		goto error_ret;
1879*7c478bd9Sstevel@tonic-gate 	}
1880*7c478bd9Sstevel@tonic-gate 	if (!__TPI_TOPT_ISALIGNED(opt_start)) {
1881*7c478bd9Sstevel@tonic-gate 		error = ENOPROTOOPT;
1882*7c478bd9Sstevel@tonic-gate 		goto error_ret;
1883*7c478bd9Sstevel@tonic-gate 	}
1884*7c478bd9Sstevel@tonic-gate 
1885*7c478bd9Sstevel@tonic-gate 	opt_end = (struct T_opthdr *)((uchar_t *)opt_start
1886*7c478bd9Sstevel@tonic-gate 	    + *opt_lenp);
1887*7c478bd9Sstevel@tonic-gate 
1888*7c478bd9Sstevel@tonic-gate 	if ((copy_mp_head = copyb(mp)) == (mblk_t *)NULL) {
1889*7c478bd9Sstevel@tonic-gate 		error = ENOMEM;
1890*7c478bd9Sstevel@tonic-gate 		goto error_ret;
1891*7c478bd9Sstevel@tonic-gate 	}
1892*7c478bd9Sstevel@tonic-gate 
1893*7c478bd9Sstevel@tonic-gate 	init_optr = optr = (uchar_t *)&copy_mp_head->b_rptr[opt_offset];
1894*7c478bd9Sstevel@tonic-gate 
1895*7c478bd9Sstevel@tonic-gate 	for (opt = opt_start; opt && (opt < opt_end);
1896*7c478bd9Sstevel@tonic-gate 	    opt = _TPI_TOPT_NEXTHDR(opt_start, *opt_lenp, opt)) {
1897*7c478bd9Sstevel@tonic-gate 		/*
1898*7c478bd9Sstevel@tonic-gate 		 * Validate the option for length and alignment
1899*7c478bd9Sstevel@tonic-gate 		 * before accessing anything in it
1900*7c478bd9Sstevel@tonic-gate 		 */
1901*7c478bd9Sstevel@tonic-gate 		if (!_TPI_TOPT_VALID(opt, opt_start, opt_end)) {
1902*7c478bd9Sstevel@tonic-gate 			error = ENOPROTOOPT;
1903*7c478bd9Sstevel@tonic-gate 			goto error_ret;
1904*7c478bd9Sstevel@tonic-gate 		}
1905*7c478bd9Sstevel@tonic-gate 
1906*7c478bd9Sstevel@tonic-gate 		/* Find the option in the opt_arr. */
1907*7c478bd9Sstevel@tonic-gate 		optd = opt_chk_lookup(opt->level, opt->name,
1908*7c478bd9Sstevel@tonic-gate 		    opt_arr, opt_arr_cnt);
1909*7c478bd9Sstevel@tonic-gate 
1910*7c478bd9Sstevel@tonic-gate 		if (optd == NULL) {
1911*7c478bd9Sstevel@tonic-gate 			/*
1912*7c478bd9Sstevel@tonic-gate 			 * Option not found
1913*7c478bd9Sstevel@tonic-gate 			 */
1914*7c478bd9Sstevel@tonic-gate 			opt->status = T_NOTSUPPORT;
1915*7c478bd9Sstevel@tonic-gate 			continue;
1916*7c478bd9Sstevel@tonic-gate 		}
1917*7c478bd9Sstevel@tonic-gate 
1918*7c478bd9Sstevel@tonic-gate 		/*
1919*7c478bd9Sstevel@tonic-gate 		 * Weird but as in XTI spec.
1920*7c478bd9Sstevel@tonic-gate 		 * Sec 6.3.6 "Privileged and ReadOnly Options"
1921*7c478bd9Sstevel@tonic-gate 		 * Permission problems (e.g.readonly) fail with bad access
1922*7c478bd9Sstevel@tonic-gate 		 * BUT "privileged" option request from those NOT PRIVILEGED
1923*7c478bd9Sstevel@tonic-gate 		 * are to be merely "ignored".
1924*7c478bd9Sstevel@tonic-gate 		 * XXX Prevents "probing" of privileged options ?
1925*7c478bd9Sstevel@tonic-gate 		 */
1926*7c478bd9Sstevel@tonic-gate 		if (OA_READONLY_PERMISSION(optd, cr)) {
1927*7c478bd9Sstevel@tonic-gate 			error = EACCES;
1928*7c478bd9Sstevel@tonic-gate 			goto error_ret;
1929*7c478bd9Sstevel@tonic-gate 		}
1930*7c478bd9Sstevel@tonic-gate 		if (OA_MATCHED_PRIV(optd, cr)) {
1931*7c478bd9Sstevel@tonic-gate 			/*
1932*7c478bd9Sstevel@tonic-gate 			 * For privileged options, we DO perform
1933*7c478bd9Sstevel@tonic-gate 			 * access checks as is common sense
1934*7c478bd9Sstevel@tonic-gate 			 */
1935*7c478bd9Sstevel@tonic-gate 			if (!OA_WX_ANYPRIV(optd)) {
1936*7c478bd9Sstevel@tonic-gate 				error = EACCES;
1937*7c478bd9Sstevel@tonic-gate 				goto error_ret;
1938*7c478bd9Sstevel@tonic-gate 			}
1939*7c478bd9Sstevel@tonic-gate 		} else {
1940*7c478bd9Sstevel@tonic-gate 			/*
1941*7c478bd9Sstevel@tonic-gate 			 * For non privileged, we fail instead following
1942*7c478bd9Sstevel@tonic-gate 			 * "ignore" semantics dictated by XTI spec for
1943*7c478bd9Sstevel@tonic-gate 			 * permissions problems.
1944*7c478bd9Sstevel@tonic-gate 			 * Sec 6.3.6 "Privileged and ReadOnly Options"
1945*7c478bd9Sstevel@tonic-gate 			 * XXX Should we do "ignore" semantics ?
1946*7c478bd9Sstevel@tonic-gate 			 */
1947*7c478bd9Sstevel@tonic-gate 			if (!OA_WX_NOPRIV(optd)) { /* nopriv */
1948*7c478bd9Sstevel@tonic-gate 				opt->status = T_FAILURE;
1949*7c478bd9Sstevel@tonic-gate 				continue;
1950*7c478bd9Sstevel@tonic-gate 			}
1951*7c478bd9Sstevel@tonic-gate 		}
1952*7c478bd9Sstevel@tonic-gate 		/*
1953*7c478bd9Sstevel@tonic-gate 		 *
1954*7c478bd9Sstevel@tonic-gate 		 * If the negotiation fails, for options that
1955*7c478bd9Sstevel@tonic-gate 		 * are "absolute requirement", it is a fatal error.
1956*7c478bd9Sstevel@tonic-gate 		 * For options that are NOT "absolute requirements",
1957*7c478bd9Sstevel@tonic-gate 		 * and the value fails to negotiate, the XTI spec
1958*7c478bd9Sstevel@tonic-gate 		 * only considers the possibility of partial success
1959*7c478bd9Sstevel@tonic-gate 		 * (T_PARTSUCCES - not likely for Internet protocols).
1960*7c478bd9Sstevel@tonic-gate 		 * The spec is in denial about complete failure
1961*7c478bd9Sstevel@tonic-gate 		 * (T_FAILURE) to negotiate for options that are
1962*7c478bd9Sstevel@tonic-gate 		 * carried on T_CONN_REQ/T_CONN_RES/T_UNITDATA
1963*7c478bd9Sstevel@tonic-gate 		 * We interpret the T_FAILURE to negotiate an option
1964*7c478bd9Sstevel@tonic-gate 		 * that is NOT an absolute requirement that it is safe
1965*7c478bd9Sstevel@tonic-gate 		 * to ignore it.
1966*7c478bd9Sstevel@tonic-gate 		 */
1967*7c478bd9Sstevel@tonic-gate 
1968*7c478bd9Sstevel@tonic-gate 		/* verify length */
1969*7c478bd9Sstevel@tonic-gate 		if (!opt_length_ok(optd, opt)) {
1970*7c478bd9Sstevel@tonic-gate 			/* bad size */
1971*7c478bd9Sstevel@tonic-gate 			if ((optd->opdes_props & OP_NOT_ABSREQ) == 0) {
1972*7c478bd9Sstevel@tonic-gate 				/* option is absolute requirement */
1973*7c478bd9Sstevel@tonic-gate 				*is_absreq_failurep = 1;
1974*7c478bd9Sstevel@tonic-gate 				error = EINVAL;
1975*7c478bd9Sstevel@tonic-gate 				goto error_ret;
1976*7c478bd9Sstevel@tonic-gate 			}
1977*7c478bd9Sstevel@tonic-gate 			opt->status = T_FAILURE;
1978*7c478bd9Sstevel@tonic-gate 			continue;
1979*7c478bd9Sstevel@tonic-gate 		}
1980*7c478bd9Sstevel@tonic-gate 
1981*7c478bd9Sstevel@tonic-gate 		/*
1982*7c478bd9Sstevel@tonic-gate 		 * verified generic attributes. Now call set function.
1983*7c478bd9Sstevel@tonic-gate 		 * Note: We assume the following to simplify code.
1984*7c478bd9Sstevel@tonic-gate 		 * XXX If this is found not to be valid, this routine
1985*7c478bd9Sstevel@tonic-gate 		 * will need to be rewritten. At this point it would
1986*7c478bd9Sstevel@tonic-gate 		 * be premature to introduce more complexity than is
1987*7c478bd9Sstevel@tonic-gate 		 * needed.
1988*7c478bd9Sstevel@tonic-gate 		 * Assumption: For variable length options, we assume
1989*7c478bd9Sstevel@tonic-gate 		 * that the value returned will be same or less length
1990*7c478bd9Sstevel@tonic-gate 		 * (size does not increase). This makes it OK to pass the
1991*7c478bd9Sstevel@tonic-gate 		 * same space for output as it is on input.
1992*7c478bd9Sstevel@tonic-gate 		 */
1993*7c478bd9Sstevel@tonic-gate 
1994*7c478bd9Sstevel@tonic-gate 		error = (*setfn)(q, optset_context, opt->level, opt->name,
1995*7c478bd9Sstevel@tonic-gate 		    opt->len - (t_uscalar_t)sizeof (struct T_opthdr),
1996*7c478bd9Sstevel@tonic-gate 		    _TPI_TOPT_DATA(opt), &olen, _TPI_TOPT_DATA(opt),
1997*7c478bd9Sstevel@tonic-gate 		    thisdg_attrs, cr, NULL);
1998*7c478bd9Sstevel@tonic-gate 
1999*7c478bd9Sstevel@tonic-gate 		if (olen > (int)(opt->len - sizeof (struct T_opthdr))) {
2000*7c478bd9Sstevel@tonic-gate 			/*
2001*7c478bd9Sstevel@tonic-gate 			 * Space on output more than space on input. Should
2002*7c478bd9Sstevel@tonic-gate 			 * not happen and we consider it a bug/error.
2003*7c478bd9Sstevel@tonic-gate 			 * More of a restriction than an error in our
2004*7c478bd9Sstevel@tonic-gate 			 * implementation. Will see if we can live with this
2005*7c478bd9Sstevel@tonic-gate 			 * otherwise code will get more hairy with multiple
2006*7c478bd9Sstevel@tonic-gate 			 * passes.
2007*7c478bd9Sstevel@tonic-gate 			 */
2008*7c478bd9Sstevel@tonic-gate 			error = EINVAL;
2009*7c478bd9Sstevel@tonic-gate 			goto error_ret;
2010*7c478bd9Sstevel@tonic-gate 		}
2011*7c478bd9Sstevel@tonic-gate 		if (error != 0) {
2012*7c478bd9Sstevel@tonic-gate 			if ((optd->opdes_props & OP_NOT_ABSREQ) == 0) {
2013*7c478bd9Sstevel@tonic-gate 				/* option is absolute requirement. */
2014*7c478bd9Sstevel@tonic-gate 				*is_absreq_failurep = 1;
2015*7c478bd9Sstevel@tonic-gate 				goto error_ret;
2016*7c478bd9Sstevel@tonic-gate 			}
2017*7c478bd9Sstevel@tonic-gate 			/*
2018*7c478bd9Sstevel@tonic-gate 			 * failed - but option "not an absolute
2019*7c478bd9Sstevel@tonic-gate 			 * requirement"
2020*7c478bd9Sstevel@tonic-gate 			 */
2021*7c478bd9Sstevel@tonic-gate 			opt->status = T_FAILURE;
2022*7c478bd9Sstevel@tonic-gate 			continue;
2023*7c478bd9Sstevel@tonic-gate 		}
2024*7c478bd9Sstevel@tonic-gate 		/*
2025*7c478bd9Sstevel@tonic-gate 		 * Fill in the only possible successful result
2026*7c478bd9Sstevel@tonic-gate 		 * (Note: TPI allows for T_PARTSUCCESS - partial
2027*7c478bd9Sstevel@tonic-gate 		 * sucess result code which is relevant in OSI world
2028*7c478bd9Sstevel@tonic-gate 		 * and not possible in Internet code)
2029*7c478bd9Sstevel@tonic-gate 		 */
2030*7c478bd9Sstevel@tonic-gate 		opt->status = T_SUCCESS;
2031*7c478bd9Sstevel@tonic-gate 
2032*7c478bd9Sstevel@tonic-gate 		/*
2033*7c478bd9Sstevel@tonic-gate 		 * Add T_SUCCESS result code options to the "output" options.
2034*7c478bd9Sstevel@tonic-gate 		 * No T_FAILURES or T_NOTSUPPORT here as they are to be
2035*7c478bd9Sstevel@tonic-gate 		 * ignored.
2036*7c478bd9Sstevel@tonic-gate 		 * This code assumes output option buffer will
2037*7c478bd9Sstevel@tonic-gate 		 * be <= input option buffer.
2038*7c478bd9Sstevel@tonic-gate 		 *
2039*7c478bd9Sstevel@tonic-gate 		 * Copy option header+value
2040*7c478bd9Sstevel@tonic-gate 		 */
2041*7c478bd9Sstevel@tonic-gate 		bcopy(opt, optr, opt->len);
2042*7c478bd9Sstevel@tonic-gate 		optr +=  _TPI_ALIGN_TOPT(opt->len);
2043*7c478bd9Sstevel@tonic-gate 	}
2044*7c478bd9Sstevel@tonic-gate 	/*
2045*7c478bd9Sstevel@tonic-gate 	 * Overwrite the input mblk option buffer now with the output
2046*7c478bd9Sstevel@tonic-gate 	 * and update length, and contents in original mbl
2047*7c478bd9Sstevel@tonic-gate 	 * (offset remains unchanged).
2048*7c478bd9Sstevel@tonic-gate 	 */
2049*7c478bd9Sstevel@tonic-gate 	*opt_lenp = (t_scalar_t)(optr - init_optr);
2050*7c478bd9Sstevel@tonic-gate 	if (*opt_lenp > 0) {
2051*7c478bd9Sstevel@tonic-gate 		bcopy(init_optr, opt_start, *opt_lenp);
2052*7c478bd9Sstevel@tonic-gate 	}
2053*7c478bd9Sstevel@tonic-gate 
2054*7c478bd9Sstevel@tonic-gate error_ret:
2055*7c478bd9Sstevel@tonic-gate 	if (copy_mp_head != NULL)
2056*7c478bd9Sstevel@tonic-gate 		freeb(copy_mp_head);
2057*7c478bd9Sstevel@tonic-gate 	return (error);
2058*7c478bd9Sstevel@tonic-gate }
2059*7c478bd9Sstevel@tonic-gate 
2060*7c478bd9Sstevel@tonic-gate static opdes_t *
2061*7c478bd9Sstevel@tonic-gate opt_chk_lookup(t_uscalar_t level, t_uscalar_t name, opdes_t *opt_arr,
2062*7c478bd9Sstevel@tonic-gate     uint_t opt_arr_cnt)
2063*7c478bd9Sstevel@tonic-gate {
2064*7c478bd9Sstevel@tonic-gate 	opdes_t		*optd;
2065*7c478bd9Sstevel@tonic-gate 
2066*7c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt];
2067*7c478bd9Sstevel@tonic-gate 	    optd++) {
2068*7c478bd9Sstevel@tonic-gate 		if (level == (uint_t)optd->opdes_level &&
2069*7c478bd9Sstevel@tonic-gate 		    name == (uint_t)optd->opdes_name)
2070*7c478bd9Sstevel@tonic-gate 			return (optd);
2071*7c478bd9Sstevel@tonic-gate 	}
2072*7c478bd9Sstevel@tonic-gate 	return (NULL);
2073*7c478bd9Sstevel@tonic-gate }
2074*7c478bd9Sstevel@tonic-gate 
2075*7c478bd9Sstevel@tonic-gate static boolean_t
2076*7c478bd9Sstevel@tonic-gate opt_level_valid(t_uscalar_t level, optlevel_t *valid_level_arr,
2077*7c478bd9Sstevel@tonic-gate     uint_t valid_level_arr_cnt)
2078*7c478bd9Sstevel@tonic-gate {
2079*7c478bd9Sstevel@tonic-gate 	optlevel_t		*olp;
2080*7c478bd9Sstevel@tonic-gate 
2081*7c478bd9Sstevel@tonic-gate 	for (olp = valid_level_arr;
2082*7c478bd9Sstevel@tonic-gate 	    olp < &valid_level_arr[valid_level_arr_cnt];
2083*7c478bd9Sstevel@tonic-gate 	    olp++) {
2084*7c478bd9Sstevel@tonic-gate 		if (level == (uint_t)(*olp))
2085*7c478bd9Sstevel@tonic-gate 			return (B_TRUE);
2086*7c478bd9Sstevel@tonic-gate 	}
2087*7c478bd9Sstevel@tonic-gate 	return (B_FALSE);
2088*7c478bd9Sstevel@tonic-gate }
2089*7c478bd9Sstevel@tonic-gate 
2090*7c478bd9Sstevel@tonic-gate 
2091*7c478bd9Sstevel@tonic-gate /*
2092*7c478bd9Sstevel@tonic-gate  * Compute largest possible size for an option buffer containing
2093*7c478bd9Sstevel@tonic-gate  * all options in one buffer.
2094*7c478bd9Sstevel@tonic-gate  *
2095*7c478bd9Sstevel@tonic-gate  * XXX TBD, investigate use of opt_bloated_maxsize() to avoid
2096*7c478bd9Sstevel@tonic-gate  *     wastefully large buffer allocation.
2097*7c478bd9Sstevel@tonic-gate  */
2098*7c478bd9Sstevel@tonic-gate static size_t
2099*7c478bd9Sstevel@tonic-gate opt_level_allopts_lengths(t_uscalar_t level, opdes_t *opt_arr,
2100*7c478bd9Sstevel@tonic-gate     uint_t opt_arr_cnt)
2101*7c478bd9Sstevel@tonic-gate {
2102*7c478bd9Sstevel@tonic-gate 	opdes_t		*optd;
2103*7c478bd9Sstevel@tonic-gate 	size_t allopt_len = 0;	/* 0 implies no option at this level */
2104*7c478bd9Sstevel@tonic-gate 
2105*7c478bd9Sstevel@tonic-gate 	/*
2106*7c478bd9Sstevel@tonic-gate 	 * Scan opt_arr computing aggregate length
2107*7c478bd9Sstevel@tonic-gate 	 * requirement for storing values of all
2108*7c478bd9Sstevel@tonic-gate 	 * options.
2109*7c478bd9Sstevel@tonic-gate 	 * Note: we do not filter for permissions
2110*7c478bd9Sstevel@tonic-gate 	 * etc. This will be >= the real aggregate
2111*7c478bd9Sstevel@tonic-gate 	 * length required (upper bound).
2112*7c478bd9Sstevel@tonic-gate 	 */
2113*7c478bd9Sstevel@tonic-gate 
2114*7c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt];
2115*7c478bd9Sstevel@tonic-gate 	    optd++) {
2116*7c478bd9Sstevel@tonic-gate 		if (level == optd->opdes_level) {
2117*7c478bd9Sstevel@tonic-gate 			allopt_len += sizeof (struct T_opthdr) +
2118*7c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_TOPT(optd->opdes_size);
2119*7c478bd9Sstevel@tonic-gate 		}
2120*7c478bd9Sstevel@tonic-gate 	}
2121*7c478bd9Sstevel@tonic-gate 	return (allopt_len);	/* 0 implies level not found */
2122*7c478bd9Sstevel@tonic-gate }
2123*7c478bd9Sstevel@tonic-gate 
2124*7c478bd9Sstevel@tonic-gate /*
2125*7c478bd9Sstevel@tonic-gate  * Compute largest possible size for an option buffer containing
2126*7c478bd9Sstevel@tonic-gate  * all options in one buffer - a (theoretical?) worst case scenario
2127*7c478bd9Sstevel@tonic-gate  * for certain cases.
2128*7c478bd9Sstevel@tonic-gate  */
2129*7c478bd9Sstevel@tonic-gate t_uscalar_t
2130*7c478bd9Sstevel@tonic-gate optcom_max_optbuf_len(opdes_t *opt_arr, uint_t opt_arr_cnt)
2131*7c478bd9Sstevel@tonic-gate {
2132*7c478bd9Sstevel@tonic-gate 	t_uscalar_t max_optbuf_len = sizeof (struct T_info_ack);
2133*7c478bd9Sstevel@tonic-gate 	opdes_t		*optd;
2134*7c478bd9Sstevel@tonic-gate 
2135*7c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
2136*7c478bd9Sstevel@tonic-gate 		max_optbuf_len += (t_uscalar_t)sizeof (struct T_opthdr) +
2137*7c478bd9Sstevel@tonic-gate 		    (t_uscalar_t)_TPI_ALIGN_TOPT(optd->opdes_size);
2138*7c478bd9Sstevel@tonic-gate 	}
2139*7c478bd9Sstevel@tonic-gate 	return (max_optbuf_len);
2140*7c478bd9Sstevel@tonic-gate }
2141*7c478bd9Sstevel@tonic-gate 
2142*7c478bd9Sstevel@tonic-gate /*
2143*7c478bd9Sstevel@tonic-gate  * Compute largest possible size for OPT_size for a transport.
2144*7c478bd9Sstevel@tonic-gate  * Heuristic used is to add all but certain extremely large
2145*7c478bd9Sstevel@tonic-gate  * size options; this is done by calling opt_bloated_maxsize().
2146*7c478bd9Sstevel@tonic-gate  * It affects user level allocations in TLI/XTI code using t_alloc()
2147*7c478bd9Sstevel@tonic-gate  * and other TLI/XTI implementation instance strucutures.
2148*7c478bd9Sstevel@tonic-gate  * The large size options excluded are presumed to be
2149*7c478bd9Sstevel@tonic-gate  * never accessed through the (theoretical?) worst case code paths
2150*7c478bd9Sstevel@tonic-gate  * through TLI/XTI as they are currently IPv6 specific options.
2151*7c478bd9Sstevel@tonic-gate  */
2152*7c478bd9Sstevel@tonic-gate 
2153*7c478bd9Sstevel@tonic-gate t_uscalar_t
2154*7c478bd9Sstevel@tonic-gate optcom_max_optsize(opdes_t *opt_arr, uint_t opt_arr_cnt)
2155*7c478bd9Sstevel@tonic-gate {
2156*7c478bd9Sstevel@tonic-gate 	t_uscalar_t max_optbuf_len = sizeof (struct T_info_ack);
2157*7c478bd9Sstevel@tonic-gate 	opdes_t		*optd;
2158*7c478bd9Sstevel@tonic-gate 
2159*7c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
2160*7c478bd9Sstevel@tonic-gate 		if (!opt_bloated_maxsize(optd)) {
2161*7c478bd9Sstevel@tonic-gate 			max_optbuf_len +=
2162*7c478bd9Sstevel@tonic-gate 			    (t_uscalar_t)sizeof (struct T_opthdr) +
2163*7c478bd9Sstevel@tonic-gate 			    (t_uscalar_t)_TPI_ALIGN_TOPT(optd->opdes_size);
2164*7c478bd9Sstevel@tonic-gate 		}
2165*7c478bd9Sstevel@tonic-gate 	}
2166*7c478bd9Sstevel@tonic-gate 	return (max_optbuf_len);
2167*7c478bd9Sstevel@tonic-gate }
2168*7c478bd9Sstevel@tonic-gate 
2169*7c478bd9Sstevel@tonic-gate /*
2170*7c478bd9Sstevel@tonic-gate  * The theoretical model used in optcom_max_optsize() and
2171*7c478bd9Sstevel@tonic-gate  * opt_level_allopts_lengths() accounts for the worst case of all
2172*7c478bd9Sstevel@tonic-gate  * possible options for the theoretical cases and results in wasteful
2173*7c478bd9Sstevel@tonic-gate  * memory allocations for certain theoretically correct usage scenarios.
2174*7c478bd9Sstevel@tonic-gate  * In practice, the "features" they support are rarely, if ever,
2175*7c478bd9Sstevel@tonic-gate  * used and even then only by test suites for those features (VSU, VST).
2176*7c478bd9Sstevel@tonic-gate  * However, they result in large allocations due to the increased transport
2177*7c478bd9Sstevel@tonic-gate  * T_INFO_ACK OPT_size field affecting t_alloc() users and TLI/XTI library
2178*7c478bd9Sstevel@tonic-gate  * instance data structures for applications.
2179*7c478bd9Sstevel@tonic-gate  *
2180*7c478bd9Sstevel@tonic-gate  * The following routine opt_bloated_maxsize() supports a hack that avoids
2181*7c478bd9Sstevel@tonic-gate  * paying the tax for the bloated options by excluding them and pretending
2182*7c478bd9Sstevel@tonic-gate  * they don't exist for certain features without affecting features that
2183*7c478bd9Sstevel@tonic-gate  * do use them.
2184*7c478bd9Sstevel@tonic-gate  *
2185*7c478bd9Sstevel@tonic-gate  * XXX Currently implemented only for optcom_max_optsize()
2186*7c478bd9Sstevel@tonic-gate  *     (to reduce risk late in release).
2187*7c478bd9Sstevel@tonic-gate  *     TBD for future, investigate use in optcom_level_allopts_lengths() and
2188*7c478bd9Sstevel@tonic-gate  *     all the instances of T_ALLOPT processing to exclude "bloated options".
2189*7c478bd9Sstevel@tonic-gate  *     Will not affect VSU/VST tests as they do not test with IPPROTO_IPV6
2190*7c478bd9Sstevel@tonic-gate  *     level options which are the only ones that fit the "bloated maxsize"
2191*7c478bd9Sstevel@tonic-gate  *     option profile now.
2192*7c478bd9Sstevel@tonic-gate  */
2193*7c478bd9Sstevel@tonic-gate static boolean_t
2194*7c478bd9Sstevel@tonic-gate opt_bloated_maxsize(opdes_t *optd)
2195*7c478bd9Sstevel@tonic-gate {
2196*7c478bd9Sstevel@tonic-gate 	if (optd->opdes_level != IPPROTO_IPV6)
2197*7c478bd9Sstevel@tonic-gate 		return (B_FALSE);
2198*7c478bd9Sstevel@tonic-gate 	switch (optd->opdes_name) {
2199*7c478bd9Sstevel@tonic-gate 	case IPV6_HOPOPTS:
2200*7c478bd9Sstevel@tonic-gate 	case IPV6_DSTOPTS:
2201*7c478bd9Sstevel@tonic-gate 	case IPV6_RTHDRDSTOPTS:
2202*7c478bd9Sstevel@tonic-gate 	case IPV6_RTHDR:
2203*7c478bd9Sstevel@tonic-gate 	case IPV6_PATHMTU:
2204*7c478bd9Sstevel@tonic-gate 		return (B_TRUE);
2205*7c478bd9Sstevel@tonic-gate 	default:
2206*7c478bd9Sstevel@tonic-gate 		break;
2207*7c478bd9Sstevel@tonic-gate 	}
2208*7c478bd9Sstevel@tonic-gate 	return (B_FALSE);
2209*7c478bd9Sstevel@tonic-gate }
2210*7c478bd9Sstevel@tonic-gate 
2211*7c478bd9Sstevel@tonic-gate static boolean_t
2212*7c478bd9Sstevel@tonic-gate opt_length_ok(opdes_t *optd, struct T_opthdr *opt)
2213*7c478bd9Sstevel@tonic-gate {
2214*7c478bd9Sstevel@tonic-gate 	/*
2215*7c478bd9Sstevel@tonic-gate 	 * Verify length.
2216*7c478bd9Sstevel@tonic-gate 	 * Value specified should match length of fixed length option or be
2217*7c478bd9Sstevel@tonic-gate 	 * less than maxlen of variable length option.
2218*7c478bd9Sstevel@tonic-gate 	 */
2219*7c478bd9Sstevel@tonic-gate 	if (optd->opdes_props & OP_VARLEN) {
2220*7c478bd9Sstevel@tonic-gate 		if (opt->len <= optd->opdes_size +
2221*7c478bd9Sstevel@tonic-gate 		    (t_uscalar_t)sizeof (struct T_opthdr))
2222*7c478bd9Sstevel@tonic-gate 			return (B_TRUE);
2223*7c478bd9Sstevel@tonic-gate 	} else {
2224*7c478bd9Sstevel@tonic-gate 		/* fixed length option */
2225*7c478bd9Sstevel@tonic-gate 		if (opt->len == optd->opdes_size +
2226*7c478bd9Sstevel@tonic-gate 		    (t_uscalar_t)sizeof (struct T_opthdr))
2227*7c478bd9Sstevel@tonic-gate 			return (B_TRUE);
2228*7c478bd9Sstevel@tonic-gate 	}
2229*7c478bd9Sstevel@tonic-gate 	return (B_FALSE);
2230*7c478bd9Sstevel@tonic-gate }
2231