xref: /illumos-gate/usr/src/uts/common/inet/optcom.c (revision 45916cd2)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*45916cd2Sjpk  * Common Development and Distribution License (the "License").
6*45916cd2Sjpk  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*45916cd2Sjpk  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate /* Copyright (c) 1990 Mentat Inc. */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * This file contains common code for handling Options Management requests.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/stream.h>
357c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
367c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
377c478bd9Sstevel@tonic-gate #include <sys/errno.h>
387c478bd9Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
397c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
407c478bd9Sstevel@tonic-gate #include <sys/socket.h>
417c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
427c478bd9Sstevel@tonic-gate #include <sys/debug.h>		/* for ASSERT */
437c478bd9Sstevel@tonic-gate #include <sys/policy.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include <inet/common.h>
467c478bd9Sstevel@tonic-gate #include <inet/mi.h>
477c478bd9Sstevel@tonic-gate #include <inet/nd.h>
487c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
497c478bd9Sstevel@tonic-gate #include <inet/ip.h>
507c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
517c478bd9Sstevel@tonic-gate #include <netinet/in.h>
527c478bd9Sstevel@tonic-gate #include "optcom.h"
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate #include <inet/optcom.h>
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /*
577c478bd9Sstevel@tonic-gate  * Function prototypes
587c478bd9Sstevel@tonic-gate  */
597c478bd9Sstevel@tonic-gate static t_scalar_t process_topthdrs_first_pass(mblk_t *, cred_t *, optdb_obj_t *,
607c478bd9Sstevel@tonic-gate     boolean_t *, size_t *);
617c478bd9Sstevel@tonic-gate static t_scalar_t do_options_second_pass(queue_t *q, mblk_t *reqmp,
627c478bd9Sstevel@tonic-gate     mblk_t *ack_mp, cred_t *, optdb_obj_t *dbobjp,
637c478bd9Sstevel@tonic-gate     mblk_t *first_mp, boolean_t is_restart, boolean_t *queued_statusp);
647c478bd9Sstevel@tonic-gate static t_uscalar_t get_worst_status(t_uscalar_t, t_uscalar_t);
657c478bd9Sstevel@tonic-gate static int do_opt_default(queue_t *, struct T_opthdr *, uchar_t **,
667c478bd9Sstevel@tonic-gate     t_uscalar_t *, cred_t *, optdb_obj_t *);
677c478bd9Sstevel@tonic-gate static void do_opt_current(queue_t *, struct T_opthdr *, uchar_t **,
687c478bd9Sstevel@tonic-gate     t_uscalar_t *, cred_t *cr, optdb_obj_t *);
697c478bd9Sstevel@tonic-gate static int do_opt_check_or_negotiate(queue_t *q, struct T_opthdr *reqopt,
707c478bd9Sstevel@tonic-gate     uint_t optset_context, uchar_t **resptrp, t_uscalar_t *worst_statusp,
717c478bd9Sstevel@tonic-gate     cred_t *, optdb_obj_t *dbobjp, mblk_t *first_mp);
727c478bd9Sstevel@tonic-gate static opdes_t *opt_chk_lookup(t_uscalar_t, t_uscalar_t, opdes_t *, uint_t);
737c478bd9Sstevel@tonic-gate static boolean_t opt_level_valid(t_uscalar_t, optlevel_t *, uint_t);
747c478bd9Sstevel@tonic-gate static size_t opt_level_allopts_lengths(t_uscalar_t, opdes_t *, uint_t);
757c478bd9Sstevel@tonic-gate static boolean_t opt_length_ok(opdes_t *, struct T_opthdr *);
767c478bd9Sstevel@tonic-gate static t_uscalar_t optcom_max_optbuf_len(opdes_t *, uint_t);
777c478bd9Sstevel@tonic-gate static boolean_t opt_bloated_maxsize(opdes_t *);
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /* Common code for sending back a T_ERROR_ACK. */
807c478bd9Sstevel@tonic-gate void
817c478bd9Sstevel@tonic-gate optcom_err_ack(queue_t *q, mblk_t *mp, t_scalar_t t_error, int sys_error)
827c478bd9Sstevel@tonic-gate {
837c478bd9Sstevel@tonic-gate 	if ((mp = mi_tpi_err_ack_alloc(mp, t_error, sys_error)) != NULL)
847c478bd9Sstevel@tonic-gate 		qreply(q, mp);
857c478bd9Sstevel@tonic-gate }
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * The option management routines svr4_optcom_req() and tpi_optcom_req() use
897c478bd9Sstevel@tonic-gate  * callback functions as arguments. Here is the expected interfaces
907c478bd9Sstevel@tonic-gate  * assumed from the callback functions
917c478bd9Sstevel@tonic-gate  *
927c478bd9Sstevel@tonic-gate  *
937c478bd9Sstevel@tonic-gate  * (1) deffn(q, optlevel, optname, optvalp)
947c478bd9Sstevel@tonic-gate  *
957c478bd9Sstevel@tonic-gate  *	- Function only called when default value comes from protocol
967c478bd9Sstevel@tonic-gate  *	 specific code and not the option database table (indicated by
977c478bd9Sstevel@tonic-gate  *	  OP_DEF_FN property in option database.)
987c478bd9Sstevel@tonic-gate  *	- Error return is -1. Valid returns are >=0.
997c478bd9Sstevel@tonic-gate  *	- When valid, the return value represents the length used for storing
1007c478bd9Sstevel@tonic-gate  *		the default value of the option.
1017c478bd9Sstevel@tonic-gate  *      - Error return implies the called routine did not recognize this
1027c478bd9Sstevel@tonic-gate  *              option. Something downstream could so input is left unchanged
1037c478bd9Sstevel@tonic-gate  *              in request buffer.
1047c478bd9Sstevel@tonic-gate  *
1057c478bd9Sstevel@tonic-gate  * (2) getfn(q, optlevel, optname, optvalp)
1067c478bd9Sstevel@tonic-gate  *
1077c478bd9Sstevel@tonic-gate  *	- Error return is -1. Valid returns are >=0.
1087c478bd9Sstevel@tonic-gate  *	- When valid, the return value represents the length used for storing
1097c478bd9Sstevel@tonic-gate  *		the actual value of the option.
1107c478bd9Sstevel@tonic-gate  *      - Error return implies the called routine did not recognize this
1117c478bd9Sstevel@tonic-gate  *              option. Something downstream could so input is left unchanged
1127c478bd9Sstevel@tonic-gate  *              in request buffer.
1137c478bd9Sstevel@tonic-gate  *
1147c478bd9Sstevel@tonic-gate  * (3) setfn(q, optset_context, optlevel, optname, inlen, invalp,
1157c478bd9Sstevel@tonic-gate  *	outlenp, outvalp, attrp, cr);
1167c478bd9Sstevel@tonic-gate  *
1177c478bd9Sstevel@tonic-gate  *	- OK return is 0, Error code is returned as a non-zero argument.
1187c478bd9Sstevel@tonic-gate  *      - If negative it is ignored by svr4_optcom_req(). If positive, error
1197c478bd9Sstevel@tonic-gate  *        is returned. A negative return implies that option, while handled on
1207c478bd9Sstevel@tonic-gate  *	  this stack is not handled at this level and will be handled further
1217c478bd9Sstevel@tonic-gate  *	  downstream.
1227c478bd9Sstevel@tonic-gate  *	- Both negative and positive errors are treats as errors in an
1237c478bd9Sstevel@tonic-gate  *	  identical manner by tpi_optcom_req(). The errors affect "status"
1247c478bd9Sstevel@tonic-gate  *	  field of each option's T_opthdr. If sucessfull, an appropriate sucess
1257c478bd9Sstevel@tonic-gate  *	  result is carried. If error, it instantiated to "failure" at the
1267c478bd9Sstevel@tonic-gate  *	  topmost level and left unchanged at other levels. (This "failure" can
1277c478bd9Sstevel@tonic-gate  *	  turn to a success at another level).
1287c478bd9Sstevel@tonic-gate  *	- optset_context passed for tpi_optcom_req(). It is interpreted as:
1297c478bd9Sstevel@tonic-gate  *        - SETFN_OPTCOM_CHECKONLY
1307c478bd9Sstevel@tonic-gate  *		semantics are to pretend to set the value and report
1317c478bd9Sstevel@tonic-gate  *		back if it would be successful.
1327c478bd9Sstevel@tonic-gate  *		This is used with T_CHECK semantics in XTI
1337c478bd9Sstevel@tonic-gate  *        - SETFN_OPTCOM_NEGOTIATE
1347c478bd9Sstevel@tonic-gate  *		set the value. Call from option management primitive
1357c478bd9Sstevel@tonic-gate  *		T_OPTMGMT_REQ when T_NEGOTIATE flags is used.
1367c478bd9Sstevel@tonic-gate  *	  - SETFN_UD_NEGOTIATE
1377c478bd9Sstevel@tonic-gate  *		option request came riding on UNITDATA primitive most often
1387c478bd9Sstevel@tonic-gate  *		has  "this datagram" semantics to influence prpoerties
1397c478bd9Sstevel@tonic-gate  *		affecting an outgoig datagram or associated with recived
1407c478bd9Sstevel@tonic-gate  *		datagram
1417c478bd9Sstevel@tonic-gate  *		[ Note: XTI permits this use outside of "this datagram"
1427c478bd9Sstevel@tonic-gate  *		semantics also and permits setting "management related"
1437c478bd9Sstevel@tonic-gate  *		options in this	context and its test suite enforces it ]
1447c478bd9Sstevel@tonic-gate  *	  - SETFN_CONN_NEGOTATE
1457c478bd9Sstevel@tonic-gate  *		option request came riding on CONN_REQ/RES primitive and
1467c478bd9Sstevel@tonic-gate  *		most often has "this connection" (negotiation during
1477c478bd9Sstevel@tonic-gate  *		"connection estblishment") semantics.
1487c478bd9Sstevel@tonic-gate  *		[ Note: XTI permits use of these outside of "this connection"
1497c478bd9Sstevel@tonic-gate  *		semantics and permits "management related" options in this
1507c478bd9Sstevel@tonic-gate  *		context and its test suite enforces it. ]
1517c478bd9Sstevel@tonic-gate  *
1527c478bd9Sstevel@tonic-gate  *	- inlen, invalp is the option length,value requested to be set.
1537c478bd9Sstevel@tonic-gate  *	- outlenp, outvalp represent return parameters which contain the
1547c478bd9Sstevel@tonic-gate  *	  value set and it might be different from one passed on input.
1557c478bd9Sstevel@tonic-gate  *	- attrp points to a data structure that's used by v6 modules to
1567c478bd9Sstevel@tonic-gate  *	  store ancillary data options or sticky options.
1577c478bd9Sstevel@tonic-gate  *	- cr points to the caller's credentials
1587c478bd9Sstevel@tonic-gate  *	- the caller might pass same buffers for input and output and the
1597c478bd9Sstevel@tonic-gate  *	  routine should protect against this case by not updating output
1607c478bd9Sstevel@tonic-gate  *	  buffers until it is done referencing input buffers and any other
1617c478bd9Sstevel@tonic-gate  *	  issues (e.g. not use bcopy() if we do not trust what it does).
1627c478bd9Sstevel@tonic-gate  *      - If option is not known, it returns error. We randomly pick EINVAL.
1637c478bd9Sstevel@tonic-gate  *        It can however get called with options that are handled downstream
1647c478bd9Sstevel@tonic-gate  *        opr upstream so for svr4_optcom_req(), it does not return error for
1657c478bd9Sstevel@tonic-gate  *        negative return values.
1667c478bd9Sstevel@tonic-gate  *
1677c478bd9Sstevel@tonic-gate  */
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate /*
1707c478bd9Sstevel@tonic-gate  * Upper Level Protocols call this routine when they receive
1717c478bd9Sstevel@tonic-gate  * a T_SVR4_OPTMGMT_REQ message.  They supply callback functions
1727c478bd9Sstevel@tonic-gate  * for setting a new value for a single options, getting the
1737c478bd9Sstevel@tonic-gate  * current value for a single option, and checking for support
1747c478bd9Sstevel@tonic-gate  * of a single option.  svr4_optcom_req validates the option management
1757c478bd9Sstevel@tonic-gate  * buffer passed in, and calls the appropriate routines to do the
1767c478bd9Sstevel@tonic-gate  * job requested.
1777c478bd9Sstevel@tonic-gate  * XXX Code below needs some restructuring after we have some more
1787c478bd9Sstevel@tonic-gate  * macros to support 'struct opthdr' in the headers.
1797c478bd9Sstevel@tonic-gate  *
1807c478bd9Sstevel@tonic-gate  * IP-MT notes: The option management framework functions svr4_optcom_req() and
1817c478bd9Sstevel@tonic-gate  * tpi_optcom_req() allocate and prepend an M_CTL mblk to the actual
1827c478bd9Sstevel@tonic-gate  * T_optmgmt_req mblk and pass the chain as an additional parameter to the
1837c478bd9Sstevel@tonic-gate  * protocol set functions. If a protocol set function (such as ip_opt_set)
1847c478bd9Sstevel@tonic-gate  * cannot process the option immediately it can return EINPROGRESS. ip_opt_set
1857c478bd9Sstevel@tonic-gate  * enqueues the message in the appropriate sq and returns EINPROGRESS. Later
1867c478bd9Sstevel@tonic-gate  * the sq framework arranges to restart this operation and passes control to
1877c478bd9Sstevel@tonic-gate  * the restart function ip_restart_optmgmt() which in turn calls
1887c478bd9Sstevel@tonic-gate  * svr4_optcom_req() or tpi_optcom_req() to restart the option processing.
1897c478bd9Sstevel@tonic-gate  */
1907c478bd9Sstevel@tonic-gate int
1917c478bd9Sstevel@tonic-gate svr4_optcom_req(queue_t *q, mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp)
1927c478bd9Sstevel@tonic-gate {
1937c478bd9Sstevel@tonic-gate 	pfi_t	deffn = dbobjp->odb_deffn;
1947c478bd9Sstevel@tonic-gate 	pfi_t	getfn = dbobjp->odb_getfn;
1957c478bd9Sstevel@tonic-gate 	opt_set_fn setfn = dbobjp->odb_setfn;
1967c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
1977c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
1987c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
1997c478bd9Sstevel@tonic-gate 	opt_restart_t *or;
2007c478bd9Sstevel@tonic-gate 	struct opthdr *restart_opt;
2017c478bd9Sstevel@tonic-gate 	boolean_t is_restart = B_FALSE;
2027c478bd9Sstevel@tonic-gate 	mblk_t	*first_mp;
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 	t_uscalar_t max_optbuf_len;
2057c478bd9Sstevel@tonic-gate 	int len;
2067c478bd9Sstevel@tonic-gate 	mblk_t	*mp1 = NULL;
2077c478bd9Sstevel@tonic-gate 	struct opthdr *next_opt;
2087c478bd9Sstevel@tonic-gate 	struct opthdr *opt;
2097c478bd9Sstevel@tonic-gate 	struct opthdr *opt1;
2107c478bd9Sstevel@tonic-gate 	struct opthdr *opt_end;
2117c478bd9Sstevel@tonic-gate 	struct opthdr *opt_start;
2127c478bd9Sstevel@tonic-gate 	opdes_t	*optd;
2137c478bd9Sstevel@tonic-gate 	boolean_t	pass_to_next = B_FALSE;
2147c478bd9Sstevel@tonic-gate 	boolean_t	pass_to_ip = B_FALSE;
215ff550d0eSmasputra 	boolean_t	is_tcp;
2167c478bd9Sstevel@tonic-gate 	struct T_optmgmt_ack *toa;
2177c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor;
2187c478bd9Sstevel@tonic-gate 
219ff550d0eSmasputra 	is_tcp = (dbobjp == &tcp_opt_obj);
220ff550d0eSmasputra 
2217c478bd9Sstevel@tonic-gate 	/*
2227c478bd9Sstevel@tonic-gate 	 * Allocate M_CTL and prepend to the packet for restarting this
2237c478bd9Sstevel@tonic-gate 	 * option if needed. IP may need to queue and restart the option
2247c478bd9Sstevel@tonic-gate 	 * if it cannot obtain exclusive conditions immediately. Please see
2257c478bd9Sstevel@tonic-gate 	 * IP-MT notes before the start of svr4_optcom_req
2267c478bd9Sstevel@tonic-gate 	 */
2277c478bd9Sstevel@tonic-gate 	if (mp->b_datap->db_type == M_CTL) {
2287c478bd9Sstevel@tonic-gate 		is_restart = B_TRUE;
2297c478bd9Sstevel@tonic-gate 		first_mp = mp;
2307c478bd9Sstevel@tonic-gate 		mp = mp->b_cont;
2317c478bd9Sstevel@tonic-gate 		ASSERT(mp->b_wptr - mp->b_rptr >=
2327c478bd9Sstevel@tonic-gate 		    sizeof (struct T_optmgmt_req));
2337c478bd9Sstevel@tonic-gate 		tor = (struct T_optmgmt_req *)mp->b_rptr;
2347c478bd9Sstevel@tonic-gate 		ASSERT(tor->MGMT_flags == T_NEGOTIATE);
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 		or = (opt_restart_t *)first_mp->b_rptr;
2377c478bd9Sstevel@tonic-gate 		opt_start = or->or_start;
2387c478bd9Sstevel@tonic-gate 		opt_end = or->or_end;
2397c478bd9Sstevel@tonic-gate 		restart_opt = or->or_ropt;
2407c478bd9Sstevel@tonic-gate 		goto restart;
2417c478bd9Sstevel@tonic-gate 	}
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	tor = (struct T_optmgmt_req *)mp->b_rptr;
2447c478bd9Sstevel@tonic-gate 	/* Verify message integrity. */
2457c478bd9Sstevel@tonic-gate 	if (mp->b_wptr - mp->b_rptr < sizeof (struct T_optmgmt_req))
2467c478bd9Sstevel@tonic-gate 		goto bad_opt;
2477c478bd9Sstevel@tonic-gate 	/* Verify MGMT_flags legal */
2487c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
2497c478bd9Sstevel@tonic-gate 	case T_DEFAULT:
2507c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
2517c478bd9Sstevel@tonic-gate 	case T_CURRENT:
2527c478bd9Sstevel@tonic-gate 	case T_CHECK:
2537c478bd9Sstevel@tonic-gate 		/* OK - legal request flags */
2547c478bd9Sstevel@tonic-gate 		break;
2557c478bd9Sstevel@tonic-gate 	default:
2567c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
2577c478bd9Sstevel@tonic-gate 		return (0);
2587c478bd9Sstevel@tonic-gate 	}
2597c478bd9Sstevel@tonic-gate 	if (tor->MGMT_flags == T_DEFAULT) {
2607c478bd9Sstevel@tonic-gate 		/* Is it a request for default option settings? */
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 		/*
2637c478bd9Sstevel@tonic-gate 		 * Note: XXX TLI and TPI specification was unclear about
2647c478bd9Sstevel@tonic-gate 		 * semantics of T_DEFAULT and the following historical note
2657c478bd9Sstevel@tonic-gate 		 * and its interpretation is incorrect (it implies a request
2667c478bd9Sstevel@tonic-gate 		 * for default values of only the identified options not all.
2677c478bd9Sstevel@tonic-gate 		 * The semantics have been explained better in XTI spec.)
2687c478bd9Sstevel@tonic-gate 		 * However, we do not modify (comment or code) here to keep
2697c478bd9Sstevel@tonic-gate 		 * compatibility.
2707c478bd9Sstevel@tonic-gate 		 * We can rethink this if it ever becomes an issue.
2717c478bd9Sstevel@tonic-gate 		 * ----historical comment start------
2727c478bd9Sstevel@tonic-gate 		 * As we understand it, the input buffer is meaningless
2737c478bd9Sstevel@tonic-gate 		 * so we ditch the message.  A T_DEFAULT request is a
2747c478bd9Sstevel@tonic-gate 		 * request to obtain a buffer containing defaults for
2757c478bd9Sstevel@tonic-gate 		 * all supported options, so we allocate a maximum length
2767c478bd9Sstevel@tonic-gate 		 * reply.
2777c478bd9Sstevel@tonic-gate 		 * ----historical comment end -------
2787c478bd9Sstevel@tonic-gate 		 */
2797c478bd9Sstevel@tonic-gate 		/* T_DEFAULT not passed down */
2807c478bd9Sstevel@tonic-gate 		ASSERT(topmost_tpiprovider == B_TRUE);
2817c478bd9Sstevel@tonic-gate 		freemsg(mp);
2827c478bd9Sstevel@tonic-gate 		max_optbuf_len = optcom_max_optbuf_len(opt_arr,
2837c478bd9Sstevel@tonic-gate 		    opt_arr_cnt);
2847c478bd9Sstevel@tonic-gate 		mp = allocb(max_optbuf_len, BPRI_MED);
2857c478bd9Sstevel@tonic-gate 		if (!mp) {
2867c478bd9Sstevel@tonic-gate no_mem:;
2877c478bd9Sstevel@tonic-gate 			optcom_err_ack(q, mp, TSYSERR, ENOMEM);
2887c478bd9Sstevel@tonic-gate 			return (0);
2897c478bd9Sstevel@tonic-gate 		}
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 		/* Initialize the T_optmgmt_ack header. */
2927c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)mp->b_rptr;
2937c478bd9Sstevel@tonic-gate 		bzero((char *)toa, max_optbuf_len);
2947c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_ACK;
2957c478bd9Sstevel@tonic-gate 		toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
2967c478bd9Sstevel@tonic-gate 		/* TODO: Is T_DEFAULT the right thing to put in MGMT_flags? */
2977c478bd9Sstevel@tonic-gate 		toa->MGMT_flags = T_DEFAULT;
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 		/* Now walk the table of options passed in */
3007c478bd9Sstevel@tonic-gate 		opt = (struct opthdr *)&toa[1];
3017c478bd9Sstevel@tonic-gate 		for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
3027c478bd9Sstevel@tonic-gate 			/*
3037c478bd9Sstevel@tonic-gate 			 * All the options in the table of options passed
3047c478bd9Sstevel@tonic-gate 			 * in are by definition supported by the protocol
3057c478bd9Sstevel@tonic-gate 			 * calling this function.
3067c478bd9Sstevel@tonic-gate 			 */
3077c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr))
3087c478bd9Sstevel@tonic-gate 				continue;
3097c478bd9Sstevel@tonic-gate 			opt->level = optd->opdes_level;
3107c478bd9Sstevel@tonic-gate 			opt->name = optd->opdes_name;
3117c478bd9Sstevel@tonic-gate 			if (!(optd->opdes_props & OP_DEF_FN) ||
3127c478bd9Sstevel@tonic-gate 			    ((len = (*deffn)(q, opt->level,
3137c478bd9Sstevel@tonic-gate 				opt->name, (uchar_t *)&opt[1])) < 0)) {
3147c478bd9Sstevel@tonic-gate 				/*
3157c478bd9Sstevel@tonic-gate 				 * Fill length and value from table.
3167c478bd9Sstevel@tonic-gate 				 *
3177c478bd9Sstevel@tonic-gate 				 * Default value not instantiated from function
3187c478bd9Sstevel@tonic-gate 				 * (or the protocol specific function failed it;
3197c478bd9Sstevel@tonic-gate 				 * In this interpretation of T_DEFAULT, this is
3207c478bd9Sstevel@tonic-gate 				 * the best we can do)
3217c478bd9Sstevel@tonic-gate 				 */
3227c478bd9Sstevel@tonic-gate 				switch (optd->opdes_size) {
3237c478bd9Sstevel@tonic-gate 				/*
3247c478bd9Sstevel@tonic-gate 				 * Since options are guaranteed aligned only
3257c478bd9Sstevel@tonic-gate 				 * on a 4 byte boundary (t_scalar_t) any
3267c478bd9Sstevel@tonic-gate 				 * option that is greater in size will default
3277c478bd9Sstevel@tonic-gate 				 * to the bcopy below
3287c478bd9Sstevel@tonic-gate 				 */
3297c478bd9Sstevel@tonic-gate 				case sizeof (int32_t):
3307c478bd9Sstevel@tonic-gate 					*(int32_t *)&opt[1] =
3317c478bd9Sstevel@tonic-gate 					    (int32_t)optd->opdes_default;
3327c478bd9Sstevel@tonic-gate 					break;
3337c478bd9Sstevel@tonic-gate 				case sizeof (int16_t):
3347c478bd9Sstevel@tonic-gate 					*(int16_t *)&opt[1] =
3357c478bd9Sstevel@tonic-gate 					    (int16_t)optd->opdes_default;
3367c478bd9Sstevel@tonic-gate 					break;
3377c478bd9Sstevel@tonic-gate 				case sizeof (int8_t):
3387c478bd9Sstevel@tonic-gate 					*(int8_t *)&opt[1] =
3397c478bd9Sstevel@tonic-gate 					    (int8_t)optd->opdes_default;
3407c478bd9Sstevel@tonic-gate 					break;
3417c478bd9Sstevel@tonic-gate 				default:
3427c478bd9Sstevel@tonic-gate 					/*
3437c478bd9Sstevel@tonic-gate 					 * other length but still assume
3447c478bd9Sstevel@tonic-gate 					 * fixed - use bcopy
3457c478bd9Sstevel@tonic-gate 					 */
3467c478bd9Sstevel@tonic-gate 					bcopy(optd->opdes_defbuf,
3477c478bd9Sstevel@tonic-gate 					    &opt[1], optd->opdes_size);
3487c478bd9Sstevel@tonic-gate 					break;
3497c478bd9Sstevel@tonic-gate 				}
3507c478bd9Sstevel@tonic-gate 				opt->len = optd->opdes_size;
3517c478bd9Sstevel@tonic-gate 			}
3527c478bd9Sstevel@tonic-gate 			else
3537c478bd9Sstevel@tonic-gate 				opt->len = (t_uscalar_t)len;
3547c478bd9Sstevel@tonic-gate 			opt = (struct opthdr *)((char *)&opt[1] +
3557c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt->len));
3567c478bd9Sstevel@tonic-gate 		}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 		/* Now record the final length. */
3597c478bd9Sstevel@tonic-gate 		toa->OPT_length = (t_scalar_t)((char *)opt - (char *)&toa[1]);
3607c478bd9Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)opt;
3617c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PCPROTO;
3627c478bd9Sstevel@tonic-gate 		/* Ship it back. */
3637c478bd9Sstevel@tonic-gate 		qreply(q, mp);
3647c478bd9Sstevel@tonic-gate 		return (0);
3657c478bd9Sstevel@tonic-gate 	}
3667c478bd9Sstevel@tonic-gate 	/* T_DEFAULT processing complete - no more T_DEFAULT */
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	/*
3697c478bd9Sstevel@tonic-gate 	 * For T_NEGOTIATE, T_CURRENT, and T_CHECK requests, we make a
3707c478bd9Sstevel@tonic-gate 	 * pass through the input buffer validating the details and
3717c478bd9Sstevel@tonic-gate 	 * making sure each option is supported by the protocol.
3727c478bd9Sstevel@tonic-gate 	 */
3737c478bd9Sstevel@tonic-gate 	if ((opt_start = (struct opthdr *)mi_offset_param(mp,
3747c478bd9Sstevel@tonic-gate 	    tor->OPT_offset, tor->OPT_length)) == NULL)
3757c478bd9Sstevel@tonic-gate 		goto bad_opt;
3767c478bd9Sstevel@tonic-gate 	if (!__TPI_OPT_ISALIGNED(opt_start))
3777c478bd9Sstevel@tonic-gate 		goto bad_opt;
3787c478bd9Sstevel@tonic-gate 
3797c478bd9Sstevel@tonic-gate 	opt_end = (struct opthdr *)((uchar_t *)opt_start +
3807c478bd9Sstevel@tonic-gate 	    tor->OPT_length);
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	for (opt = opt_start; opt < opt_end; opt = next_opt) {
3837c478bd9Sstevel@tonic-gate 		/*
3847c478bd9Sstevel@tonic-gate 		 * Verify we have room to reference the option header
3857c478bd9Sstevel@tonic-gate 		 * fields in the option buffer.
3867c478bd9Sstevel@tonic-gate 		 */
3877c478bd9Sstevel@tonic-gate 		if ((uchar_t *)opt + sizeof (struct opthdr) >
3887c478bd9Sstevel@tonic-gate 		    (uchar_t *)opt_end)
3897c478bd9Sstevel@tonic-gate 			goto bad_opt;
3907c478bd9Sstevel@tonic-gate 		/*
3917c478bd9Sstevel@tonic-gate 		 * We now compute pointer to next option in buffer 'next_opt'
3927c478bd9Sstevel@tonic-gate 		 * The next_opt computation above below 'opt->len' initialized
3937c478bd9Sstevel@tonic-gate 		 * by application which cannot be trusted. The usual value
3947c478bd9Sstevel@tonic-gate 		 * too large will be captured by the loop termination condition
3957c478bd9Sstevel@tonic-gate 		 * above. We check for the following which it will miss.
3967c478bd9Sstevel@tonic-gate 		 * 	-pointer space wraparound arithmetic overflow
3977c478bd9Sstevel@tonic-gate 		 *	-last option in buffer with 'opt->len' being too large
3987c478bd9Sstevel@tonic-gate 		 *	 (only reason 'next_opt' should equal or exceed
3997c478bd9Sstevel@tonic-gate 		 *	 'opt_end' for last option is roundup unless length is
4007c478bd9Sstevel@tonic-gate 		 *	 too-large/invalid)
4017c478bd9Sstevel@tonic-gate 		 */
4027c478bd9Sstevel@tonic-gate 		next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
4037c478bd9Sstevel@tonic-gate 		    _TPI_ALIGN_OPT(opt->len));
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 		if ((uchar_t *)next_opt < (uchar_t *)&opt[1] ||
4067c478bd9Sstevel@tonic-gate 		    ((next_opt >= opt_end) &&
4077c478bd9Sstevel@tonic-gate 			(((uchar_t *)next_opt - (uchar_t *)opt_end) >=
4087c478bd9Sstevel@tonic-gate 			    __TPI_ALIGN_SIZE)))
4097c478bd9Sstevel@tonic-gate 			goto bad_opt;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 		/* sanity check */
4127c478bd9Sstevel@tonic-gate 		if (opt->name == T_ALLOPT)
4137c478bd9Sstevel@tonic-gate 			goto bad_opt;
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 		/* Find the option in the opt_arr. */
4167c478bd9Sstevel@tonic-gate 		if ((optd = opt_chk_lookup(opt->level, opt->name,
4177c478bd9Sstevel@tonic-gate 		    opt_arr, opt_arr_cnt)) == NULL) {
4187c478bd9Sstevel@tonic-gate 			/*
4197c478bd9Sstevel@tonic-gate 			 * Not found, that is a bad thing if
4207c478bd9Sstevel@tonic-gate 			 * the caller is a tpi provider
4217c478bd9Sstevel@tonic-gate 			 */
4227c478bd9Sstevel@tonic-gate 			if (topmost_tpiprovider)
4237c478bd9Sstevel@tonic-gate 				goto bad_opt;
4247c478bd9Sstevel@tonic-gate 			else
4257c478bd9Sstevel@tonic-gate 				continue; /* skip unmodified */
4267c478bd9Sstevel@tonic-gate 		}
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 		/* Additional checks dependent on operation. */
4297c478bd9Sstevel@tonic-gate 		switch (tor->MGMT_flags) {
4307c478bd9Sstevel@tonic-gate 		case T_NEGOTIATE:
4317c478bd9Sstevel@tonic-gate 			if (!OA_WRITE_OR_EXECUTE(optd, cr)) {
4327c478bd9Sstevel@tonic-gate 				/* can't negotiate option */
4337c478bd9Sstevel@tonic-gate 				if (!(OA_MATCHED_PRIV(optd, cr)) &&
4347c478bd9Sstevel@tonic-gate 				    OA_WX_ANYPRIV(optd)) {
4357c478bd9Sstevel@tonic-gate 					/*
4367c478bd9Sstevel@tonic-gate 					 * not privileged but privilege
4377c478bd9Sstevel@tonic-gate 					 * will help negotiate option.
4387c478bd9Sstevel@tonic-gate 					 */
4397c478bd9Sstevel@tonic-gate 					optcom_err_ack(q, mp, TACCES, 0);
4407c478bd9Sstevel@tonic-gate 					return (0);
4417c478bd9Sstevel@tonic-gate 				} else
4427c478bd9Sstevel@tonic-gate 					goto bad_opt;
4437c478bd9Sstevel@tonic-gate 			}
4447c478bd9Sstevel@tonic-gate 			/*
4457c478bd9Sstevel@tonic-gate 			 * Verify size for options
4467c478bd9Sstevel@tonic-gate 			 * Note: For retaining compatibility with historical
4477c478bd9Sstevel@tonic-gate 			 * behavior, variable lengths options will have their
4487c478bd9Sstevel@tonic-gate 			 * length verified in the setfn() processing.
4497c478bd9Sstevel@tonic-gate 			 * In order to be compatible with SunOS 4.X we return
4507c478bd9Sstevel@tonic-gate 			 * EINVAL errors for bad lengths.
4517c478bd9Sstevel@tonic-gate 			 */
4527c478bd9Sstevel@tonic-gate 			if (!(optd->opdes_props & OP_VARLEN)) {
4537c478bd9Sstevel@tonic-gate 				/* fixed length - size must match */
4547c478bd9Sstevel@tonic-gate 				if (opt->len != optd->opdes_size) {
4557c478bd9Sstevel@tonic-gate 					optcom_err_ack(q, mp, TSYSERR, EINVAL);
4567c478bd9Sstevel@tonic-gate 					return (0);
4577c478bd9Sstevel@tonic-gate 				}
4587c478bd9Sstevel@tonic-gate 			}
4597c478bd9Sstevel@tonic-gate 			break;
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 		case T_CHECK:
4627c478bd9Sstevel@tonic-gate 			if (!OA_RWX_ANYPRIV(optd))
4637c478bd9Sstevel@tonic-gate 				/* any of "rwx" permission but not not none */
4647c478bd9Sstevel@tonic-gate 				goto bad_opt;
4657c478bd9Sstevel@tonic-gate 			/*
4667c478bd9Sstevel@tonic-gate 			 * XXX Since T_CURRENT was not there in TLI and the
4677c478bd9Sstevel@tonic-gate 			 * official TLI inspired TPI standard, getsockopt()
4687c478bd9Sstevel@tonic-gate 			 * API uses T_CHECK (for T_CURRENT semantics)
4697c478bd9Sstevel@tonic-gate 			 * The following fallthru makes sense because of its
4707c478bd9Sstevel@tonic-gate 			 * historical use as semantic equivalent to T_CURRENT.
4717c478bd9Sstevel@tonic-gate 			 */
4727c478bd9Sstevel@tonic-gate 			/* FALLTHRU */
4737c478bd9Sstevel@tonic-gate 		case T_CURRENT:
4747c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr)) {
4757c478bd9Sstevel@tonic-gate 				/* can't read option value */
4767c478bd9Sstevel@tonic-gate 				if (!(OA_MATCHED_PRIV(optd, cr)) &&
4777c478bd9Sstevel@tonic-gate 				    OA_R_ANYPRIV(optd)) {
4787c478bd9Sstevel@tonic-gate 					/*
4797c478bd9Sstevel@tonic-gate 					 * not privileged but privilege
4807c478bd9Sstevel@tonic-gate 					 * will help in reading option value.
4817c478bd9Sstevel@tonic-gate 					 */
4827c478bd9Sstevel@tonic-gate 					optcom_err_ack(q, mp, TACCES, 0);
4837c478bd9Sstevel@tonic-gate 					return (0);
4847c478bd9Sstevel@tonic-gate 				} else
4857c478bd9Sstevel@tonic-gate 					goto bad_opt;
4867c478bd9Sstevel@tonic-gate 			}
4877c478bd9Sstevel@tonic-gate 			break;
4887c478bd9Sstevel@tonic-gate 
4897c478bd9Sstevel@tonic-gate 		default:
4907c478bd9Sstevel@tonic-gate 			optcom_err_ack(q, mp, TBADFLAG, 0);
4917c478bd9Sstevel@tonic-gate 			return (0);
4927c478bd9Sstevel@tonic-gate 		}
4937c478bd9Sstevel@tonic-gate 		/* We liked it.  Keep going. */
4947c478bd9Sstevel@tonic-gate 	} /* end for loop scanning option buffer */
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	/* Now complete the operation as required. */
4977c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
4987c478bd9Sstevel@tonic-gate 	case T_CHECK:
4997c478bd9Sstevel@tonic-gate 		/*
5007c478bd9Sstevel@tonic-gate 		 * Historically used same as T_CURRENT (which was added to
5017c478bd9Sstevel@tonic-gate 		 * standard later). Code retained for compatibility.
5027c478bd9Sstevel@tonic-gate 		 */
5037c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
5047c478bd9Sstevel@tonic-gate 	case T_CURRENT:
5057c478bd9Sstevel@tonic-gate 		/*
5067c478bd9Sstevel@tonic-gate 		 * Allocate a maximum size reply.  Perhaps we are supposed to
5077c478bd9Sstevel@tonic-gate 		 * assume that the input buffer includes space for the answers
5087c478bd9Sstevel@tonic-gate 		 * as well as the opthdrs, but we don't know that for sure.
5097c478bd9Sstevel@tonic-gate 		 * So, instead, we create a new output buffer, using the
5107c478bd9Sstevel@tonic-gate 		 * input buffer only as a list of options.
5117c478bd9Sstevel@tonic-gate 		 */
5127c478bd9Sstevel@tonic-gate 		max_optbuf_len = optcom_max_optbuf_len(opt_arr,
5137c478bd9Sstevel@tonic-gate 		    opt_arr_cnt);
5147c478bd9Sstevel@tonic-gate 		mp1 = allocb_cred(max_optbuf_len, cr);
5157c478bd9Sstevel@tonic-gate 		if (!mp1)
5167c478bd9Sstevel@tonic-gate 			goto no_mem;
5177c478bd9Sstevel@tonic-gate 		/* Initialize the header. */
5187c478bd9Sstevel@tonic-gate 		mp1->b_datap->db_type = M_PCPROTO;
5197c478bd9Sstevel@tonic-gate 		mp1->b_wptr = &mp1->b_rptr[sizeof (struct T_optmgmt_ack)];
5207c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)mp1->b_rptr;
5217c478bd9Sstevel@tonic-gate 		toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
5227c478bd9Sstevel@tonic-gate 		toa->MGMT_flags = tor->MGMT_flags;
5237c478bd9Sstevel@tonic-gate 		/*
5247c478bd9Sstevel@tonic-gate 		 * Walk through the input buffer again, this time adding
5257c478bd9Sstevel@tonic-gate 		 * entries to the output buffer for each option requested.
5267c478bd9Sstevel@tonic-gate 		 * Note, sanity of option header, last option etc, verified
5277c478bd9Sstevel@tonic-gate 		 * in first pass.
5287c478bd9Sstevel@tonic-gate 		 */
5297c478bd9Sstevel@tonic-gate 		opt1 = (struct opthdr *)&toa[1];
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 		for (opt = opt_start; opt < opt_end; opt = next_opt) {
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 		    next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
5347c478bd9Sstevel@tonic-gate 			_TPI_ALIGN_OPT(opt->len));
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 			opt1->name = opt->name;
5377c478bd9Sstevel@tonic-gate 			opt1->level = opt->level;
5387c478bd9Sstevel@tonic-gate 			len = (*getfn)(q, opt->level,
5397c478bd9Sstevel@tonic-gate 			    opt->name, (uchar_t *)&opt1[1]);
5407c478bd9Sstevel@tonic-gate 			/*
5417c478bd9Sstevel@tonic-gate 			 * Failure means option is not recognized. Copy input
5427c478bd9Sstevel@tonic-gate 			 * buffer as is
5437c478bd9Sstevel@tonic-gate 			 */
5447c478bd9Sstevel@tonic-gate 			if (len < 0) {
5457c478bd9Sstevel@tonic-gate 				opt1->len = opt->len;
5467c478bd9Sstevel@tonic-gate 				bcopy(&opt[1], &opt1[1], opt->len);
5477c478bd9Sstevel@tonic-gate 				/*
548ff550d0eSmasputra 				 * Pass the option down to IP only
549ff550d0eSmasputra 				 * if TCP hasn't processed it.
5507c478bd9Sstevel@tonic-gate 				 */
551ff550d0eSmasputra 				if (is_tcp)
5527c478bd9Sstevel@tonic-gate 					pass_to_ip = B_TRUE;
553ff550d0eSmasputra 			} else {
5547c478bd9Sstevel@tonic-gate 				opt1->len = (t_uscalar_t)len;
555ff550d0eSmasputra 			}
5567c478bd9Sstevel@tonic-gate 			opt1 = (struct opthdr *)((uchar_t *)&opt1[1] +
5577c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt1->len));
5587c478bd9Sstevel@tonic-gate 		} /* end for loop */
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate 		/* Record the final length. */
5617c478bd9Sstevel@tonic-gate 		toa->OPT_length = (t_scalar_t)((uchar_t *)opt1 -
5627c478bd9Sstevel@tonic-gate 		    (uchar_t *)&toa[1]);
5637c478bd9Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)opt1;
5647c478bd9Sstevel@tonic-gate 		/* Ditch the input buffer. */
5657c478bd9Sstevel@tonic-gate 		freemsg(mp);
5667c478bd9Sstevel@tonic-gate 		mp = mp1;
5677c478bd9Sstevel@tonic-gate 		/* Always let the next module look at the option. */
5687c478bd9Sstevel@tonic-gate 		pass_to_next = B_TRUE;
5697c478bd9Sstevel@tonic-gate 		break;
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
5727c478bd9Sstevel@tonic-gate 		first_mp = allocb(sizeof (opt_restart_t), BPRI_LO);
5737c478bd9Sstevel@tonic-gate 		if (first_mp == NULL) {
5747c478bd9Sstevel@tonic-gate 			optcom_err_ack(q, mp, TSYSERR, ENOMEM);
5757c478bd9Sstevel@tonic-gate 			return (0);
5767c478bd9Sstevel@tonic-gate 		}
5777c478bd9Sstevel@tonic-gate 		first_mp->b_datap->db_type = M_CTL;
5787c478bd9Sstevel@tonic-gate 		or = (opt_restart_t *)first_mp->b_rptr;
5797c478bd9Sstevel@tonic-gate 		or->or_start = opt_start;
5807c478bd9Sstevel@tonic-gate 		or->or_end =  opt_end;
5817c478bd9Sstevel@tonic-gate 		or->or_type = T_SVR4_OPTMGMT_REQ;
5827c478bd9Sstevel@tonic-gate 		or->or_private = 0;
5837c478bd9Sstevel@tonic-gate 		first_mp->b_cont = mp;
5847c478bd9Sstevel@tonic-gate restart:
5857c478bd9Sstevel@tonic-gate 		/*
5867c478bd9Sstevel@tonic-gate 		 * Here we are expecting that the response buffer is exactly
5877c478bd9Sstevel@tonic-gate 		 * the same size as the input buffer.  We pass each opthdr
5887c478bd9Sstevel@tonic-gate 		 * to the protocol's set function.  If the protocol doesn't
5897c478bd9Sstevel@tonic-gate 		 * like it, it can update the value in it return argument.
5907c478bd9Sstevel@tonic-gate 		 */
5917c478bd9Sstevel@tonic-gate 		/*
5927c478bd9Sstevel@tonic-gate 		 * Pass each negotiated option through the protocol set
5937c478bd9Sstevel@tonic-gate 		 * function.
5947c478bd9Sstevel@tonic-gate 		 * Note: sanity check on option header values done in first
5957c478bd9Sstevel@tonic-gate 		 * pass and not repeated here.
5967c478bd9Sstevel@tonic-gate 		 */
5977c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)tor;
5987c478bd9Sstevel@tonic-gate 
5997c478bd9Sstevel@tonic-gate 		for (opt = is_restart ? restart_opt: opt_start; opt < opt_end;
6007c478bd9Sstevel@tonic-gate 		    opt = next_opt) {
6017c478bd9Sstevel@tonic-gate 			int error;
6027c478bd9Sstevel@tonic-gate 
6037c478bd9Sstevel@tonic-gate 			/*
6047c478bd9Sstevel@tonic-gate 			 * Point to the current option in or, in case this
6057c478bd9Sstevel@tonic-gate 			 * option has to be restarted later on
6067c478bd9Sstevel@tonic-gate 			 */
6077c478bd9Sstevel@tonic-gate 			or->or_ropt = opt;
6087c478bd9Sstevel@tonic-gate 			next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
6097c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt->len));
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 			error = (*setfn)(q, SETFN_OPTCOM_NEGOTIATE,
6127c478bd9Sstevel@tonic-gate 			    opt->level, opt->name,
6137c478bd9Sstevel@tonic-gate 			    opt->len, (uchar_t *)&opt[1],
6147c478bd9Sstevel@tonic-gate 			    &opt->len, (uchar_t *)&opt[1], NULL, cr, first_mp);
6157c478bd9Sstevel@tonic-gate 			/*
6167c478bd9Sstevel@tonic-gate 			 * Treat positive "errors" as real.
6177c478bd9Sstevel@tonic-gate 			 * Note: negative errors are to be treated as
6187c478bd9Sstevel@tonic-gate 			 * non-fatal by svr4_optcom_req() and are
6197c478bd9Sstevel@tonic-gate 			 * returned by setfn() when it is passed an
6207c478bd9Sstevel@tonic-gate 			 * option it does not handle. Since the option
6217c478bd9Sstevel@tonic-gate 			 * passed opt_chk_lookup(), it is implied that
6227c478bd9Sstevel@tonic-gate 			 * it is valid but was either handled upstream
6237c478bd9Sstevel@tonic-gate 			 * or will be handled downstream.
6247c478bd9Sstevel@tonic-gate 			 */
6257c478bd9Sstevel@tonic-gate 			if (error == EINPROGRESS) {
6267c478bd9Sstevel@tonic-gate 				/*
6277c478bd9Sstevel@tonic-gate 				 * The message is queued and will be
6287c478bd9Sstevel@tonic-gate 				 * reprocessed later. Typically ip queued
6297c478bd9Sstevel@tonic-gate 				 * the message to get some exclusive conditions
6307c478bd9Sstevel@tonic-gate 				 * and later on calls this func again.
6317c478bd9Sstevel@tonic-gate 				 */
6327c478bd9Sstevel@tonic-gate 				return (EINPROGRESS);
6337c478bd9Sstevel@tonic-gate 			} else if (error > 0) {
6347c478bd9Sstevel@tonic-gate 				optcom_err_ack(q, mp, TSYSERR, error);
6357c478bd9Sstevel@tonic-gate 				freeb(first_mp);
6367c478bd9Sstevel@tonic-gate 				return (0);
637ff550d0eSmasputra 			} else if (error < 0 && is_tcp) {
6387c478bd9Sstevel@tonic-gate 				/*
639ff550d0eSmasputra 				 * Pass the option down to IP only
640ff550d0eSmasputra 				 * if TCP hasn't processed it.
6417c478bd9Sstevel@tonic-gate 				 */
6427c478bd9Sstevel@tonic-gate 				pass_to_ip = B_TRUE;
6437c478bd9Sstevel@tonic-gate 			}
6447c478bd9Sstevel@tonic-gate 		}
6457c478bd9Sstevel@tonic-gate 		/* Done with the restart control mp. */
6467c478bd9Sstevel@tonic-gate 		freeb(first_mp);
6477c478bd9Sstevel@tonic-gate 		pass_to_next = B_TRUE;
6487c478bd9Sstevel@tonic-gate 		break;
6497c478bd9Sstevel@tonic-gate 	default:
6507c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
6517c478bd9Sstevel@tonic-gate 		return (0);
6527c478bd9Sstevel@tonic-gate 	}
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	if (pass_to_next && (q->q_next != NULL || pass_to_ip)) {
6557c478bd9Sstevel@tonic-gate 		/* Send it down to the next module and let it reply */
6567c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_SVR4_OPTMGMT_REQ; /* Changed by IP to ACK */
6577c478bd9Sstevel@tonic-gate 		if (q->q_next != NULL)
6587c478bd9Sstevel@tonic-gate 			putnext(q, mp);
6597c478bd9Sstevel@tonic-gate 		else
6607c478bd9Sstevel@tonic-gate 			ip_output(Q_TO_CONN(q), mp, q, IP_WPUT);
6617c478bd9Sstevel@tonic-gate 	} else {
6627c478bd9Sstevel@tonic-gate 		/* Set common fields in the header. */
6637c478bd9Sstevel@tonic-gate 		toa->MGMT_flags = T_SUCCESS;
6647c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PCPROTO;
6657c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_ACK;
6667c478bd9Sstevel@tonic-gate 		qreply(q, mp);
6677c478bd9Sstevel@tonic-gate 	}
6687c478bd9Sstevel@tonic-gate 	return (0);
6697c478bd9Sstevel@tonic-gate bad_opt:;
6707c478bd9Sstevel@tonic-gate 	optcom_err_ack(q, mp, TBADOPT, 0);
6717c478bd9Sstevel@tonic-gate 	return (0);
6727c478bd9Sstevel@tonic-gate }
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate /*
6757c478bd9Sstevel@tonic-gate  * New optcom_req inspired by TPI/XTI semantics
6767c478bd9Sstevel@tonic-gate  */
6777c478bd9Sstevel@tonic-gate int
6787c478bd9Sstevel@tonic-gate tpi_optcom_req(queue_t *q, mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp)
6797c478bd9Sstevel@tonic-gate {
6807c478bd9Sstevel@tonic-gate 	t_scalar_t t_error;
6817c478bd9Sstevel@tonic-gate 	mblk_t *toa_mp;
6827c478bd9Sstevel@tonic-gate 	boolean_t pass_to_next;
6837c478bd9Sstevel@tonic-gate 	size_t toa_len;
6847c478bd9Sstevel@tonic-gate 	struct T_optmgmt_ack *toa;
6857c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor =
6867c478bd9Sstevel@tonic-gate 	    (struct T_optmgmt_req *)mp->b_rptr;
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	opt_restart_t *or;
6897c478bd9Sstevel@tonic-gate 	boolean_t is_restart = B_FALSE;
6907c478bd9Sstevel@tonic-gate 	mblk_t	*first_mp = NULL;
6917c478bd9Sstevel@tonic-gate 	t_uscalar_t worst_status;
6927c478bd9Sstevel@tonic-gate 	boolean_t queued_status;
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	/*
6957c478bd9Sstevel@tonic-gate 	 * Allocate M_CTL and prepend to the packet for restarting this
6967c478bd9Sstevel@tonic-gate 	 * option if needed. IP may need to queue and restart the option
6977c478bd9Sstevel@tonic-gate 	 * if it cannot obtain exclusive conditions immediately. Please see
6987c478bd9Sstevel@tonic-gate 	 * IP-MT notes before the start of svr4_optcom_req
6997c478bd9Sstevel@tonic-gate 	 */
7007c478bd9Sstevel@tonic-gate 	if (mp->b_datap->db_type == M_CTL) {
7017c478bd9Sstevel@tonic-gate 		is_restart = B_TRUE;
7027c478bd9Sstevel@tonic-gate 		first_mp = mp;
7037c478bd9Sstevel@tonic-gate 		toa_mp = mp->b_cont;
7047c478bd9Sstevel@tonic-gate 		mp = toa_mp->b_cont;
7057c478bd9Sstevel@tonic-gate 		ASSERT(mp->b_wptr - mp->b_rptr >=
7067c478bd9Sstevel@tonic-gate 		    sizeof (struct T_optmgmt_req));
7077c478bd9Sstevel@tonic-gate 		tor = (struct T_optmgmt_req *)mp->b_rptr;
7087c478bd9Sstevel@tonic-gate 		ASSERT(tor->MGMT_flags == T_NEGOTIATE);
7097c478bd9Sstevel@tonic-gate 
7107c478bd9Sstevel@tonic-gate 		or = (opt_restart_t *)first_mp->b_rptr;
7117c478bd9Sstevel@tonic-gate 		goto restart;
7127c478bd9Sstevel@tonic-gate 	}
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	/* Verify message integrity. */
7157c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (struct T_optmgmt_req)) {
7167c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADOPT, 0);
7177c478bd9Sstevel@tonic-gate 		return (0);
7187c478bd9Sstevel@tonic-gate 	}
7197c478bd9Sstevel@tonic-gate 
7207c478bd9Sstevel@tonic-gate 	/* Verify MGMT_flags legal */
7217c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
7227c478bd9Sstevel@tonic-gate 	case T_DEFAULT:
7237c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
7247c478bd9Sstevel@tonic-gate 	case T_CURRENT:
7257c478bd9Sstevel@tonic-gate 	case T_CHECK:
7267c478bd9Sstevel@tonic-gate 		/* OK - legal request flags */
7277c478bd9Sstevel@tonic-gate 		break;
7287c478bd9Sstevel@tonic-gate 	default:
7297c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
7307c478bd9Sstevel@tonic-gate 		return (0);
7317c478bd9Sstevel@tonic-gate 	}
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 	/*
7347c478bd9Sstevel@tonic-gate 	 * In this design, there are two passes required on the input buffer
7357c478bd9Sstevel@tonic-gate 	 * mostly to accomodate variable length options and "T_ALLOPT" option
7367c478bd9Sstevel@tonic-gate 	 * which has the semantics "all options of the specified level".
7377c478bd9Sstevel@tonic-gate 	 *
7387c478bd9Sstevel@tonic-gate 	 * For T_DEFAULT, T_NEGOTIATE, T_CURRENT, and T_CHECK requests, we make
7397c478bd9Sstevel@tonic-gate 	 * a pass through the input buffer validating the details and making
7407c478bd9Sstevel@tonic-gate 	 * sure each option is supported by the protocol. We also determine the
7417c478bd9Sstevel@tonic-gate 	 * length of the option buffer to return. (Variable length options and
7427c478bd9Sstevel@tonic-gate 	 * T_ALLOPT mean that length can be different for output buffer).
7437c478bd9Sstevel@tonic-gate 	 */
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	pass_to_next = B_FALSE;	/* initial value */
7467c478bd9Sstevel@tonic-gate 	toa_len = 0;		/* initial value */
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	/*
7497c478bd9Sstevel@tonic-gate 	 * First pass, we do the following
7507c478bd9Sstevel@tonic-gate 	 *	- estimate cumulative length needed for results
7517c478bd9Sstevel@tonic-gate 	 *	- set "status" field based on permissions, option header check
7527c478bd9Sstevel@tonic-gate 	 *	  etc.
7537c478bd9Sstevel@tonic-gate 	 *	- determine "pass_to_next" whether we need to send request to
7547c478bd9Sstevel@tonic-gate 	 *	  downstream module/driver.
7557c478bd9Sstevel@tonic-gate 	 */
7567c478bd9Sstevel@tonic-gate 	if ((t_error = process_topthdrs_first_pass(mp, cr, dbobjp,
7577c478bd9Sstevel@tonic-gate 	    &pass_to_next, &toa_len)) != 0) {
7587c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, t_error, 0);
7597c478bd9Sstevel@tonic-gate 		return (0);
7607c478bd9Sstevel@tonic-gate 	}
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 	/*
7637c478bd9Sstevel@tonic-gate 	 * A validation phase of the input buffer is done. We have also
7647c478bd9Sstevel@tonic-gate 	 * obtained the length requirement and and other details about the
7657c478bd9Sstevel@tonic-gate 	 * input and we liked input buffer so far.  We make another scan
7667c478bd9Sstevel@tonic-gate 	 * through the input now and generate the output necessary to complete
7677c478bd9Sstevel@tonic-gate 	 * the operation.
7687c478bd9Sstevel@tonic-gate 	 */
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	toa_mp = allocb_cred(toa_len, cr);
7717c478bd9Sstevel@tonic-gate 	if (!toa_mp) {
7727c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TSYSERR, ENOMEM);
7737c478bd9Sstevel@tonic-gate 		return (0);
7747c478bd9Sstevel@tonic-gate 	}
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	first_mp = allocb(sizeof (opt_restart_t), BPRI_LO);
7777c478bd9Sstevel@tonic-gate 	if (first_mp == NULL) {
7787c478bd9Sstevel@tonic-gate 		freeb(toa_mp);
7797c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TSYSERR, ENOMEM);
7807c478bd9Sstevel@tonic-gate 		return (0);
7817c478bd9Sstevel@tonic-gate 	}
7827c478bd9Sstevel@tonic-gate 	first_mp->b_datap->db_type = M_CTL;
7837c478bd9Sstevel@tonic-gate 	or = (opt_restart_t *)first_mp->b_rptr;
7847c478bd9Sstevel@tonic-gate 	/*
7857c478bd9Sstevel@tonic-gate 	 * Set initial values for generating output.
7867c478bd9Sstevel@tonic-gate 	 */
7877c478bd9Sstevel@tonic-gate 	or->or_worst_status = T_SUCCESS;
7887c478bd9Sstevel@tonic-gate 	or->or_type = T_OPTMGMT_REQ;
7897c478bd9Sstevel@tonic-gate 	or->or_private = 0;
7907c478bd9Sstevel@tonic-gate 	/* remaining fields fileed in do_options_second_pass */
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate restart:
7937c478bd9Sstevel@tonic-gate 	/*
7947c478bd9Sstevel@tonic-gate 	 * This routine makes another pass through the option buffer this
7957c478bd9Sstevel@tonic-gate 	 * time acting on the request based on "status" result in the
7967c478bd9Sstevel@tonic-gate 	 * first pass. It also performs "expansion" of T_ALLOPT into
7977c478bd9Sstevel@tonic-gate 	 * all options of a certain level and acts on each for this request.
7987c478bd9Sstevel@tonic-gate 	 */
7997c478bd9Sstevel@tonic-gate 	if ((t_error = do_options_second_pass(q, mp, toa_mp, cr, dbobjp,
8007c478bd9Sstevel@tonic-gate 	    first_mp, is_restart, &queued_status)) != 0) {
8017c478bd9Sstevel@tonic-gate 		freemsg(toa_mp);
8027c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, t_error, 0);
8037c478bd9Sstevel@tonic-gate 		return (0);
8047c478bd9Sstevel@tonic-gate 	}
8057c478bd9Sstevel@tonic-gate 	if (queued_status) {
8067c478bd9Sstevel@tonic-gate 		/* Option will be restarted */
8077c478bd9Sstevel@tonic-gate 		return (EINPROGRESS);
8087c478bd9Sstevel@tonic-gate 	}
8097c478bd9Sstevel@tonic-gate 	worst_status = or->or_worst_status;
8107c478bd9Sstevel@tonic-gate 	/* Done with the first mp */
8117c478bd9Sstevel@tonic-gate 	freeb(first_mp);
8127c478bd9Sstevel@tonic-gate 	toa_mp->b_cont = NULL;
8137c478bd9Sstevel@tonic-gate 
8147c478bd9Sstevel@tonic-gate 	/*
8157c478bd9Sstevel@tonic-gate 	 * Following code relies on the coincidence that T_optmgmt_req
8167c478bd9Sstevel@tonic-gate 	 * and T_optmgmt_ack are identical in binary representation
8177c478bd9Sstevel@tonic-gate 	 */
8187c478bd9Sstevel@tonic-gate 	toa = (struct T_optmgmt_ack *)toa_mp->b_rptr;
8197c478bd9Sstevel@tonic-gate 	toa->OPT_length = (t_scalar_t)(toa_mp->b_wptr - (toa_mp->b_rptr +
8207c478bd9Sstevel@tonic-gate 	    sizeof (struct T_optmgmt_ack)));
8217c478bd9Sstevel@tonic-gate 	toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 	toa->MGMT_flags = tor->MGMT_flags;
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 	freemsg(mp);		/* free input mblk */
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	/*
8297c478bd9Sstevel@tonic-gate 	 * If there is atleast one option that requires a downstream
8307c478bd9Sstevel@tonic-gate 	 * forwarding and if it is possible, we forward the message
8317c478bd9Sstevel@tonic-gate 	 * downstream. Else we ack it.
8327c478bd9Sstevel@tonic-gate 	 */
8337c478bd9Sstevel@tonic-gate 	if (pass_to_next && (q->q_next != NULL || dbobjp == &tcp_opt_obj)) {
8347c478bd9Sstevel@tonic-gate 		/*
8357c478bd9Sstevel@tonic-gate 		 * We pass it down as T_OPTMGMT_REQ. This code relies
8367c478bd9Sstevel@tonic-gate 		 * on the happy coincidence that T_optmgmt_req and
8377c478bd9Sstevel@tonic-gate 		 * T_optmgmt_ack are identical data structures
8387c478bd9Sstevel@tonic-gate 		 * at the binary representation level.
8397c478bd9Sstevel@tonic-gate 		 */
8407c478bd9Sstevel@tonic-gate 		toa_mp->b_datap->db_type = M_PROTO;
8417c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_REQ;
8427c478bd9Sstevel@tonic-gate 		if (q->q_next != NULL)
8437c478bd9Sstevel@tonic-gate 			putnext(q, toa_mp);
8447c478bd9Sstevel@tonic-gate 		else
8457c478bd9Sstevel@tonic-gate 			ip_output(Q_TO_CONN(q), toa_mp, q, IP_WPUT);
8467c478bd9Sstevel@tonic-gate 	} else {
8477c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_ACK;
8487c478bd9Sstevel@tonic-gate 		toa_mp->b_datap->db_type = M_PCPROTO;
8497c478bd9Sstevel@tonic-gate 		toa->MGMT_flags |= worst_status; /* XXX "worst" or "OR" TPI ? */
8507c478bd9Sstevel@tonic-gate 		qreply(q, toa_mp);
8517c478bd9Sstevel@tonic-gate 	}
8527c478bd9Sstevel@tonic-gate 	return (0);
8537c478bd9Sstevel@tonic-gate }
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 
8567c478bd9Sstevel@tonic-gate /*
8577c478bd9Sstevel@tonic-gate  * Following routine makes a pass through option buffer in mp and performs the
8587c478bd9Sstevel@tonic-gate  * following tasks.
8597c478bd9Sstevel@tonic-gate  *	- estimate cumulative length needed for results
8607c478bd9Sstevel@tonic-gate  *	- set "status" field based on permissions, option header check
8617c478bd9Sstevel@tonic-gate  *	  etc.
8627c478bd9Sstevel@tonic-gate  *	- determine "pass_to_next" whether we need to send request to
8637c478bd9Sstevel@tonic-gate  *	  downstream module/driver.
8647c478bd9Sstevel@tonic-gate  */
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate static t_scalar_t
8677c478bd9Sstevel@tonic-gate process_topthdrs_first_pass(mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp,
8687c478bd9Sstevel@tonic-gate     boolean_t *pass_to_nextp, size_t *toa_lenp)
8697c478bd9Sstevel@tonic-gate {
8707c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
8717c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
8727c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
8737c478bd9Sstevel@tonic-gate 	optlevel_t *valid_level_arr = dbobjp->odb_valid_levels_arr;
8747c478bd9Sstevel@tonic-gate 	uint_t valid_level_arr_cnt = dbobjp->odb_valid_levels_arr_cnt;
8757c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt;
8767c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt_start, *opt_end;
8777c478bd9Sstevel@tonic-gate 	opdes_t	*optd;
8787c478bd9Sstevel@tonic-gate 	size_t allopt_len;
8797c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor =
8807c478bd9Sstevel@tonic-gate 	    (struct T_optmgmt_req *)mp->b_rptr;
8817c478bd9Sstevel@tonic-gate 
8827c478bd9Sstevel@tonic-gate 	*toa_lenp = sizeof (struct T_optmgmt_ack); /* initial value */
8837c478bd9Sstevel@tonic-gate 
8847c478bd9Sstevel@tonic-gate 	if ((opt_start = (struct T_opthdr *)
8857c478bd9Sstevel@tonic-gate 	    mi_offset_param(mp, tor->OPT_offset, tor->OPT_length)) == NULL) {
8867c478bd9Sstevel@tonic-gate 		return (TBADOPT);
8877c478bd9Sstevel@tonic-gate 	}
8887c478bd9Sstevel@tonic-gate 	if (!__TPI_TOPT_ISALIGNED(opt_start))
8897c478bd9Sstevel@tonic-gate 		return (TBADOPT);
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 	opt_end = (struct T_opthdr *)((uchar_t *)opt_start + tor->OPT_length);
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 	for (opt = opt_start; opt && (opt < opt_end);
8947c478bd9Sstevel@tonic-gate 	    opt = _TPI_TOPT_NEXTHDR(opt_start, tor->OPT_length, opt)) {
8957c478bd9Sstevel@tonic-gate 		/*
8967c478bd9Sstevel@tonic-gate 		 * Validate the option for length and alignment
8977c478bd9Sstevel@tonic-gate 		 * before accessing anything in it.
8987c478bd9Sstevel@tonic-gate 		 */
8997c478bd9Sstevel@tonic-gate 		if (!(_TPI_TOPT_VALID(opt, opt_start, opt_end)))
9007c478bd9Sstevel@tonic-gate 			return (TBADOPT);
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 		/* Find the option in the opt_arr. */
9037c478bd9Sstevel@tonic-gate 		if (opt->name != T_ALLOPT) {
9047c478bd9Sstevel@tonic-gate 			optd = opt_chk_lookup(opt->level, opt->name,
9057c478bd9Sstevel@tonic-gate 			    opt_arr, opt_arr_cnt);
9067c478bd9Sstevel@tonic-gate 			if (optd == NULL) {
9077c478bd9Sstevel@tonic-gate 				/*
9087c478bd9Sstevel@tonic-gate 				 * Option not found
9097c478bd9Sstevel@tonic-gate 				 *
9107c478bd9Sstevel@tonic-gate 				 * Verify if level is "valid" or not.
9117c478bd9Sstevel@tonic-gate 				 * Note: This check is required by XTI
9127c478bd9Sstevel@tonic-gate 				 *
9137c478bd9Sstevel@tonic-gate 				 * TPI provider always initializes
9147c478bd9Sstevel@tonic-gate 				 * the "not supported" (or whatever) status
9157c478bd9Sstevel@tonic-gate 				 * for the options. Other levels leave status
9167c478bd9Sstevel@tonic-gate 				 * unchanged if they do not understand an
9177c478bd9Sstevel@tonic-gate 				 * option.
9187c478bd9Sstevel@tonic-gate 				 */
9197c478bd9Sstevel@tonic-gate 				if (topmost_tpiprovider) {
9207c478bd9Sstevel@tonic-gate 					if (!opt_level_valid(opt->level,
9217c478bd9Sstevel@tonic-gate 					    valid_level_arr,
9227c478bd9Sstevel@tonic-gate 					    valid_level_arr_cnt))
9237c478bd9Sstevel@tonic-gate 						return (TBADOPT);
9247c478bd9Sstevel@tonic-gate 					/*
9257c478bd9Sstevel@tonic-gate 					 * level is valid - initialize
9267c478bd9Sstevel@tonic-gate 					 * option as not supported
9277c478bd9Sstevel@tonic-gate 					 */
9287c478bd9Sstevel@tonic-gate 					opt->status = T_NOTSUPPORT;
9297c478bd9Sstevel@tonic-gate 				}
9307c478bd9Sstevel@tonic-gate 
9317c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
9327c478bd9Sstevel@tonic-gate 				continue;
9337c478bd9Sstevel@tonic-gate 			}
9347c478bd9Sstevel@tonic-gate 		} else {
9357c478bd9Sstevel@tonic-gate 			/*
9367c478bd9Sstevel@tonic-gate 			 * Handle T_ALLOPT case as a special case.
9377c478bd9Sstevel@tonic-gate 			 * Note: T_ALLOPT does not mean anything
9387c478bd9Sstevel@tonic-gate 			 * for T_CHECK operation.
9397c478bd9Sstevel@tonic-gate 			 */
9407c478bd9Sstevel@tonic-gate 			allopt_len = 0;
9417c478bd9Sstevel@tonic-gate 			if (tor->MGMT_flags == T_CHECK ||
9427c478bd9Sstevel@tonic-gate 			    !topmost_tpiprovider ||
9437c478bd9Sstevel@tonic-gate 			    ((allopt_len = opt_level_allopts_lengths(opt->level,
9447c478bd9Sstevel@tonic-gate 				opt_arr, opt_arr_cnt)) == 0)) {
9457c478bd9Sstevel@tonic-gate 				/*
9467c478bd9Sstevel@tonic-gate 				 * This is confusing but correct !
9477c478bd9Sstevel@tonic-gate 				 * It is not valid to to use T_ALLOPT with
9487c478bd9Sstevel@tonic-gate 				 * T_CHECK flag.
9497c478bd9Sstevel@tonic-gate 				 *
9507c478bd9Sstevel@tonic-gate 				 * T_ALLOPT is assumed "expanded" at the
9517c478bd9Sstevel@tonic-gate 				 * topmost_tpiprovider level so it should not
9527c478bd9Sstevel@tonic-gate 				 * be there as an "option name" if this is not
9537c478bd9Sstevel@tonic-gate 				 * a topmost_tpiprovider call and we fail it.
9547c478bd9Sstevel@tonic-gate 				 *
9557c478bd9Sstevel@tonic-gate 				 * opt_level_allopts_lengths() is used to verify
9567c478bd9Sstevel@tonic-gate 				 * that "level" associated with the T_ALLOPT is
9577c478bd9Sstevel@tonic-gate 				 * supported.
9587c478bd9Sstevel@tonic-gate 				 *
9597c478bd9Sstevel@tonic-gate 				 */
9607c478bd9Sstevel@tonic-gate 				opt->status = T_FAILURE;
9617c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
9627c478bd9Sstevel@tonic-gate 				continue;
9637c478bd9Sstevel@tonic-gate 			}
9647c478bd9Sstevel@tonic-gate 			ASSERT(allopt_len != 0); /* remove ? */
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate 			*toa_lenp += allopt_len;
9677c478bd9Sstevel@tonic-gate 			opt->status = T_SUCCESS;
9687c478bd9Sstevel@tonic-gate 			/* XXX - always set T_ALLOPT 'pass_to_next' for now */
9697c478bd9Sstevel@tonic-gate 			*pass_to_nextp = B_TRUE;
9707c478bd9Sstevel@tonic-gate 			continue;
9717c478bd9Sstevel@tonic-gate 		}
9727c478bd9Sstevel@tonic-gate 		/*
9737c478bd9Sstevel@tonic-gate 		 * Check if option wants to flow downstream
9747c478bd9Sstevel@tonic-gate 		 */
9757c478bd9Sstevel@tonic-gate 		if (optd->opdes_props & OP_PASSNEXT)
9767c478bd9Sstevel@tonic-gate 			*pass_to_nextp = B_TRUE;
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 		/* Additional checks dependent on operation. */
9797c478bd9Sstevel@tonic-gate 		switch (tor->MGMT_flags) {
9807c478bd9Sstevel@tonic-gate 		case T_DEFAULT:
9817c478bd9Sstevel@tonic-gate 		case T_CURRENT:
9827c478bd9Sstevel@tonic-gate 
9837c478bd9Sstevel@tonic-gate 			/*
9847c478bd9Sstevel@tonic-gate 			 * The opt_chk_lookup() routine call above approved of
9857c478bd9Sstevel@tonic-gate 			 * this option so we can work on the status for it
9867c478bd9Sstevel@tonic-gate 			 * based on the permissions for the operation. (This
9877c478bd9Sstevel@tonic-gate 			 * can override any status for it set at higher levels)
9887c478bd9Sstevel@tonic-gate 			 * We assume this override is OK since chkfn at this
9897c478bd9Sstevel@tonic-gate 			 * level approved of this option.
9907c478bd9Sstevel@tonic-gate 			 *
9917c478bd9Sstevel@tonic-gate 			 * T_CURRENT semantics:
9927c478bd9Sstevel@tonic-gate 			 * The read access is required. Else option
9937c478bd9Sstevel@tonic-gate 			 * status is T_NOTSUPPORT.
9947c478bd9Sstevel@tonic-gate 			 *
9957c478bd9Sstevel@tonic-gate 			 * T_DEFAULT semantics:
9967c478bd9Sstevel@tonic-gate 			 * Note: specification is not clear on this but we
9977c478bd9Sstevel@tonic-gate 			 * interpret T_DEFAULT semantics such that access to
9987c478bd9Sstevel@tonic-gate 			 * read value is required for access even the default
9997c478bd9Sstevel@tonic-gate 			 * value. Otherwise the option status is T_NOTSUPPORT.
10007c478bd9Sstevel@tonic-gate 			 */
10017c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr)) {
10027c478bd9Sstevel@tonic-gate 				opt->status = T_NOTSUPPORT;
10037c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
10047c478bd9Sstevel@tonic-gate 				/* skip to next */
10057c478bd9Sstevel@tonic-gate 				continue;
10067c478bd9Sstevel@tonic-gate 			}
10077c478bd9Sstevel@tonic-gate 
10087c478bd9Sstevel@tonic-gate 			/*
10097c478bd9Sstevel@tonic-gate 			 * T_DEFAULT/T_CURRENT semantics:
10107c478bd9Sstevel@tonic-gate 			 * We know that read access is set. If no other access
10117c478bd9Sstevel@tonic-gate 			 * is set, then status is T_READONLY.
10127c478bd9Sstevel@tonic-gate 			 */
10137c478bd9Sstevel@tonic-gate 			if (OA_READONLY_PERMISSION(optd, cr))
10147c478bd9Sstevel@tonic-gate 				opt->status = T_READONLY;
10157c478bd9Sstevel@tonic-gate 			else
10167c478bd9Sstevel@tonic-gate 				opt->status = T_SUCCESS;
10177c478bd9Sstevel@tonic-gate 			/*
10187c478bd9Sstevel@tonic-gate 			 * Option passes all checks. Make room for it in the
10197c478bd9Sstevel@tonic-gate 			 * ack. Note: size stored in table does not include
10207c478bd9Sstevel@tonic-gate 			 * space for option header.
10217c478bd9Sstevel@tonic-gate 			 */
10227c478bd9Sstevel@tonic-gate 			*toa_lenp += sizeof (struct T_opthdr) +
10237c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_TOPT(optd->opdes_size);
10247c478bd9Sstevel@tonic-gate 			break;
10257c478bd9Sstevel@tonic-gate 
10267c478bd9Sstevel@tonic-gate 		case T_CHECK:
10277c478bd9Sstevel@tonic-gate 		case T_NEGOTIATE:
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 			/*
10307c478bd9Sstevel@tonic-gate 			 * T_NEGOTIATE semantics:
10317c478bd9Sstevel@tonic-gate 			 * If for fixed length option value on input is not the
10327c478bd9Sstevel@tonic-gate 			 * same as value supplied, then status is T_FAILURE.
10337c478bd9Sstevel@tonic-gate 			 *
10347c478bd9Sstevel@tonic-gate 			 * T_CHECK semantics:
10357c478bd9Sstevel@tonic-gate 			 * If value is supplied, semantics same as T_NEGOTIATE.
10367c478bd9Sstevel@tonic-gate 			 * It is however ok not to supply a value with T_CHECK.
10377c478bd9Sstevel@tonic-gate 			 */
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate 			if (tor->MGMT_flags == T_NEGOTIATE ||
10407c478bd9Sstevel@tonic-gate 			    (opt->len != sizeof (struct T_opthdr))) {
10417c478bd9Sstevel@tonic-gate 				/*
10427c478bd9Sstevel@tonic-gate 				 * Implies "value" is specified in T_CHECK or
10437c478bd9Sstevel@tonic-gate 				 * it is a T_NEGOTIATE request.
10447c478bd9Sstevel@tonic-gate 				 * Verify size.
10457c478bd9Sstevel@tonic-gate 				 * Note: This can override anything about this
10467c478bd9Sstevel@tonic-gate 				 * option request done at a higher level.
10477c478bd9Sstevel@tonic-gate 				 */
10487c478bd9Sstevel@tonic-gate 				if (!opt_length_ok(optd, opt)) {
10497c478bd9Sstevel@tonic-gate 					/* bad size */
10507c478bd9Sstevel@tonic-gate 					*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
10517c478bd9Sstevel@tonic-gate 					opt->status = T_FAILURE;
10527c478bd9Sstevel@tonic-gate 					continue;
10537c478bd9Sstevel@tonic-gate 				}
10547c478bd9Sstevel@tonic-gate 			}
10557c478bd9Sstevel@tonic-gate 			/*
10567c478bd9Sstevel@tonic-gate 			 * The opt_chk_lookup()  routine above() approved of
10577c478bd9Sstevel@tonic-gate 			 * this option so we can work on the status for it based
10587c478bd9Sstevel@tonic-gate 			 * on the permissions for the operation. (This can
10597c478bd9Sstevel@tonic-gate 			 * override anything set at a higher level).
10607c478bd9Sstevel@tonic-gate 			 *
10617c478bd9Sstevel@tonic-gate 			 * T_CHECK/T_NEGOTIATE semantics:
10627c478bd9Sstevel@tonic-gate 			 * Set status to T_READONLY if read is the only access
10637c478bd9Sstevel@tonic-gate 			 * permitted
10647c478bd9Sstevel@tonic-gate 			 */
10657c478bd9Sstevel@tonic-gate 			if (OA_READONLY_PERMISSION(optd, cr)) {
10667c478bd9Sstevel@tonic-gate 				opt->status = T_READONLY;
10677c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
10687c478bd9Sstevel@tonic-gate 				/* skip to next */
10697c478bd9Sstevel@tonic-gate 				continue;
10707c478bd9Sstevel@tonic-gate 			}
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 			/*
10737c478bd9Sstevel@tonic-gate 			 * T_CHECK/T_NEGOTIATE semantics:
10747c478bd9Sstevel@tonic-gate 			 * If write (or execute) access is not set, then status
10757c478bd9Sstevel@tonic-gate 			 * is T_NOTSUPPORT.
10767c478bd9Sstevel@tonic-gate 			 */
10777c478bd9Sstevel@tonic-gate 			if (!OA_WRITE_OR_EXECUTE(optd, cr)) {
10787c478bd9Sstevel@tonic-gate 				opt->status = T_NOTSUPPORT;
10797c478bd9Sstevel@tonic-gate 				*toa_lenp += _TPI_ALIGN_TOPT(opt->len);
10807c478bd9Sstevel@tonic-gate 				/* skip to next option */
10817c478bd9Sstevel@tonic-gate 				continue;
10827c478bd9Sstevel@tonic-gate 			}
10837c478bd9Sstevel@tonic-gate 			/*
10847c478bd9Sstevel@tonic-gate 			 * Option passes all checks. Make room for it in the
10857c478bd9Sstevel@tonic-gate 			 * ack and set success in status.
10867c478bd9Sstevel@tonic-gate 			 * Note: size stored in table does not include header
10877c478bd9Sstevel@tonic-gate 			 * length.
10887c478bd9Sstevel@tonic-gate 			 */
10897c478bd9Sstevel@tonic-gate 			opt->status = T_SUCCESS;
10907c478bd9Sstevel@tonic-gate 			*toa_lenp += sizeof (struct T_opthdr) +
10917c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_TOPT(optd->opdes_size);
10927c478bd9Sstevel@tonic-gate 			break;
10937c478bd9Sstevel@tonic-gate 
10947c478bd9Sstevel@tonic-gate 		default:
10957c478bd9Sstevel@tonic-gate 			return (TBADFLAG);
10967c478bd9Sstevel@tonic-gate 		}
10977c478bd9Sstevel@tonic-gate 	} /* for loop scanning input buffer */
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 	return (0);		/* OK return */
11007c478bd9Sstevel@tonic-gate }
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate /*
11037c478bd9Sstevel@tonic-gate  * This routine makes another pass through the option buffer this
11047c478bd9Sstevel@tonic-gate  * time acting on the request based on "status" result in the
11057c478bd9Sstevel@tonic-gate  * first pass. It also performs "expansion" of T_ALLOPT into
11067c478bd9Sstevel@tonic-gate  * all options of a certain level and acts on each for this request.
11077c478bd9Sstevel@tonic-gate  */
11087c478bd9Sstevel@tonic-gate static t_scalar_t
11097c478bd9Sstevel@tonic-gate do_options_second_pass(queue_t *q, mblk_t *reqmp, mblk_t *ack_mp, cred_t *cr,
11107c478bd9Sstevel@tonic-gate     optdb_obj_t *dbobjp, mblk_t *first_mp, boolean_t is_restart,
11117c478bd9Sstevel@tonic-gate     boolean_t *queued_statusp)
11127c478bd9Sstevel@tonic-gate {
11137c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
11147c478bd9Sstevel@tonic-gate 	int failed_option;
11157c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt;
11167c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt_start, *opt_end, *restart_opt;
11177c478bd9Sstevel@tonic-gate 	uchar_t *optr;
11187c478bd9Sstevel@tonic-gate 	uint_t optset_context;
11197c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor = (struct T_optmgmt_req *)reqmp->b_rptr;
11207c478bd9Sstevel@tonic-gate 	opt_restart_t	*or;
11217c478bd9Sstevel@tonic-gate 	t_uscalar_t	*worst_statusp;
11227c478bd9Sstevel@tonic-gate 	int	err;
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 	*queued_statusp = B_FALSE;
11257c478bd9Sstevel@tonic-gate 	or = (opt_restart_t *)first_mp->b_rptr;
11267c478bd9Sstevel@tonic-gate 	worst_statusp = &or->or_worst_status;
11277c478bd9Sstevel@tonic-gate 
11287c478bd9Sstevel@tonic-gate 	optr = (uchar_t *)ack_mp->b_rptr +
11297c478bd9Sstevel@tonic-gate 	    sizeof (struct T_optmgmt_ack); /* assumed int32_t aligned */
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate 	/*
11327c478bd9Sstevel@tonic-gate 	 * Set initial values for scanning input
11337c478bd9Sstevel@tonic-gate 	 */
11347c478bd9Sstevel@tonic-gate 	if (is_restart) {
11357c478bd9Sstevel@tonic-gate 		opt_start = (struct T_opthdr *)or->or_start;
11367c478bd9Sstevel@tonic-gate 		opt_end = (struct T_opthdr *)or->or_end;
11377c478bd9Sstevel@tonic-gate 		restart_opt = (struct T_opthdr *)or->or_ropt;
11387c478bd9Sstevel@tonic-gate 	} else {
11397c478bd9Sstevel@tonic-gate 		opt_start = (struct T_opthdr *)mi_offset_param(reqmp,
11407c478bd9Sstevel@tonic-gate 		    tor->OPT_offset, tor->OPT_length);
11417c478bd9Sstevel@tonic-gate 		if (opt_start == NULL)
11427c478bd9Sstevel@tonic-gate 			return (TBADOPT);
11437c478bd9Sstevel@tonic-gate 		opt_end = (struct T_opthdr *)((uchar_t *)opt_start +
11447c478bd9Sstevel@tonic-gate 		    tor->OPT_length);
11457c478bd9Sstevel@tonic-gate 		or->or_start = (struct opthdr *)opt_start;
11467c478bd9Sstevel@tonic-gate 		or->or_end = (struct opthdr *)opt_end;
11477c478bd9Sstevel@tonic-gate 		/*
11487c478bd9Sstevel@tonic-gate 		 * construct the mp chain, in case the setfn needs to
11497c478bd9Sstevel@tonic-gate 		 * queue this and restart option processing later on.
11507c478bd9Sstevel@tonic-gate 		 */
11517c478bd9Sstevel@tonic-gate 		first_mp->b_cont = ack_mp;
11527c478bd9Sstevel@tonic-gate 		ack_mp->b_cont = reqmp;
11537c478bd9Sstevel@tonic-gate 	}
11547c478bd9Sstevel@tonic-gate 	ASSERT(__TPI_TOPT_ISALIGNED(opt_start)); /* verified in first pass */
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate 	for (opt = is_restart ? restart_opt : opt_start;
11577c478bd9Sstevel@tonic-gate 	    opt && (opt < opt_end);
11587c478bd9Sstevel@tonic-gate 	    opt = _TPI_TOPT_NEXTHDR(opt_start, tor->OPT_length, opt)) {
11597c478bd9Sstevel@tonic-gate 		or->or_ropt = (struct opthdr *)opt;
11607c478bd9Sstevel@tonic-gate 		/* verified in first pass */
11617c478bd9Sstevel@tonic-gate 		ASSERT(_TPI_TOPT_VALID(opt, opt_start, opt_end));
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 		/*
11647c478bd9Sstevel@tonic-gate 		 * If the first pass in process_topthdrs_first_pass()
11657c478bd9Sstevel@tonic-gate 		 * has marked the option as a failure case for the MGMT_flags
11667c478bd9Sstevel@tonic-gate 		 * semantics then there is not much to do.
11677c478bd9Sstevel@tonic-gate 		 *
11687c478bd9Sstevel@tonic-gate 		 * Note: For all practical purposes, T_READONLY status is
11697c478bd9Sstevel@tonic-gate 		 * a "success" for T_DEFAULT/T_CURRENT and "failure" for
11707c478bd9Sstevel@tonic-gate 		 * T_CHECK/T_NEGOTIATE
11717c478bd9Sstevel@tonic-gate 		 */
11727c478bd9Sstevel@tonic-gate 		failed_option =
11737c478bd9Sstevel@tonic-gate 		    (opt->status == T_NOTSUPPORT) ||
11747c478bd9Sstevel@tonic-gate 		    (opt->status == T_FAILURE) ||
11757c478bd9Sstevel@tonic-gate 		    ((tor->MGMT_flags & (T_NEGOTIATE|T_CHECK)) &&
11767c478bd9Sstevel@tonic-gate 			(opt->status == T_READONLY));
11777c478bd9Sstevel@tonic-gate 
11787c478bd9Sstevel@tonic-gate 		if (failed_option) {
11797c478bd9Sstevel@tonic-gate 			/*
11807c478bd9Sstevel@tonic-gate 			 * According to T_DEFAULT/T_CURRENT semantics, the
11817c478bd9Sstevel@tonic-gate 			 * input values, even if present, are to be ignored.
11827c478bd9Sstevel@tonic-gate 			 * Note: Specification is not clear on this, but we
11837c478bd9Sstevel@tonic-gate 			 * interpret that even though we ignore the values, we
11847c478bd9Sstevel@tonic-gate 			 * can return them as is. So we process them similar to
11857c478bd9Sstevel@tonic-gate 			 * T_CHECK/T_NEGOTIATE case which has the semantics to
11867c478bd9Sstevel@tonic-gate 			 * return the values as is. XXX If interpretation is
11877c478bd9Sstevel@tonic-gate 			 * ever determined incorrect fill in appropriate code
11887c478bd9Sstevel@tonic-gate 			 * here to treat T_DEFAULT/T_CURRENT differently.
11897c478bd9Sstevel@tonic-gate 			 *
11907c478bd9Sstevel@tonic-gate 			 * According to T_CHECK/T_NEGOTIATE semantics,
11917c478bd9Sstevel@tonic-gate 			 * in the case of T_NOTSUPPORT/T_FAILURE/T_READONLY,
11927c478bd9Sstevel@tonic-gate 			 * the semantics are to return the "value" part of
11937c478bd9Sstevel@tonic-gate 			 * option untouched. So here we copy the option
11947c478bd9Sstevel@tonic-gate 			 * head including value part if any to output.
11957c478bd9Sstevel@tonic-gate 			 */
11967c478bd9Sstevel@tonic-gate 
11977c478bd9Sstevel@tonic-gate 			bcopy(opt, optr, opt->len);
11987c478bd9Sstevel@tonic-gate 			optr += _TPI_ALIGN_TOPT(opt->len);
11997c478bd9Sstevel@tonic-gate 
12007c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(opt->status,
12017c478bd9Sstevel@tonic-gate 			    *worst_statusp);
12027c478bd9Sstevel@tonic-gate 
12037c478bd9Sstevel@tonic-gate 			/* skip to process next option in buffer */
12047c478bd9Sstevel@tonic-gate 			continue;
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 		} /* end if "failed option" */
12077c478bd9Sstevel@tonic-gate 		/*
12087c478bd9Sstevel@tonic-gate 		 * The status is T_SUCCESS or T_READONLY
12097c478bd9Sstevel@tonic-gate 		 * We process the value part here
12107c478bd9Sstevel@tonic-gate 		 */
12117c478bd9Sstevel@tonic-gate 		ASSERT(opt->status == T_SUCCESS || opt->status == T_READONLY);
12127c478bd9Sstevel@tonic-gate 		switch (tor->MGMT_flags) {
12137c478bd9Sstevel@tonic-gate 		case T_DEFAULT:
12147c478bd9Sstevel@tonic-gate 			/*
12157c478bd9Sstevel@tonic-gate 			 * We fill default value from table or protocol specific
12167c478bd9Sstevel@tonic-gate 			 * function. If this call fails, we pass input through.
12177c478bd9Sstevel@tonic-gate 			 */
12187c478bd9Sstevel@tonic-gate 			if (do_opt_default(q, opt, &optr, worst_statusp,
12197c478bd9Sstevel@tonic-gate 			    cr, dbobjp) < 0) {
12207c478bd9Sstevel@tonic-gate 				/* fail or pass transparently */
12217c478bd9Sstevel@tonic-gate 				if (topmost_tpiprovider)
12227c478bd9Sstevel@tonic-gate 					opt->status = T_FAILURE;
12237c478bd9Sstevel@tonic-gate 				bcopy(opt, optr, opt->len);
12247c478bd9Sstevel@tonic-gate 				optr += _TPI_ALIGN_TOPT(opt->len);
12257c478bd9Sstevel@tonic-gate 				*worst_statusp = get_worst_status(opt->status,
12267c478bd9Sstevel@tonic-gate 				    *worst_statusp);
12277c478bd9Sstevel@tonic-gate 			}
12287c478bd9Sstevel@tonic-gate 			break;
12297c478bd9Sstevel@tonic-gate 
12307c478bd9Sstevel@tonic-gate 		case T_CURRENT:
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 			do_opt_current(q, opt, &optr, worst_statusp, cr,
12337c478bd9Sstevel@tonic-gate 			    dbobjp);
12347c478bd9Sstevel@tonic-gate 			break;
12357c478bd9Sstevel@tonic-gate 
12367c478bd9Sstevel@tonic-gate 		case T_CHECK:
12377c478bd9Sstevel@tonic-gate 		case T_NEGOTIATE:
12387c478bd9Sstevel@tonic-gate 			if (tor->MGMT_flags == T_CHECK)
12397c478bd9Sstevel@tonic-gate 				optset_context = SETFN_OPTCOM_CHECKONLY;
12407c478bd9Sstevel@tonic-gate 			else	/* T_NEGOTIATE */
12417c478bd9Sstevel@tonic-gate 				optset_context = SETFN_OPTCOM_NEGOTIATE;
12427c478bd9Sstevel@tonic-gate 			err = do_opt_check_or_negotiate(q, opt, optset_context,
12437c478bd9Sstevel@tonic-gate 			    &optr, worst_statusp, cr, dbobjp, first_mp);
12447c478bd9Sstevel@tonic-gate 			if (err == EINPROGRESS) {
12457c478bd9Sstevel@tonic-gate 				*queued_statusp = B_TRUE;
12467c478bd9Sstevel@tonic-gate 				return (0);
12477c478bd9Sstevel@tonic-gate 			}
12487c478bd9Sstevel@tonic-gate 			break;
12497c478bd9Sstevel@tonic-gate 		default:
12507c478bd9Sstevel@tonic-gate 			return (TBADFLAG);
12517c478bd9Sstevel@tonic-gate 		}
12527c478bd9Sstevel@tonic-gate 	} /* end for loop scanning option buffer */
12537c478bd9Sstevel@tonic-gate 
12547c478bd9Sstevel@tonic-gate 	ack_mp->b_wptr = optr;
12557c478bd9Sstevel@tonic-gate 	ASSERT(ack_mp->b_wptr <= ack_mp->b_datap->db_lim);
12567c478bd9Sstevel@tonic-gate 
12577c478bd9Sstevel@tonic-gate 	return (0);		/* OK return */
12587c478bd9Sstevel@tonic-gate }
12597c478bd9Sstevel@tonic-gate 
12607c478bd9Sstevel@tonic-gate 
12617c478bd9Sstevel@tonic-gate static t_uscalar_t
12627c478bd9Sstevel@tonic-gate get_worst_status(t_uscalar_t status, t_uscalar_t current_worst_status)
12637c478bd9Sstevel@tonic-gate {
12647c478bd9Sstevel@tonic-gate 	/*
12657c478bd9Sstevel@tonic-gate 	 * Return the "worst" among the arguments "status" and
12667c478bd9Sstevel@tonic-gate 	 * "current_worst_status".
12677c478bd9Sstevel@tonic-gate 	 *
12687c478bd9Sstevel@tonic-gate 	 * Note: Tracking "worst_status" can be made a bit simpler
12697c478bd9Sstevel@tonic-gate 	 * if we use the property that status codes are bitwise
12707c478bd9Sstevel@tonic-gate 	 * distinct.
12717c478bd9Sstevel@tonic-gate 	 *
12727c478bd9Sstevel@tonic-gate 	 * The pecking order is
12737c478bd9Sstevel@tonic-gate 	 *
12747c478bd9Sstevel@tonic-gate 	 * T_SUCCESS ..... best
12757c478bd9Sstevel@tonic-gate 	 * T_PARTSUCCESS
12767c478bd9Sstevel@tonic-gate 	 * T_FAILURE
12777c478bd9Sstevel@tonic-gate 	 * T_READONLY
12787c478bd9Sstevel@tonic-gate 	 * T_NOTSUPPORT... worst
12797c478bd9Sstevel@tonic-gate 	 */
12807c478bd9Sstevel@tonic-gate 	if (status == current_worst_status)
12817c478bd9Sstevel@tonic-gate 		return (current_worst_status);
12827c478bd9Sstevel@tonic-gate 	switch (current_worst_status) {
12837c478bd9Sstevel@tonic-gate 	case T_SUCCESS:
12847c478bd9Sstevel@tonic-gate 		if (status == T_PARTSUCCESS)
12857c478bd9Sstevel@tonic-gate 			return (T_PARTSUCCESS);
12867c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
12877c478bd9Sstevel@tonic-gate 	case T_PARTSUCCESS:
12887c478bd9Sstevel@tonic-gate 		if (status == T_FAILURE)
12897c478bd9Sstevel@tonic-gate 			return (T_FAILURE);
12907c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
12917c478bd9Sstevel@tonic-gate 	case T_FAILURE:
12927c478bd9Sstevel@tonic-gate 		if (status == T_READONLY)
12937c478bd9Sstevel@tonic-gate 			return (T_READONLY);
12947c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
12957c478bd9Sstevel@tonic-gate 	case T_READONLY:
12967c478bd9Sstevel@tonic-gate 		if (status == T_NOTSUPPORT)
12977c478bd9Sstevel@tonic-gate 			return (T_NOTSUPPORT);
12987c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
12997c478bd9Sstevel@tonic-gate 	case T_NOTSUPPORT:
13007c478bd9Sstevel@tonic-gate 	default:
13017c478bd9Sstevel@tonic-gate 		return (current_worst_status);
13027c478bd9Sstevel@tonic-gate 	}
13037c478bd9Sstevel@tonic-gate }
13047c478bd9Sstevel@tonic-gate 
13057c478bd9Sstevel@tonic-gate static int
13067c478bd9Sstevel@tonic-gate do_opt_default(queue_t *q, struct T_opthdr *reqopt, uchar_t **resptrp,
13077c478bd9Sstevel@tonic-gate     t_uscalar_t *worst_statusp, cred_t *cr, optdb_obj_t *dbobjp)
13087c478bd9Sstevel@tonic-gate {
13097c478bd9Sstevel@tonic-gate 	pfi_t	deffn = dbobjp->odb_deffn;
13107c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
13117c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
13127c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
13137c478bd9Sstevel@tonic-gate 
13147c478bd9Sstevel@tonic-gate 	struct T_opthdr *topth;
13157c478bd9Sstevel@tonic-gate 	opdes_t *optd;
13167c478bd9Sstevel@tonic-gate 
13177c478bd9Sstevel@tonic-gate 	if (reqopt->name != T_ALLOPT) {
13187c478bd9Sstevel@tonic-gate 		/*
13197c478bd9Sstevel@tonic-gate 		 * lookup the option in the table and fill default value
13207c478bd9Sstevel@tonic-gate 		 */
13217c478bd9Sstevel@tonic-gate 		optd = opt_chk_lookup(reqopt->level, reqopt->name,
13227c478bd9Sstevel@tonic-gate 		    opt_arr, opt_arr_cnt);
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 		if (optd == NULL) {
13257c478bd9Sstevel@tonic-gate 			/*
13267c478bd9Sstevel@tonic-gate 			 * not found - fail this one. Should not happen
13277c478bd9Sstevel@tonic-gate 			 * for topmost_tpiprovider as calling routine
13287c478bd9Sstevel@tonic-gate 			 * should have verified it.
13297c478bd9Sstevel@tonic-gate 			 */
13307c478bd9Sstevel@tonic-gate 			ASSERT(!topmost_tpiprovider);
13317c478bd9Sstevel@tonic-gate 			return (-1);
13327c478bd9Sstevel@tonic-gate 		}
13337c478bd9Sstevel@tonic-gate 
13347c478bd9Sstevel@tonic-gate 		topth = (struct T_opthdr *)(*resptrp);
13357c478bd9Sstevel@tonic-gate 		topth->level = reqopt->level;
13367c478bd9Sstevel@tonic-gate 		topth->name = reqopt->name;
13377c478bd9Sstevel@tonic-gate 		topth->status = reqopt->status;
13387c478bd9Sstevel@tonic-gate 
13397c478bd9Sstevel@tonic-gate 		*worst_statusp = get_worst_status(reqopt->status,
13407c478bd9Sstevel@tonic-gate 		    *worst_statusp);
13417c478bd9Sstevel@tonic-gate 
13427c478bd9Sstevel@tonic-gate 		if (optd->opdes_props & OP_NODEFAULT) {
13437c478bd9Sstevel@tonic-gate 			/* header only, no default "value" part */
13447c478bd9Sstevel@tonic-gate 			topth->len = sizeof (struct T_opthdr);
13457c478bd9Sstevel@tonic-gate 			*resptrp += sizeof (struct T_opthdr);
13467c478bd9Sstevel@tonic-gate 		} else {
13477c478bd9Sstevel@tonic-gate 			int deflen;
13487c478bd9Sstevel@tonic-gate 
13497c478bd9Sstevel@tonic-gate 			if (optd->opdes_props & OP_DEF_FN) {
13507c478bd9Sstevel@tonic-gate 				deflen = (*deffn)(q, reqopt->level,
13517c478bd9Sstevel@tonic-gate 				    reqopt->name, _TPI_TOPT_DATA(topth));
13527c478bd9Sstevel@tonic-gate 				if (deflen >= 0) {
13537c478bd9Sstevel@tonic-gate 					topth->len = (t_uscalar_t)
13547c478bd9Sstevel@tonic-gate 					    (sizeof (struct T_opthdr) + deflen);
13557c478bd9Sstevel@tonic-gate 				} else {
13567c478bd9Sstevel@tonic-gate 					/*
13577c478bd9Sstevel@tonic-gate 					 * return error, this should 'pass
13587c478bd9Sstevel@tonic-gate 					 * through' the option and maybe some
13597c478bd9Sstevel@tonic-gate 					 * other level will fill it in or
13607c478bd9Sstevel@tonic-gate 					 * already did.
13617c478bd9Sstevel@tonic-gate 					 * (No change in 'resptrp' upto here)
13627c478bd9Sstevel@tonic-gate 					 */
13637c478bd9Sstevel@tonic-gate 					return (-1);
13647c478bd9Sstevel@tonic-gate 				}
13657c478bd9Sstevel@tonic-gate 			} else {
13667c478bd9Sstevel@tonic-gate 				/* fill length and value part */
13677c478bd9Sstevel@tonic-gate 				switch (optd->opdes_size) {
13687c478bd9Sstevel@tonic-gate 				/*
13697c478bd9Sstevel@tonic-gate 				 * Since options are guaranteed aligned only
13707c478bd9Sstevel@tonic-gate 				 * on a 4 byte boundary (t_scalar_t) any
13717c478bd9Sstevel@tonic-gate 				 * option that is greater in size will default
13727c478bd9Sstevel@tonic-gate 				 * to the bcopy below
13737c478bd9Sstevel@tonic-gate 				 */
13747c478bd9Sstevel@tonic-gate 				case sizeof (int32_t):
13757c478bd9Sstevel@tonic-gate 					*(int32_t *)_TPI_TOPT_DATA(topth) =
13767c478bd9Sstevel@tonic-gate 					    (int32_t)optd->opdes_default;
13777c478bd9Sstevel@tonic-gate 					break;
13787c478bd9Sstevel@tonic-gate 				case sizeof (int16_t):
13797c478bd9Sstevel@tonic-gate 					*(int16_t *)_TPI_TOPT_DATA(topth) =
13807c478bd9Sstevel@tonic-gate 					    (int16_t)optd->opdes_default;
13817c478bd9Sstevel@tonic-gate 					break;
13827c478bd9Sstevel@tonic-gate 				case sizeof (int8_t):
13837c478bd9Sstevel@tonic-gate 					*(int8_t *)_TPI_TOPT_DATA(topth) =
13847c478bd9Sstevel@tonic-gate 					    (int8_t)optd->opdes_default;
13857c478bd9Sstevel@tonic-gate 					break;
13867c478bd9Sstevel@tonic-gate 				default:
13877c478bd9Sstevel@tonic-gate 					/*
13887c478bd9Sstevel@tonic-gate 					 * other length but still assume
13897c478bd9Sstevel@tonic-gate 					 * fixed - use bcopy
13907c478bd9Sstevel@tonic-gate 					 */
13917c478bd9Sstevel@tonic-gate 					bcopy(optd->opdes_defbuf,
13927c478bd9Sstevel@tonic-gate 					    _TPI_TOPT_DATA(topth),
13937c478bd9Sstevel@tonic-gate 					    optd->opdes_size);
13947c478bd9Sstevel@tonic-gate 					break;
13957c478bd9Sstevel@tonic-gate 				}
13967c478bd9Sstevel@tonic-gate 				topth->len = (t_uscalar_t)(optd->opdes_size +
13977c478bd9Sstevel@tonic-gate 				    sizeof (struct T_opthdr));
13987c478bd9Sstevel@tonic-gate 			}
13997c478bd9Sstevel@tonic-gate 			*resptrp += _TPI_ALIGN_TOPT(topth->len);
14007c478bd9Sstevel@tonic-gate 		}
14017c478bd9Sstevel@tonic-gate 		return (0);	/* OK return */
14027c478bd9Sstevel@tonic-gate 	}
14037c478bd9Sstevel@tonic-gate 
14047c478bd9Sstevel@tonic-gate 	/*
14057c478bd9Sstevel@tonic-gate 	 * T_ALLOPT processing
14067c478bd9Sstevel@tonic-gate 	 *
14077c478bd9Sstevel@tonic-gate 	 * lookup and stuff default values of all the options of the
14087c478bd9Sstevel@tonic-gate 	 * level specified
14097c478bd9Sstevel@tonic-gate 	 * Note: This expansion of T_ALLOPT should happen in
14107c478bd9Sstevel@tonic-gate 	 * a topmost_tpiprovider.
14117c478bd9Sstevel@tonic-gate 	 */
14127c478bd9Sstevel@tonic-gate 	ASSERT(topmost_tpiprovider);
14137c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
14147c478bd9Sstevel@tonic-gate 		if (reqopt->level != optd->opdes_level)
14157c478bd9Sstevel@tonic-gate 			continue;
14167c478bd9Sstevel@tonic-gate 		/*
14177c478bd9Sstevel@tonic-gate 		 *
14187c478bd9Sstevel@tonic-gate 		 * T_DEFAULT semantics:
14197c478bd9Sstevel@tonic-gate 		 * XXX: we interpret T_DEFAULT semantics such that access to
14207c478bd9Sstevel@tonic-gate 		 * read value is required for access even the default value.
14217c478bd9Sstevel@tonic-gate 		 * Else option is ignored for T_ALLOPT request.
14227c478bd9Sstevel@tonic-gate 		 */
14237c478bd9Sstevel@tonic-gate 		if (!OA_READ_PERMISSION(optd, cr))
14247c478bd9Sstevel@tonic-gate 			/* skip this one */
14257c478bd9Sstevel@tonic-gate 			continue;
14267c478bd9Sstevel@tonic-gate 
14277c478bd9Sstevel@tonic-gate 		/*
14287c478bd9Sstevel@tonic-gate 		 * Found option of same level as T_ALLOPT request
14297c478bd9Sstevel@tonic-gate 		 * that we can return.
14307c478bd9Sstevel@tonic-gate 		 */
14317c478bd9Sstevel@tonic-gate 
14327c478bd9Sstevel@tonic-gate 		topth = (struct T_opthdr *)(*resptrp);
14337c478bd9Sstevel@tonic-gate 		topth->level = optd->opdes_level;
14347c478bd9Sstevel@tonic-gate 		topth->name = optd->opdes_name;
14357c478bd9Sstevel@tonic-gate 
14367c478bd9Sstevel@tonic-gate 		/*
14377c478bd9Sstevel@tonic-gate 		 * T_DEFAULT semantics:
14387c478bd9Sstevel@tonic-gate 		 * We know that read access is set. If no other access is set,
14397c478bd9Sstevel@tonic-gate 		 * then status is T_READONLY
14407c478bd9Sstevel@tonic-gate 		 */
14417c478bd9Sstevel@tonic-gate 		if (OA_READONLY_PERMISSION(optd, cr)) {
14427c478bd9Sstevel@tonic-gate 			topth->status = T_READONLY;
14437c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(T_READONLY,
14447c478bd9Sstevel@tonic-gate 			    *worst_statusp);
14457c478bd9Sstevel@tonic-gate 		} else {
14467c478bd9Sstevel@tonic-gate 			topth->status = T_SUCCESS;
14477c478bd9Sstevel@tonic-gate 			/*
14487c478bd9Sstevel@tonic-gate 			 * Note: *worst_statusp has to be T_SUCCESS or
14497c478bd9Sstevel@tonic-gate 			 * worse so no need to adjust
14507c478bd9Sstevel@tonic-gate 			 */
14517c478bd9Sstevel@tonic-gate 		}
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 		if (optd->opdes_props & OP_NODEFAULT) {
14547c478bd9Sstevel@tonic-gate 			/* header only, no value part */
14557c478bd9Sstevel@tonic-gate 			topth->len = sizeof (struct T_opthdr);
14567c478bd9Sstevel@tonic-gate 			*resptrp += sizeof (struct T_opthdr);
14577c478bd9Sstevel@tonic-gate 		} else {
14587c478bd9Sstevel@tonic-gate 			int deflen;
14597c478bd9Sstevel@tonic-gate 
14607c478bd9Sstevel@tonic-gate 			if (optd->opdes_props & OP_DEF_FN) {
14617c478bd9Sstevel@tonic-gate 				deflen = (*deffn)(q, reqopt->level,
14627c478bd9Sstevel@tonic-gate 				    reqopt->name, _TPI_TOPT_DATA(topth));
14637c478bd9Sstevel@tonic-gate 				if (deflen >= 0) {
14647c478bd9Sstevel@tonic-gate 					topth->len = (t_uscalar_t)(deflen +
14657c478bd9Sstevel@tonic-gate 					    sizeof (struct T_opthdr));
14667c478bd9Sstevel@tonic-gate 				} else {
14677c478bd9Sstevel@tonic-gate 					/*
14687c478bd9Sstevel@tonic-gate 					 * deffn failed.
14697c478bd9Sstevel@tonic-gate 					 * return just the header as T_ALLOPT
14707c478bd9Sstevel@tonic-gate 					 * expansion.
14717c478bd9Sstevel@tonic-gate 					 * Some other level deffn may
14727c478bd9Sstevel@tonic-gate 					 * supply value part.
14737c478bd9Sstevel@tonic-gate 					 */
14747c478bd9Sstevel@tonic-gate 					topth->len = sizeof (struct T_opthdr);
14757c478bd9Sstevel@tonic-gate 					topth->status = T_FAILURE;
14767c478bd9Sstevel@tonic-gate 					*worst_statusp =
14777c478bd9Sstevel@tonic-gate 					    get_worst_status(T_FAILURE,
14787c478bd9Sstevel@tonic-gate 						*worst_statusp);
14797c478bd9Sstevel@tonic-gate 				}
14807c478bd9Sstevel@tonic-gate 			} else {
14817c478bd9Sstevel@tonic-gate 				/*
14827c478bd9Sstevel@tonic-gate 				 * fill length and value part from
14837c478bd9Sstevel@tonic-gate 				 * table
14847c478bd9Sstevel@tonic-gate 				 */
14857c478bd9Sstevel@tonic-gate 				switch (optd->opdes_size) {
14867c478bd9Sstevel@tonic-gate 				/*
14877c478bd9Sstevel@tonic-gate 				 * Since options are guaranteed aligned only
14887c478bd9Sstevel@tonic-gate 				 * on a 4 byte boundary (t_scalar_t) any
14897c478bd9Sstevel@tonic-gate 				 * option that is greater in size will default
14907c478bd9Sstevel@tonic-gate 				 * to the bcopy below
14917c478bd9Sstevel@tonic-gate 				 */
14927c478bd9Sstevel@tonic-gate 				case sizeof (int32_t):
14937c478bd9Sstevel@tonic-gate 					*(int32_t *)_TPI_TOPT_DATA(topth) =
14947c478bd9Sstevel@tonic-gate 					    (int32_t)optd->opdes_default;
14957c478bd9Sstevel@tonic-gate 					break;
14967c478bd9Sstevel@tonic-gate 				case sizeof (int16_t):
14977c478bd9Sstevel@tonic-gate 					*(int16_t *)_TPI_TOPT_DATA(topth) =
14987c478bd9Sstevel@tonic-gate 					    (int16_t)optd->opdes_default;
14997c478bd9Sstevel@tonic-gate 					break;
15007c478bd9Sstevel@tonic-gate 				case sizeof (int8_t):
15017c478bd9Sstevel@tonic-gate 					*(int8_t *)_TPI_TOPT_DATA(topth) =
15027c478bd9Sstevel@tonic-gate 					    (int8_t)optd->opdes_default;
15037c478bd9Sstevel@tonic-gate 					break;
15047c478bd9Sstevel@tonic-gate 				default:
15057c478bd9Sstevel@tonic-gate 					/*
15067c478bd9Sstevel@tonic-gate 					 * other length but still assume
15077c478bd9Sstevel@tonic-gate 					 * fixed - use bcopy
15087c478bd9Sstevel@tonic-gate 					 */
15097c478bd9Sstevel@tonic-gate 					bcopy(optd->opdes_defbuf,
15107c478bd9Sstevel@tonic-gate 					    _TPI_TOPT_DATA(topth),
15117c478bd9Sstevel@tonic-gate 					    optd->opdes_size);
15127c478bd9Sstevel@tonic-gate 				}
15137c478bd9Sstevel@tonic-gate 				topth->len = (t_uscalar_t)(optd->opdes_size +
15147c478bd9Sstevel@tonic-gate 				    sizeof (struct T_opthdr));
15157c478bd9Sstevel@tonic-gate 			}
15167c478bd9Sstevel@tonic-gate 			*resptrp += _TPI_ALIGN_TOPT(topth->len);
15177c478bd9Sstevel@tonic-gate 		}
15187c478bd9Sstevel@tonic-gate 	}
15197c478bd9Sstevel@tonic-gate 	return (0);
15207c478bd9Sstevel@tonic-gate }
15217c478bd9Sstevel@tonic-gate 
15227c478bd9Sstevel@tonic-gate static void
15237c478bd9Sstevel@tonic-gate do_opt_current(queue_t *q, struct T_opthdr *reqopt, uchar_t **resptrp,
15247c478bd9Sstevel@tonic-gate     t_uscalar_t *worst_statusp, cred_t *cr, optdb_obj_t *dbobjp)
15257c478bd9Sstevel@tonic-gate {
15267c478bd9Sstevel@tonic-gate 	pfi_t	getfn = dbobjp->odb_getfn;
15277c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
15287c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
15297c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
15307c478bd9Sstevel@tonic-gate 
15317c478bd9Sstevel@tonic-gate 	struct T_opthdr *topth;
15327c478bd9Sstevel@tonic-gate 	opdes_t *optd;
15337c478bd9Sstevel@tonic-gate 	int optlen;
15347c478bd9Sstevel@tonic-gate 	uchar_t *initptr = *resptrp;
15357c478bd9Sstevel@tonic-gate 
15367c478bd9Sstevel@tonic-gate 	/*
15377c478bd9Sstevel@tonic-gate 	 * We call getfn to get the current value of an option. The call may
15387c478bd9Sstevel@tonic-gate 	 * fail in which case we copy the values from the input buffer. Maybe
15397c478bd9Sstevel@tonic-gate 	 * something downstream will fill it in or something upstream did.
15407c478bd9Sstevel@tonic-gate 	 */
15417c478bd9Sstevel@tonic-gate 
15427c478bd9Sstevel@tonic-gate 	if (reqopt->name != T_ALLOPT) {
15437c478bd9Sstevel@tonic-gate 		topth = (struct T_opthdr *)*resptrp;
15447c478bd9Sstevel@tonic-gate 		*resptrp += sizeof (struct T_opthdr);
15457c478bd9Sstevel@tonic-gate 		optlen = (*getfn)(q, reqopt->level, reqopt->name, *resptrp);
15467c478bd9Sstevel@tonic-gate 		if (optlen >= 0) {
15477c478bd9Sstevel@tonic-gate 			topth->len = (t_uscalar_t)(optlen +
15487c478bd9Sstevel@tonic-gate 			    sizeof (struct T_opthdr));
15497c478bd9Sstevel@tonic-gate 			topth->level = reqopt->level;
15507c478bd9Sstevel@tonic-gate 			topth->name = reqopt->name;
15517c478bd9Sstevel@tonic-gate 			topth->status = reqopt->status;
15527c478bd9Sstevel@tonic-gate 			*resptrp += _TPI_ALIGN_TOPT(optlen);
15537c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(topth->status,
15547c478bd9Sstevel@tonic-gate 			    *worst_statusp);
15557c478bd9Sstevel@tonic-gate 		} else {
15567c478bd9Sstevel@tonic-gate 			/* failed - reset "*resptrp" pointer */
15577c478bd9Sstevel@tonic-gate 			*resptrp -= sizeof (struct T_opthdr);
15587c478bd9Sstevel@tonic-gate 		}
15597c478bd9Sstevel@tonic-gate 	} else {		/* T_ALLOPT processing */
15607c478bd9Sstevel@tonic-gate 		ASSERT(topmost_tpiprovider == B_TRUE);
15617c478bd9Sstevel@tonic-gate 		/* scan and get all options */
15627c478bd9Sstevel@tonic-gate 		for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
15637c478bd9Sstevel@tonic-gate 			/* skip other levels */
15647c478bd9Sstevel@tonic-gate 			if (reqopt->level != optd->opdes_level)
15657c478bd9Sstevel@tonic-gate 				continue;
15667c478bd9Sstevel@tonic-gate 
15677c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr))
15687c478bd9Sstevel@tonic-gate 				/* skip this one */
15697c478bd9Sstevel@tonic-gate 				continue;
15707c478bd9Sstevel@tonic-gate 
15717c478bd9Sstevel@tonic-gate 			topth = (struct T_opthdr *)*resptrp;
15727c478bd9Sstevel@tonic-gate 			*resptrp += sizeof (struct T_opthdr);
15737c478bd9Sstevel@tonic-gate 
15747c478bd9Sstevel@tonic-gate 			/* get option of this level */
15757c478bd9Sstevel@tonic-gate 			optlen = (*getfn)(q, reqopt->level, optd->opdes_name,
15767c478bd9Sstevel@tonic-gate 			    *resptrp);
15777c478bd9Sstevel@tonic-gate 			if (optlen >= 0) {
15787c478bd9Sstevel@tonic-gate 				/* success */
15797c478bd9Sstevel@tonic-gate 				topth->len = (t_uscalar_t)(optlen +
15807c478bd9Sstevel@tonic-gate 				    sizeof (struct T_opthdr));
15817c478bd9Sstevel@tonic-gate 				topth->level = reqopt->level;
15827c478bd9Sstevel@tonic-gate 				topth->name = optd->opdes_name;
15837c478bd9Sstevel@tonic-gate 				if (OA_READONLY_PERMISSION(optd, cr))
15847c478bd9Sstevel@tonic-gate 					topth->status = T_READONLY;
15857c478bd9Sstevel@tonic-gate 				else
15867c478bd9Sstevel@tonic-gate 					topth->status = T_SUCCESS;
15877c478bd9Sstevel@tonic-gate 				*resptrp += _TPI_ALIGN_TOPT(optlen);
15887c478bd9Sstevel@tonic-gate 			} else {
15897c478bd9Sstevel@tonic-gate 				/*
15907c478bd9Sstevel@tonic-gate 				 * failed, return as T_FAILURE and null value
15917c478bd9Sstevel@tonic-gate 				 * part. Maybe something downstream will
15927c478bd9Sstevel@tonic-gate 				 * handle this one and fill in a value. Here
15937c478bd9Sstevel@tonic-gate 				 * it is just part of T_ALLOPT expansion.
15947c478bd9Sstevel@tonic-gate 				 */
15957c478bd9Sstevel@tonic-gate 				topth->len = sizeof (struct T_opthdr);
15967c478bd9Sstevel@tonic-gate 				topth->level = reqopt->level;
15977c478bd9Sstevel@tonic-gate 				topth->name = optd->opdes_name;
15987c478bd9Sstevel@tonic-gate 				topth->status = T_FAILURE;
15997c478bd9Sstevel@tonic-gate 			}
16007c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(topth->status,
16017c478bd9Sstevel@tonic-gate 			    *worst_statusp);
16027c478bd9Sstevel@tonic-gate 		} /* end for loop */
16037c478bd9Sstevel@tonic-gate 	}
16047c478bd9Sstevel@tonic-gate 	if (*resptrp == initptr) {
16057c478bd9Sstevel@tonic-gate 		/*
16067c478bd9Sstevel@tonic-gate 		 * getfn failed and does not want to handle this option. Maybe
16077c478bd9Sstevel@tonic-gate 		 * something downstream will or something upstream did. (If
16087c478bd9Sstevel@tonic-gate 		 * topmost_tpiprovider, initialize "status" to failure which
16097c478bd9Sstevel@tonic-gate 		 * can possibly change downstream). Copy the input "as is" from
16107c478bd9Sstevel@tonic-gate 		 * input option buffer if any to maintain transparency.
16117c478bd9Sstevel@tonic-gate 		 */
16127c478bd9Sstevel@tonic-gate 		if (topmost_tpiprovider)
16137c478bd9Sstevel@tonic-gate 			reqopt->status = T_FAILURE;
16147c478bd9Sstevel@tonic-gate 		bcopy(reqopt, *resptrp, reqopt->len);
16157c478bd9Sstevel@tonic-gate 		*resptrp += _TPI_ALIGN_TOPT(reqopt->len);
16167c478bd9Sstevel@tonic-gate 		*worst_statusp = get_worst_status(reqopt->status,
16177c478bd9Sstevel@tonic-gate 		    *worst_statusp);
16187c478bd9Sstevel@tonic-gate 	}
16197c478bd9Sstevel@tonic-gate }
16207c478bd9Sstevel@tonic-gate 
16217c478bd9Sstevel@tonic-gate 
16227c478bd9Sstevel@tonic-gate 
16237c478bd9Sstevel@tonic-gate static int
16247c478bd9Sstevel@tonic-gate do_opt_check_or_negotiate(queue_t *q, struct T_opthdr *reqopt,
16257c478bd9Sstevel@tonic-gate     uint_t optset_context, uchar_t **resptrp, t_uscalar_t *worst_statusp,
16267c478bd9Sstevel@tonic-gate     cred_t *cr, optdb_obj_t *dbobjp, mblk_t *first_mp)
16277c478bd9Sstevel@tonic-gate {
16287c478bd9Sstevel@tonic-gate 	pfi_t	deffn = dbobjp->odb_deffn;
16297c478bd9Sstevel@tonic-gate 	opt_set_fn setfn = dbobjp->odb_setfn;
16307c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
16317c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
16327c478bd9Sstevel@tonic-gate 	boolean_t topmost_tpiprovider = dbobjp->odb_topmost_tpiprovider;
16337c478bd9Sstevel@tonic-gate 
16347c478bd9Sstevel@tonic-gate 	struct T_opthdr *topth;
16357c478bd9Sstevel@tonic-gate 	opdes_t *optd;
16367c478bd9Sstevel@tonic-gate 	int error;
16377c478bd9Sstevel@tonic-gate 	t_uscalar_t optlen;
16387c478bd9Sstevel@tonic-gate 	t_scalar_t optsize;
16397c478bd9Sstevel@tonic-gate 	uchar_t *initptr = *resptrp;
16407c478bd9Sstevel@tonic-gate 
16417c478bd9Sstevel@tonic-gate 	ASSERT(reqopt->status == T_SUCCESS);
16427c478bd9Sstevel@tonic-gate 
16437c478bd9Sstevel@tonic-gate 	if (reqopt->name != T_ALLOPT) {
16447c478bd9Sstevel@tonic-gate 		topth = (struct T_opthdr *)*resptrp;
16457c478bd9Sstevel@tonic-gate 		*resptrp += sizeof (struct T_opthdr);
16467c478bd9Sstevel@tonic-gate 		error = (*setfn)(q, optset_context, reqopt->level, reqopt->name,
16477c478bd9Sstevel@tonic-gate 		    reqopt->len - sizeof (struct T_opthdr),
16487c478bd9Sstevel@tonic-gate 		    _TPI_TOPT_DATA(reqopt), &optlen, _TPI_TOPT_DATA(topth),
16497c478bd9Sstevel@tonic-gate 		    NULL, cr, first_mp);
16507c478bd9Sstevel@tonic-gate 		if (error) {
16517c478bd9Sstevel@tonic-gate 			/* failed - reset "*resptrp" */
16527c478bd9Sstevel@tonic-gate 			*resptrp -= sizeof (struct T_opthdr);
16537c478bd9Sstevel@tonic-gate 			if (error == EINPROGRESS)
16547c478bd9Sstevel@tonic-gate 				return (error);
16557c478bd9Sstevel@tonic-gate 		} else {
16567c478bd9Sstevel@tonic-gate 			/*
16577c478bd9Sstevel@tonic-gate 			 * success - "value" already filled in setfn()
16587c478bd9Sstevel@tonic-gate 			 */
16597c478bd9Sstevel@tonic-gate 			topth->len = (t_uscalar_t)(optlen +
16607c478bd9Sstevel@tonic-gate 			    sizeof (struct T_opthdr));
16617c478bd9Sstevel@tonic-gate 			topth->level = reqopt->level;
16627c478bd9Sstevel@tonic-gate 			topth->name = reqopt->name;
16637c478bd9Sstevel@tonic-gate 			topth->status = reqopt->status;
16647c478bd9Sstevel@tonic-gate 			*resptrp += _TPI_ALIGN_TOPT(optlen);
16657c478bd9Sstevel@tonic-gate 			*worst_statusp = get_worst_status(topth->status,
16667c478bd9Sstevel@tonic-gate 			    *worst_statusp);
16677c478bd9Sstevel@tonic-gate 		}
16687c478bd9Sstevel@tonic-gate 	} else {		/* T_ALLOPT processing */
16697c478bd9Sstevel@tonic-gate 		/* only for T_NEGOTIATE case */
16707c478bd9Sstevel@tonic-gate 		ASSERT(optset_context == SETFN_OPTCOM_NEGOTIATE);
16717c478bd9Sstevel@tonic-gate 		ASSERT(topmost_tpiprovider == B_TRUE);
16727c478bd9Sstevel@tonic-gate 
16737c478bd9Sstevel@tonic-gate 		/* scan and set all options to default value */
16747c478bd9Sstevel@tonic-gate 		for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
16757c478bd9Sstevel@tonic-gate 
16767c478bd9Sstevel@tonic-gate 			/* skip other levels */
16777c478bd9Sstevel@tonic-gate 			if (reqopt->level != optd->opdes_level)
16787c478bd9Sstevel@tonic-gate 				continue;
16797c478bd9Sstevel@tonic-gate 
16807c478bd9Sstevel@tonic-gate 			if (OA_EXECUTE_PERMISSION(optd, cr) ||
16817c478bd9Sstevel@tonic-gate 			    OA_NO_PERMISSION(optd, cr)) {
16827c478bd9Sstevel@tonic-gate 				/*
16837c478bd9Sstevel@tonic-gate 				 * skip this one too. Does not make sense to
16847c478bd9Sstevel@tonic-gate 				 * set anything to default value for "execute"
16857c478bd9Sstevel@tonic-gate 				 * options.
16867c478bd9Sstevel@tonic-gate 				 */
16877c478bd9Sstevel@tonic-gate 				continue;
16887c478bd9Sstevel@tonic-gate 			}
16897c478bd9Sstevel@tonic-gate 
16907c478bd9Sstevel@tonic-gate 			if (OA_READONLY_PERMISSION(optd, cr)) {
16917c478bd9Sstevel@tonic-gate 				/*
16927c478bd9Sstevel@tonic-gate 				 * Return with T_READONLY status (and no value
16937c478bd9Sstevel@tonic-gate 				 * part). Note: spec is not clear but
16947c478bd9Sstevel@tonic-gate 				 * XTI test suite needs this.
16957c478bd9Sstevel@tonic-gate 				 */
16967c478bd9Sstevel@tonic-gate 				topth = (struct T_opthdr *)*resptrp;
16977c478bd9Sstevel@tonic-gate 				topth->len = sizeof (struct T_opthdr);
16987c478bd9Sstevel@tonic-gate 				*resptrp += topth->len;
16997c478bd9Sstevel@tonic-gate 				topth->level = reqopt->level;
17007c478bd9Sstevel@tonic-gate 				topth->name = optd->opdes_name;
17017c478bd9Sstevel@tonic-gate 				topth->status = T_READONLY;
17027c478bd9Sstevel@tonic-gate 				*worst_statusp = get_worst_status(topth->status,
17037c478bd9Sstevel@tonic-gate 				    *worst_statusp);
17047c478bd9Sstevel@tonic-gate 				continue;
17057c478bd9Sstevel@tonic-gate 			}
17067c478bd9Sstevel@tonic-gate 
17077c478bd9Sstevel@tonic-gate 			/*
17087c478bd9Sstevel@tonic-gate 			 * It is not read only or execute type
17097c478bd9Sstevel@tonic-gate 			 * the it must have write permission
17107c478bd9Sstevel@tonic-gate 			 */
17117c478bd9Sstevel@tonic-gate 			ASSERT(OA_WRITE_PERMISSION(optd, cr));
17127c478bd9Sstevel@tonic-gate 
17137c478bd9Sstevel@tonic-gate 			topth = (struct T_opthdr *)*resptrp;
17147c478bd9Sstevel@tonic-gate 			*resptrp += sizeof (struct T_opthdr);
17157c478bd9Sstevel@tonic-gate 
17167c478bd9Sstevel@tonic-gate 			topth->len = sizeof (struct T_opthdr);
17177c478bd9Sstevel@tonic-gate 			topth->level = reqopt->level;
17187c478bd9Sstevel@tonic-gate 			topth->name = optd->opdes_name;
17197c478bd9Sstevel@tonic-gate 			if (optd->opdes_props & OP_NODEFAULT) {
17207c478bd9Sstevel@tonic-gate 				/*
17217c478bd9Sstevel@tonic-gate 				 * Option of "no default value" so it does not
17227c478bd9Sstevel@tonic-gate 				 * make sense to try to set it. We just return
17237c478bd9Sstevel@tonic-gate 				 * header with status of T_SUCCESS
17247c478bd9Sstevel@tonic-gate 				 * XXX should this be failure ?
17257c478bd9Sstevel@tonic-gate 				 */
17267c478bd9Sstevel@tonic-gate 				topth->status = T_SUCCESS;
17277c478bd9Sstevel@tonic-gate 				continue; /* skip setting */
17287c478bd9Sstevel@tonic-gate 			}
17297c478bd9Sstevel@tonic-gate 			if (optd->opdes_props & OP_DEF_FN) {
17307c478bd9Sstevel@tonic-gate 				if ((optd->opdes_props & OP_VARLEN) ||
17317c478bd9Sstevel@tonic-gate 				    ((optsize = (*deffn)(q, reqopt->level,
17327c478bd9Sstevel@tonic-gate 					optd->opdes_name,
17337c478bd9Sstevel@tonic-gate 					(uchar_t *)optd->opdes_defbuf)) < 0)) {
17347c478bd9Sstevel@tonic-gate 					/* XXX - skip these too */
17357c478bd9Sstevel@tonic-gate 					topth->status = T_SUCCESS;
17367c478bd9Sstevel@tonic-gate 					continue; /* skip setting */
17377c478bd9Sstevel@tonic-gate 				}
17387c478bd9Sstevel@tonic-gate 			} else {
17397c478bd9Sstevel@tonic-gate 				optsize = optd->opdes_size;
17407c478bd9Sstevel@tonic-gate 			}
17417c478bd9Sstevel@tonic-gate 
17427c478bd9Sstevel@tonic-gate 
17437c478bd9Sstevel@tonic-gate 			/* set option of this level */
17447c478bd9Sstevel@tonic-gate 			error = (*setfn)(q, SETFN_OPTCOM_NEGOTIATE,
17457c478bd9Sstevel@tonic-gate 			    reqopt->level, optd->opdes_name, optsize,
17467c478bd9Sstevel@tonic-gate 			    (uchar_t *)optd->opdes_defbuf, &optlen,
17477c478bd9Sstevel@tonic-gate 			    _TPI_TOPT_DATA(topth), NULL, cr, NULL);
17487c478bd9Sstevel@tonic-gate 			if (error) {
17497c478bd9Sstevel@tonic-gate 				/*
17507c478bd9Sstevel@tonic-gate 				 * failed, return as T_FAILURE and null value
17517c478bd9Sstevel@tonic-gate 				 * part. Maybe something downstream will
17527c478bd9Sstevel@tonic-gate 				 * handle this one and fill in a value. Here
17537c478bd9Sstevel@tonic-gate 				 * it is just part of T_ALLOPT expansion.
17547c478bd9Sstevel@tonic-gate 				 */
17557c478bd9Sstevel@tonic-gate 				topth->status = T_FAILURE;
17567c478bd9Sstevel@tonic-gate 				*worst_statusp = get_worst_status(topth->status,
17577c478bd9Sstevel@tonic-gate 				    *worst_statusp);
17587c478bd9Sstevel@tonic-gate 			} else {
17597c478bd9Sstevel@tonic-gate 				/* success */
17607c478bd9Sstevel@tonic-gate 				topth->len += optlen;
17617c478bd9Sstevel@tonic-gate 				topth->status = T_SUCCESS;
17627c478bd9Sstevel@tonic-gate 				*resptrp += _TPI_ALIGN_TOPT(optlen);
17637c478bd9Sstevel@tonic-gate 			}
17647c478bd9Sstevel@tonic-gate 		} /* end for loop */
17657c478bd9Sstevel@tonic-gate 		/* END T_ALLOPT */
17667c478bd9Sstevel@tonic-gate 	}
17677c478bd9Sstevel@tonic-gate 
17687c478bd9Sstevel@tonic-gate 	if (*resptrp == initptr) {
17697c478bd9Sstevel@tonic-gate 		/*
17707c478bd9Sstevel@tonic-gate 		 * setfn failed and does not want to handle this option. Maybe
17717c478bd9Sstevel@tonic-gate 		 * something downstream will or something upstream
17727c478bd9Sstevel@tonic-gate 		 * did. Copy the input as is from input option buffer if any to
17737c478bd9Sstevel@tonic-gate 		 * maintain transparency (maybe something at a level above
17747c478bd9Sstevel@tonic-gate 		 * did something.
17757c478bd9Sstevel@tonic-gate 		 */
17767c478bd9Sstevel@tonic-gate 		if (topmost_tpiprovider)
17777c478bd9Sstevel@tonic-gate 			reqopt->status = T_FAILURE;
17787c478bd9Sstevel@tonic-gate 		bcopy(reqopt, *resptrp, reqopt->len);
17797c478bd9Sstevel@tonic-gate 		*resptrp += _TPI_ALIGN_TOPT(reqopt->len);
17807c478bd9Sstevel@tonic-gate 		*worst_statusp = get_worst_status(reqopt->status,
17817c478bd9Sstevel@tonic-gate 		    *worst_statusp);
17827c478bd9Sstevel@tonic-gate 	}
17837c478bd9Sstevel@tonic-gate 	return (0);
17847c478bd9Sstevel@tonic-gate }
17857c478bd9Sstevel@tonic-gate 
17867c478bd9Sstevel@tonic-gate /*
17877c478bd9Sstevel@tonic-gate  * The following routines process options buffer passed with
17887c478bd9Sstevel@tonic-gate  * T_CONN_REQ, T_CONN_RES and T_UNITDATA_REQ.
17897c478bd9Sstevel@tonic-gate  * This routine does the consistency check applied to the
17907c478bd9Sstevel@tonic-gate  * sanity of formatting of multiple options packed in the
17917c478bd9Sstevel@tonic-gate  * buffer.
17927c478bd9Sstevel@tonic-gate  *
17937c478bd9Sstevel@tonic-gate  * XTI brain damage alert:
17947c478bd9Sstevel@tonic-gate  * XTI interface adopts the notion of an option being an
17957c478bd9Sstevel@tonic-gate  * "absolute requirement" from OSI transport service (but applies
17967c478bd9Sstevel@tonic-gate  * it to all transports including Internet transports).
17977c478bd9Sstevel@tonic-gate  * The main effect of that is action on failure to "negotiate" a
17987c478bd9Sstevel@tonic-gate  * requested option to the exact requested value
17997c478bd9Sstevel@tonic-gate  *
18007c478bd9Sstevel@tonic-gate  *          - if the option is an "absolute requirement", the primitive
18017c478bd9Sstevel@tonic-gate  *            is aborted (e.g T_DISCON_REQ or T_UDERR generated)
18027c478bd9Sstevel@tonic-gate  *          - if the option is NOT and "absolute requirement" it can
18037c478bd9Sstevel@tonic-gate  *            just be ignored.
18047c478bd9Sstevel@tonic-gate  *
18057c478bd9Sstevel@tonic-gate  * We would not support "negotiating" of options on connection
18067c478bd9Sstevel@tonic-gate  * primitives for Internet transports. However just in case we
18077c478bd9Sstevel@tonic-gate  * forced to in order to pass strange test suites, the design here
18087c478bd9Sstevel@tonic-gate  * tries to support these notions.
18097c478bd9Sstevel@tonic-gate  *
18107c478bd9Sstevel@tonic-gate  * tpi_optcom_buf(q, mp, opt_lenp, opt_offset, cred, dbobjp, thisdg_attrs,
18117c478bd9Sstevel@tonic-gate  *	*is_absreq_failurep)
18127c478bd9Sstevel@tonic-gate  *
18137c478bd9Sstevel@tonic-gate  * - Verify the option buffer, if formatted badly, return error 1
18147c478bd9Sstevel@tonic-gate  *
18157c478bd9Sstevel@tonic-gate  * - If it is a "permissions" failure (read-only), return error 2
18167c478bd9Sstevel@tonic-gate  *
18177c478bd9Sstevel@tonic-gate  * - Else, process the option "in place", the following can happen,
18187c478bd9Sstevel@tonic-gate  *	     - if a "privileged" option, mark it as "ignored".
18197c478bd9Sstevel@tonic-gate  *	     - if "not supported", mark "ignored"
18207c478bd9Sstevel@tonic-gate  *	     - if "supported" attempt negotiation and fill result in
18217c478bd9Sstevel@tonic-gate  *	       the outcome
18227c478bd9Sstevel@tonic-gate  *			- if "absolute requirement", set "*is_absreq_failurep"
18237c478bd9Sstevel@tonic-gate  *			- if NOT an "absolute requirement", then our
18247c478bd9Sstevel@tonic-gate  *			  interpretation is to mark is at ignored if
18257c478bd9Sstevel@tonic-gate  *			  negotiation fails (Spec allows partial success
18267c478bd9Sstevel@tonic-gate  *			  as in OSI protocols but not failure)
18277c478bd9Sstevel@tonic-gate  *
18287c478bd9Sstevel@tonic-gate  *   Then delete "ignored" options from option buffer and return success.
18297c478bd9Sstevel@tonic-gate  *
18307c478bd9Sstevel@tonic-gate  */
18317c478bd9Sstevel@tonic-gate 
18327c478bd9Sstevel@tonic-gate int
18337c478bd9Sstevel@tonic-gate tpi_optcom_buf(queue_t *q, mblk_t *mp, t_scalar_t *opt_lenp,
18347c478bd9Sstevel@tonic-gate     t_scalar_t opt_offset, cred_t *cr, optdb_obj_t *dbobjp,
18357c478bd9Sstevel@tonic-gate     void *thisdg_attrs, int *is_absreq_failurep)
18367c478bd9Sstevel@tonic-gate {
18377c478bd9Sstevel@tonic-gate 	opt_set_fn setfn = dbobjp->odb_setfn;
18387c478bd9Sstevel@tonic-gate 	opdes_t *opt_arr = dbobjp->odb_opt_des_arr;
18397c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
18407c478bd9Sstevel@tonic-gate 	struct T_opthdr *opt, *opt_start, *opt_end;
18417c478bd9Sstevel@tonic-gate 	mblk_t  *copy_mp_head;
18427c478bd9Sstevel@tonic-gate 	uchar_t *optr, *init_optr;
18437c478bd9Sstevel@tonic-gate 	opdes_t *optd;
18447c478bd9Sstevel@tonic-gate 	uint_t optset_context;
18457c478bd9Sstevel@tonic-gate 	t_uscalar_t olen;
18467c478bd9Sstevel@tonic-gate 	int error = 0;
18477c478bd9Sstevel@tonic-gate 
18487c478bd9Sstevel@tonic-gate 	ASSERT((uchar_t *)opt_lenp > mp->b_rptr &&
18497c478bd9Sstevel@tonic-gate 	    (uchar_t *)opt_lenp < mp->b_wptr);
18507c478bd9Sstevel@tonic-gate 
18517c478bd9Sstevel@tonic-gate 	copy_mp_head = NULL;
18527c478bd9Sstevel@tonic-gate 	*is_absreq_failurep = 0;
18537c478bd9Sstevel@tonic-gate 	switch (((union T_primitives *)mp->b_rptr)->type) {
18547c478bd9Sstevel@tonic-gate 	case T_CONN_REQ:
18557c478bd9Sstevel@tonic-gate 	case T_CONN_RES:
18567c478bd9Sstevel@tonic-gate 		optset_context = SETFN_CONN_NEGOTIATE;
18577c478bd9Sstevel@tonic-gate 		break;
18587c478bd9Sstevel@tonic-gate 	case T_UNITDATA_REQ:
18597c478bd9Sstevel@tonic-gate 		optset_context = SETFN_UD_NEGOTIATE;
18607c478bd9Sstevel@tonic-gate 		break;
18617c478bd9Sstevel@tonic-gate 	default:
18627c478bd9Sstevel@tonic-gate 		/*
18637c478bd9Sstevel@tonic-gate 		 * should never get here, all possible TPI primitives
18647c478bd9Sstevel@tonic-gate 		 * where this can be called from should be accounted
18657c478bd9Sstevel@tonic-gate 		 * for in the cases above
18667c478bd9Sstevel@tonic-gate 		 */
18677c478bd9Sstevel@tonic-gate 		return (EINVAL);
18687c478bd9Sstevel@tonic-gate 	}
18697c478bd9Sstevel@tonic-gate 
18707c478bd9Sstevel@tonic-gate 	if ((opt_start = (struct T_opthdr *)
18717c478bd9Sstevel@tonic-gate 	    mi_offset_param(mp, opt_offset, *opt_lenp)) == NULL) {
18727c478bd9Sstevel@tonic-gate 		error = ENOPROTOOPT;
18737c478bd9Sstevel@tonic-gate 		goto error_ret;
18747c478bd9Sstevel@tonic-gate 	}
18757c478bd9Sstevel@tonic-gate 	if (!__TPI_TOPT_ISALIGNED(opt_start)) {
18767c478bd9Sstevel@tonic-gate 		error = ENOPROTOOPT;
18777c478bd9Sstevel@tonic-gate 		goto error_ret;
18787c478bd9Sstevel@tonic-gate 	}
18797c478bd9Sstevel@tonic-gate 
18807c478bd9Sstevel@tonic-gate 	opt_end = (struct T_opthdr *)((uchar_t *)opt_start
18817c478bd9Sstevel@tonic-gate 	    + *opt_lenp);
18827c478bd9Sstevel@tonic-gate 
18837c478bd9Sstevel@tonic-gate 	if ((copy_mp_head = copyb(mp)) == (mblk_t *)NULL) {
18847c478bd9Sstevel@tonic-gate 		error = ENOMEM;
18857c478bd9Sstevel@tonic-gate 		goto error_ret;
18867c478bd9Sstevel@tonic-gate 	}
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate 	init_optr = optr = (uchar_t *)&copy_mp_head->b_rptr[opt_offset];
18897c478bd9Sstevel@tonic-gate 
18907c478bd9Sstevel@tonic-gate 	for (opt = opt_start; opt && (opt < opt_end);
18917c478bd9Sstevel@tonic-gate 	    opt = _TPI_TOPT_NEXTHDR(opt_start, *opt_lenp, opt)) {
18927c478bd9Sstevel@tonic-gate 		/*
18937c478bd9Sstevel@tonic-gate 		 * Validate the option for length and alignment
18947c478bd9Sstevel@tonic-gate 		 * before accessing anything in it
18957c478bd9Sstevel@tonic-gate 		 */
18967c478bd9Sstevel@tonic-gate 		if (!_TPI_TOPT_VALID(opt, opt_start, opt_end)) {
18977c478bd9Sstevel@tonic-gate 			error = ENOPROTOOPT;
18987c478bd9Sstevel@tonic-gate 			goto error_ret;
18997c478bd9Sstevel@tonic-gate 		}
19007c478bd9Sstevel@tonic-gate 
19017c478bd9Sstevel@tonic-gate 		/* Find the option in the opt_arr. */
19027c478bd9Sstevel@tonic-gate 		optd = opt_chk_lookup(opt->level, opt->name,
19037c478bd9Sstevel@tonic-gate 		    opt_arr, opt_arr_cnt);
19047c478bd9Sstevel@tonic-gate 
19057c478bd9Sstevel@tonic-gate 		if (optd == NULL) {
19067c478bd9Sstevel@tonic-gate 			/*
19077c478bd9Sstevel@tonic-gate 			 * Option not found
19087c478bd9Sstevel@tonic-gate 			 */
19097c478bd9Sstevel@tonic-gate 			opt->status = T_NOTSUPPORT;
19107c478bd9Sstevel@tonic-gate 			continue;
19117c478bd9Sstevel@tonic-gate 		}
19127c478bd9Sstevel@tonic-gate 
19137c478bd9Sstevel@tonic-gate 		/*
19147c478bd9Sstevel@tonic-gate 		 * Weird but as in XTI spec.
19157c478bd9Sstevel@tonic-gate 		 * Sec 6.3.6 "Privileged and ReadOnly Options"
19167c478bd9Sstevel@tonic-gate 		 * Permission problems (e.g.readonly) fail with bad access
19177c478bd9Sstevel@tonic-gate 		 * BUT "privileged" option request from those NOT PRIVILEGED
19187c478bd9Sstevel@tonic-gate 		 * are to be merely "ignored".
19197c478bd9Sstevel@tonic-gate 		 * XXX Prevents "probing" of privileged options ?
19207c478bd9Sstevel@tonic-gate 		 */
19217c478bd9Sstevel@tonic-gate 		if (OA_READONLY_PERMISSION(optd, cr)) {
19227c478bd9Sstevel@tonic-gate 			error = EACCES;
19237c478bd9Sstevel@tonic-gate 			goto error_ret;
19247c478bd9Sstevel@tonic-gate 		}
19257c478bd9Sstevel@tonic-gate 		if (OA_MATCHED_PRIV(optd, cr)) {
19267c478bd9Sstevel@tonic-gate 			/*
19277c478bd9Sstevel@tonic-gate 			 * For privileged options, we DO perform
19287c478bd9Sstevel@tonic-gate 			 * access checks as is common sense
19297c478bd9Sstevel@tonic-gate 			 */
19307c478bd9Sstevel@tonic-gate 			if (!OA_WX_ANYPRIV(optd)) {
19317c478bd9Sstevel@tonic-gate 				error = EACCES;
19327c478bd9Sstevel@tonic-gate 				goto error_ret;
19337c478bd9Sstevel@tonic-gate 			}
19347c478bd9Sstevel@tonic-gate 		} else {
19357c478bd9Sstevel@tonic-gate 			/*
19367c478bd9Sstevel@tonic-gate 			 * For non privileged, we fail instead following
19377c478bd9Sstevel@tonic-gate 			 * "ignore" semantics dictated by XTI spec for
19387c478bd9Sstevel@tonic-gate 			 * permissions problems.
19397c478bd9Sstevel@tonic-gate 			 * Sec 6.3.6 "Privileged and ReadOnly Options"
19407c478bd9Sstevel@tonic-gate 			 * XXX Should we do "ignore" semantics ?
19417c478bd9Sstevel@tonic-gate 			 */
19427c478bd9Sstevel@tonic-gate 			if (!OA_WX_NOPRIV(optd)) { /* nopriv */
19437c478bd9Sstevel@tonic-gate 				opt->status = T_FAILURE;
19447c478bd9Sstevel@tonic-gate 				continue;
19457c478bd9Sstevel@tonic-gate 			}
19467c478bd9Sstevel@tonic-gate 		}
19477c478bd9Sstevel@tonic-gate 		/*
19487c478bd9Sstevel@tonic-gate 		 *
19497c478bd9Sstevel@tonic-gate 		 * If the negotiation fails, for options that
19507c478bd9Sstevel@tonic-gate 		 * are "absolute requirement", it is a fatal error.
19517c478bd9Sstevel@tonic-gate 		 * For options that are NOT "absolute requirements",
19527c478bd9Sstevel@tonic-gate 		 * and the value fails to negotiate, the XTI spec
19537c478bd9Sstevel@tonic-gate 		 * only considers the possibility of partial success
19547c478bd9Sstevel@tonic-gate 		 * (T_PARTSUCCES - not likely for Internet protocols).
19557c478bd9Sstevel@tonic-gate 		 * The spec is in denial about complete failure
19567c478bd9Sstevel@tonic-gate 		 * (T_FAILURE) to negotiate for options that are
19577c478bd9Sstevel@tonic-gate 		 * carried on T_CONN_REQ/T_CONN_RES/T_UNITDATA
19587c478bd9Sstevel@tonic-gate 		 * We interpret the T_FAILURE to negotiate an option
19597c478bd9Sstevel@tonic-gate 		 * that is NOT an absolute requirement that it is safe
19607c478bd9Sstevel@tonic-gate 		 * to ignore it.
19617c478bd9Sstevel@tonic-gate 		 */
19627c478bd9Sstevel@tonic-gate 
19637c478bd9Sstevel@tonic-gate 		/* verify length */
19647c478bd9Sstevel@tonic-gate 		if (!opt_length_ok(optd, opt)) {
19657c478bd9Sstevel@tonic-gate 			/* bad size */
19667c478bd9Sstevel@tonic-gate 			if ((optd->opdes_props & OP_NOT_ABSREQ) == 0) {
19677c478bd9Sstevel@tonic-gate 				/* option is absolute requirement */
19687c478bd9Sstevel@tonic-gate 				*is_absreq_failurep = 1;
19697c478bd9Sstevel@tonic-gate 				error = EINVAL;
19707c478bd9Sstevel@tonic-gate 				goto error_ret;
19717c478bd9Sstevel@tonic-gate 			}
19727c478bd9Sstevel@tonic-gate 			opt->status = T_FAILURE;
19737c478bd9Sstevel@tonic-gate 			continue;
19747c478bd9Sstevel@tonic-gate 		}
19757c478bd9Sstevel@tonic-gate 
19767c478bd9Sstevel@tonic-gate 		/*
19777c478bd9Sstevel@tonic-gate 		 * verified generic attributes. Now call set function.
19787c478bd9Sstevel@tonic-gate 		 * Note: We assume the following to simplify code.
19797c478bd9Sstevel@tonic-gate 		 * XXX If this is found not to be valid, this routine
19807c478bd9Sstevel@tonic-gate 		 * will need to be rewritten. At this point it would
19817c478bd9Sstevel@tonic-gate 		 * be premature to introduce more complexity than is
19827c478bd9Sstevel@tonic-gate 		 * needed.
19837c478bd9Sstevel@tonic-gate 		 * Assumption: For variable length options, we assume
19847c478bd9Sstevel@tonic-gate 		 * that the value returned will be same or less length
19857c478bd9Sstevel@tonic-gate 		 * (size does not increase). This makes it OK to pass the
19867c478bd9Sstevel@tonic-gate 		 * same space for output as it is on input.
19877c478bd9Sstevel@tonic-gate 		 */
19887c478bd9Sstevel@tonic-gate 
19897c478bd9Sstevel@tonic-gate 		error = (*setfn)(q, optset_context, opt->level, opt->name,
19907c478bd9Sstevel@tonic-gate 		    opt->len - (t_uscalar_t)sizeof (struct T_opthdr),
19917c478bd9Sstevel@tonic-gate 		    _TPI_TOPT_DATA(opt), &olen, _TPI_TOPT_DATA(opt),
19927c478bd9Sstevel@tonic-gate 		    thisdg_attrs, cr, NULL);
19937c478bd9Sstevel@tonic-gate 
19947c478bd9Sstevel@tonic-gate 		if (olen > (int)(opt->len - sizeof (struct T_opthdr))) {
19957c478bd9Sstevel@tonic-gate 			/*
19967c478bd9Sstevel@tonic-gate 			 * Space on output more than space on input. Should
19977c478bd9Sstevel@tonic-gate 			 * not happen and we consider it a bug/error.
19987c478bd9Sstevel@tonic-gate 			 * More of a restriction than an error in our
19997c478bd9Sstevel@tonic-gate 			 * implementation. Will see if we can live with this
20007c478bd9Sstevel@tonic-gate 			 * otherwise code will get more hairy with multiple
20017c478bd9Sstevel@tonic-gate 			 * passes.
20027c478bd9Sstevel@tonic-gate 			 */
20037c478bd9Sstevel@tonic-gate 			error = EINVAL;
20047c478bd9Sstevel@tonic-gate 			goto error_ret;
20057c478bd9Sstevel@tonic-gate 		}
20067c478bd9Sstevel@tonic-gate 		if (error != 0) {
20077c478bd9Sstevel@tonic-gate 			if ((optd->opdes_props & OP_NOT_ABSREQ) == 0) {
20087c478bd9Sstevel@tonic-gate 				/* option is absolute requirement. */
20097c478bd9Sstevel@tonic-gate 				*is_absreq_failurep = 1;
20107c478bd9Sstevel@tonic-gate 				goto error_ret;
20117c478bd9Sstevel@tonic-gate 			}
20127c478bd9Sstevel@tonic-gate 			/*
20137c478bd9Sstevel@tonic-gate 			 * failed - but option "not an absolute
20147c478bd9Sstevel@tonic-gate 			 * requirement"
20157c478bd9Sstevel@tonic-gate 			 */
20167c478bd9Sstevel@tonic-gate 			opt->status = T_FAILURE;
20177c478bd9Sstevel@tonic-gate 			continue;
20187c478bd9Sstevel@tonic-gate 		}
20197c478bd9Sstevel@tonic-gate 		/*
20207c478bd9Sstevel@tonic-gate 		 * Fill in the only possible successful result
20217c478bd9Sstevel@tonic-gate 		 * (Note: TPI allows for T_PARTSUCCESS - partial
20227c478bd9Sstevel@tonic-gate 		 * sucess result code which is relevant in OSI world
20237c478bd9Sstevel@tonic-gate 		 * and not possible in Internet code)
20247c478bd9Sstevel@tonic-gate 		 */
20257c478bd9Sstevel@tonic-gate 		opt->status = T_SUCCESS;
20267c478bd9Sstevel@tonic-gate 
20277c478bd9Sstevel@tonic-gate 		/*
20287c478bd9Sstevel@tonic-gate 		 * Add T_SUCCESS result code options to the "output" options.
20297c478bd9Sstevel@tonic-gate 		 * No T_FAILURES or T_NOTSUPPORT here as they are to be
20307c478bd9Sstevel@tonic-gate 		 * ignored.
20317c478bd9Sstevel@tonic-gate 		 * This code assumes output option buffer will
20327c478bd9Sstevel@tonic-gate 		 * be <= input option buffer.
20337c478bd9Sstevel@tonic-gate 		 *
20347c478bd9Sstevel@tonic-gate 		 * Copy option header+value
20357c478bd9Sstevel@tonic-gate 		 */
20367c478bd9Sstevel@tonic-gate 		bcopy(opt, optr, opt->len);
20377c478bd9Sstevel@tonic-gate 		optr +=  _TPI_ALIGN_TOPT(opt->len);
20387c478bd9Sstevel@tonic-gate 	}
20397c478bd9Sstevel@tonic-gate 	/*
20407c478bd9Sstevel@tonic-gate 	 * Overwrite the input mblk option buffer now with the output
20417c478bd9Sstevel@tonic-gate 	 * and update length, and contents in original mbl
20427c478bd9Sstevel@tonic-gate 	 * (offset remains unchanged).
20437c478bd9Sstevel@tonic-gate 	 */
20447c478bd9Sstevel@tonic-gate 	*opt_lenp = (t_scalar_t)(optr - init_optr);
20457c478bd9Sstevel@tonic-gate 	if (*opt_lenp > 0) {
20467c478bd9Sstevel@tonic-gate 		bcopy(init_optr, opt_start, *opt_lenp);
20477c478bd9Sstevel@tonic-gate 	}
20487c478bd9Sstevel@tonic-gate 
20497c478bd9Sstevel@tonic-gate error_ret:
20507c478bd9Sstevel@tonic-gate 	if (copy_mp_head != NULL)
20517c478bd9Sstevel@tonic-gate 		freeb(copy_mp_head);
20527c478bd9Sstevel@tonic-gate 	return (error);
20537c478bd9Sstevel@tonic-gate }
20547c478bd9Sstevel@tonic-gate 
20557c478bd9Sstevel@tonic-gate static opdes_t *
20567c478bd9Sstevel@tonic-gate opt_chk_lookup(t_uscalar_t level, t_uscalar_t name, opdes_t *opt_arr,
20577c478bd9Sstevel@tonic-gate     uint_t opt_arr_cnt)
20587c478bd9Sstevel@tonic-gate {
20597c478bd9Sstevel@tonic-gate 	opdes_t		*optd;
20607c478bd9Sstevel@tonic-gate 
20617c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt];
20627c478bd9Sstevel@tonic-gate 	    optd++) {
20637c478bd9Sstevel@tonic-gate 		if (level == (uint_t)optd->opdes_level &&
20647c478bd9Sstevel@tonic-gate 		    name == (uint_t)optd->opdes_name)
20657c478bd9Sstevel@tonic-gate 			return (optd);
20667c478bd9Sstevel@tonic-gate 	}
20677c478bd9Sstevel@tonic-gate 	return (NULL);
20687c478bd9Sstevel@tonic-gate }
20697c478bd9Sstevel@tonic-gate 
20707c478bd9Sstevel@tonic-gate static boolean_t
20717c478bd9Sstevel@tonic-gate opt_level_valid(t_uscalar_t level, optlevel_t *valid_level_arr,
20727c478bd9Sstevel@tonic-gate     uint_t valid_level_arr_cnt)
20737c478bd9Sstevel@tonic-gate {
20747c478bd9Sstevel@tonic-gate 	optlevel_t		*olp;
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 	for (olp = valid_level_arr;
20777c478bd9Sstevel@tonic-gate 	    olp < &valid_level_arr[valid_level_arr_cnt];
20787c478bd9Sstevel@tonic-gate 	    olp++) {
20797c478bd9Sstevel@tonic-gate 		if (level == (uint_t)(*olp))
20807c478bd9Sstevel@tonic-gate 			return (B_TRUE);
20817c478bd9Sstevel@tonic-gate 	}
20827c478bd9Sstevel@tonic-gate 	return (B_FALSE);
20837c478bd9Sstevel@tonic-gate }
20847c478bd9Sstevel@tonic-gate 
20857c478bd9Sstevel@tonic-gate 
20867c478bd9Sstevel@tonic-gate /*
20877c478bd9Sstevel@tonic-gate  * Compute largest possible size for an option buffer containing
20887c478bd9Sstevel@tonic-gate  * all options in one buffer.
20897c478bd9Sstevel@tonic-gate  *
20907c478bd9Sstevel@tonic-gate  * XXX TBD, investigate use of opt_bloated_maxsize() to avoid
20917c478bd9Sstevel@tonic-gate  *     wastefully large buffer allocation.
20927c478bd9Sstevel@tonic-gate  */
20937c478bd9Sstevel@tonic-gate static size_t
20947c478bd9Sstevel@tonic-gate opt_level_allopts_lengths(t_uscalar_t level, opdes_t *opt_arr,
20957c478bd9Sstevel@tonic-gate     uint_t opt_arr_cnt)
20967c478bd9Sstevel@tonic-gate {
20977c478bd9Sstevel@tonic-gate 	opdes_t		*optd;
20987c478bd9Sstevel@tonic-gate 	size_t allopt_len = 0;	/* 0 implies no option at this level */
20997c478bd9Sstevel@tonic-gate 
21007c478bd9Sstevel@tonic-gate 	/*
21017c478bd9Sstevel@tonic-gate 	 * Scan opt_arr computing aggregate length
21027c478bd9Sstevel@tonic-gate 	 * requirement for storing values of all
21037c478bd9Sstevel@tonic-gate 	 * options.
21047c478bd9Sstevel@tonic-gate 	 * Note: we do not filter for permissions
21057c478bd9Sstevel@tonic-gate 	 * etc. This will be >= the real aggregate
21067c478bd9Sstevel@tonic-gate 	 * length required (upper bound).
21077c478bd9Sstevel@tonic-gate 	 */
21087c478bd9Sstevel@tonic-gate 
21097c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt];
21107c478bd9Sstevel@tonic-gate 	    optd++) {
21117c478bd9Sstevel@tonic-gate 		if (level == optd->opdes_level) {
21127c478bd9Sstevel@tonic-gate 			allopt_len += sizeof (struct T_opthdr) +
21137c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_TOPT(optd->opdes_size);
21147c478bd9Sstevel@tonic-gate 		}
21157c478bd9Sstevel@tonic-gate 	}
21167c478bd9Sstevel@tonic-gate 	return (allopt_len);	/* 0 implies level not found */
21177c478bd9Sstevel@tonic-gate }
21187c478bd9Sstevel@tonic-gate 
21197c478bd9Sstevel@tonic-gate /*
21207c478bd9Sstevel@tonic-gate  * Compute largest possible size for an option buffer containing
21217c478bd9Sstevel@tonic-gate  * all options in one buffer - a (theoretical?) worst case scenario
21227c478bd9Sstevel@tonic-gate  * for certain cases.
21237c478bd9Sstevel@tonic-gate  */
21247c478bd9Sstevel@tonic-gate t_uscalar_t
21257c478bd9Sstevel@tonic-gate optcom_max_optbuf_len(opdes_t *opt_arr, uint_t opt_arr_cnt)
21267c478bd9Sstevel@tonic-gate {
21277c478bd9Sstevel@tonic-gate 	t_uscalar_t max_optbuf_len = sizeof (struct T_info_ack);
21287c478bd9Sstevel@tonic-gate 	opdes_t		*optd;
21297c478bd9Sstevel@tonic-gate 
21307c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
21317c478bd9Sstevel@tonic-gate 		max_optbuf_len += (t_uscalar_t)sizeof (struct T_opthdr) +
21327c478bd9Sstevel@tonic-gate 		    (t_uscalar_t)_TPI_ALIGN_TOPT(optd->opdes_size);
21337c478bd9Sstevel@tonic-gate 	}
21347c478bd9Sstevel@tonic-gate 	return (max_optbuf_len);
21357c478bd9Sstevel@tonic-gate }
21367c478bd9Sstevel@tonic-gate 
21377c478bd9Sstevel@tonic-gate /*
21387c478bd9Sstevel@tonic-gate  * Compute largest possible size for OPT_size for a transport.
21397c478bd9Sstevel@tonic-gate  * Heuristic used is to add all but certain extremely large
21407c478bd9Sstevel@tonic-gate  * size options; this is done by calling opt_bloated_maxsize().
21417c478bd9Sstevel@tonic-gate  * It affects user level allocations in TLI/XTI code using t_alloc()
21427c478bd9Sstevel@tonic-gate  * and other TLI/XTI implementation instance strucutures.
21437c478bd9Sstevel@tonic-gate  * The large size options excluded are presumed to be
21447c478bd9Sstevel@tonic-gate  * never accessed through the (theoretical?) worst case code paths
21457c478bd9Sstevel@tonic-gate  * through TLI/XTI as they are currently IPv6 specific options.
21467c478bd9Sstevel@tonic-gate  */
21477c478bd9Sstevel@tonic-gate 
21487c478bd9Sstevel@tonic-gate t_uscalar_t
21497c478bd9Sstevel@tonic-gate optcom_max_optsize(opdes_t *opt_arr, uint_t opt_arr_cnt)
21507c478bd9Sstevel@tonic-gate {
21517c478bd9Sstevel@tonic-gate 	t_uscalar_t max_optbuf_len = sizeof (struct T_info_ack);
21527c478bd9Sstevel@tonic-gate 	opdes_t		*optd;
21537c478bd9Sstevel@tonic-gate 
21547c478bd9Sstevel@tonic-gate 	for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
21557c478bd9Sstevel@tonic-gate 		if (!opt_bloated_maxsize(optd)) {
21567c478bd9Sstevel@tonic-gate 			max_optbuf_len +=
21577c478bd9Sstevel@tonic-gate 			    (t_uscalar_t)sizeof (struct T_opthdr) +
21587c478bd9Sstevel@tonic-gate 			    (t_uscalar_t)_TPI_ALIGN_TOPT(optd->opdes_size);
21597c478bd9Sstevel@tonic-gate 		}
21607c478bd9Sstevel@tonic-gate 	}
21617c478bd9Sstevel@tonic-gate 	return (max_optbuf_len);
21627c478bd9Sstevel@tonic-gate }
21637c478bd9Sstevel@tonic-gate 
21647c478bd9Sstevel@tonic-gate /*
21657c478bd9Sstevel@tonic-gate  * The theoretical model used in optcom_max_optsize() and
21667c478bd9Sstevel@tonic-gate  * opt_level_allopts_lengths() accounts for the worst case of all
21677c478bd9Sstevel@tonic-gate  * possible options for the theoretical cases and results in wasteful
21687c478bd9Sstevel@tonic-gate  * memory allocations for certain theoretically correct usage scenarios.
21697c478bd9Sstevel@tonic-gate  * In practice, the "features" they support are rarely, if ever,
21707c478bd9Sstevel@tonic-gate  * used and even then only by test suites for those features (VSU, VST).
21717c478bd9Sstevel@tonic-gate  * However, they result in large allocations due to the increased transport
21727c478bd9Sstevel@tonic-gate  * T_INFO_ACK OPT_size field affecting t_alloc() users and TLI/XTI library
21737c478bd9Sstevel@tonic-gate  * instance data structures for applications.
21747c478bd9Sstevel@tonic-gate  *
21757c478bd9Sstevel@tonic-gate  * The following routine opt_bloated_maxsize() supports a hack that avoids
21767c478bd9Sstevel@tonic-gate  * paying the tax for the bloated options by excluding them and pretending
21777c478bd9Sstevel@tonic-gate  * they don't exist for certain features without affecting features that
21787c478bd9Sstevel@tonic-gate  * do use them.
21797c478bd9Sstevel@tonic-gate  *
21807c478bd9Sstevel@tonic-gate  * XXX Currently implemented only for optcom_max_optsize()
21817c478bd9Sstevel@tonic-gate  *     (to reduce risk late in release).
21827c478bd9Sstevel@tonic-gate  *     TBD for future, investigate use in optcom_level_allopts_lengths() and
21837c478bd9Sstevel@tonic-gate  *     all the instances of T_ALLOPT processing to exclude "bloated options".
21847c478bd9Sstevel@tonic-gate  *     Will not affect VSU/VST tests as they do not test with IPPROTO_IPV6
21857c478bd9Sstevel@tonic-gate  *     level options which are the only ones that fit the "bloated maxsize"
21867c478bd9Sstevel@tonic-gate  *     option profile now.
21877c478bd9Sstevel@tonic-gate  */
21887c478bd9Sstevel@tonic-gate static boolean_t
21897c478bd9Sstevel@tonic-gate opt_bloated_maxsize(opdes_t *optd)
21907c478bd9Sstevel@tonic-gate {
21917c478bd9Sstevel@tonic-gate 	if (optd->opdes_level != IPPROTO_IPV6)
21927c478bd9Sstevel@tonic-gate 		return (B_FALSE);
21937c478bd9Sstevel@tonic-gate 	switch (optd->opdes_name) {
21947c478bd9Sstevel@tonic-gate 	case IPV6_HOPOPTS:
21957c478bd9Sstevel@tonic-gate 	case IPV6_DSTOPTS:
21967c478bd9Sstevel@tonic-gate 	case IPV6_RTHDRDSTOPTS:
21977c478bd9Sstevel@tonic-gate 	case IPV6_RTHDR:
21987c478bd9Sstevel@tonic-gate 	case IPV6_PATHMTU:
21997c478bd9Sstevel@tonic-gate 		return (B_TRUE);
22007c478bd9Sstevel@tonic-gate 	default:
22017c478bd9Sstevel@tonic-gate 		break;
22027c478bd9Sstevel@tonic-gate 	}
22037c478bd9Sstevel@tonic-gate 	return (B_FALSE);
22047c478bd9Sstevel@tonic-gate }
22057c478bd9Sstevel@tonic-gate 
22067c478bd9Sstevel@tonic-gate static boolean_t
22077c478bd9Sstevel@tonic-gate opt_length_ok(opdes_t *optd, struct T_opthdr *opt)
22087c478bd9Sstevel@tonic-gate {
22097c478bd9Sstevel@tonic-gate 	/*
22107c478bd9Sstevel@tonic-gate 	 * Verify length.
22117c478bd9Sstevel@tonic-gate 	 * Value specified should match length of fixed length option or be
22127c478bd9Sstevel@tonic-gate 	 * less than maxlen of variable length option.
22137c478bd9Sstevel@tonic-gate 	 */
22147c478bd9Sstevel@tonic-gate 	if (optd->opdes_props & OP_VARLEN) {
22157c478bd9Sstevel@tonic-gate 		if (opt->len <= optd->opdes_size +
22167c478bd9Sstevel@tonic-gate 		    (t_uscalar_t)sizeof (struct T_opthdr))
22177c478bd9Sstevel@tonic-gate 			return (B_TRUE);
22187c478bd9Sstevel@tonic-gate 	} else {
22197c478bd9Sstevel@tonic-gate 		/* fixed length option */
22207c478bd9Sstevel@tonic-gate 		if (opt->len == optd->opdes_size +
22217c478bd9Sstevel@tonic-gate 		    (t_uscalar_t)sizeof (struct T_opthdr))
22227c478bd9Sstevel@tonic-gate 			return (B_TRUE);
22237c478bd9Sstevel@tonic-gate 	}
22247c478bd9Sstevel@tonic-gate 	return (B_FALSE);
22257c478bd9Sstevel@tonic-gate }
2226*45916cd2Sjpk 
2227*45916cd2Sjpk /*
2228*45916cd2Sjpk  * This routine appends a pssed in hop-by-hop option to the existing
2229*45916cd2Sjpk  * option (in this case a cipso label encoded in HOPOPT option). The
2230*45916cd2Sjpk  * passed in option is always padded. The 'reservelen' is the
2231*45916cd2Sjpk  * length of reserved data (label). New memory will be allocated if
2232*45916cd2Sjpk  * the current buffer is not large enough. Return failure if memory
2233*45916cd2Sjpk  * can not be allocated.
2234*45916cd2Sjpk  */
2235*45916cd2Sjpk int
2236*45916cd2Sjpk optcom_pkt_set(uchar_t *invalp, uint_t inlen, boolean_t sticky,
2237*45916cd2Sjpk     uchar_t **optbufp, uint_t *optlenp, uint_t reservelen)
2238*45916cd2Sjpk {
2239*45916cd2Sjpk 	uchar_t *optbuf;
2240*45916cd2Sjpk 	uchar_t	*optp;
2241*45916cd2Sjpk 
2242*45916cd2Sjpk 	if (!sticky) {
2243*45916cd2Sjpk 		*optbufp = invalp;
2244*45916cd2Sjpk 		*optlenp = inlen;
2245*45916cd2Sjpk 		return (0);
2246*45916cd2Sjpk 	}
2247*45916cd2Sjpk 
2248*45916cd2Sjpk 	if (inlen == *optlenp - reservelen) {
2249*45916cd2Sjpk 		/* Unchanged length - no need to reallocate */
2250*45916cd2Sjpk 		optp = *optbufp + reservelen;
2251*45916cd2Sjpk 		bcopy(invalp, optp, inlen);
2252*45916cd2Sjpk 		if (reservelen != 0) {
2253*45916cd2Sjpk 			/*
2254*45916cd2Sjpk 			 * Convert the NextHeader and Length of the
2255*45916cd2Sjpk 			 * passed in hop-by-hop header to pads
2256*45916cd2Sjpk 			 */
2257*45916cd2Sjpk 			optp[0] = IP6OPT_PADN;
2258*45916cd2Sjpk 			optp[1] = 0;
2259*45916cd2Sjpk 		}
2260*45916cd2Sjpk 		return (0);
2261*45916cd2Sjpk 	}
2262*45916cd2Sjpk 	if (inlen + reservelen > 0) {
2263*45916cd2Sjpk 		/* Allocate new buffer before free */
2264*45916cd2Sjpk 		optbuf = kmem_alloc(inlen + reservelen, KM_NOSLEEP);
2265*45916cd2Sjpk 		if (optbuf == NULL)
2266*45916cd2Sjpk 			return (ENOMEM);
2267*45916cd2Sjpk 	} else {
2268*45916cd2Sjpk 		optbuf = NULL;
2269*45916cd2Sjpk 	}
2270*45916cd2Sjpk 
2271*45916cd2Sjpk 	/* Copy out old reserved data (label) */
2272*45916cd2Sjpk 	if (reservelen > 0)
2273*45916cd2Sjpk 		bcopy(*optbufp, optbuf, reservelen);
2274*45916cd2Sjpk 
2275*45916cd2Sjpk 	/* Free old buffer */
2276*45916cd2Sjpk 	if (*optlenp != 0)
2277*45916cd2Sjpk 		kmem_free(*optbufp, *optlenp);
2278*45916cd2Sjpk 
2279*45916cd2Sjpk 	if (inlen > 0)
2280*45916cd2Sjpk 		bcopy(invalp, optbuf + reservelen, inlen);
2281*45916cd2Sjpk 
2282*45916cd2Sjpk 	if (reservelen != 0) {
2283*45916cd2Sjpk 		/*
2284*45916cd2Sjpk 		 * Convert the NextHeader and Length of the
2285*45916cd2Sjpk 		 * passed in hop-by-hop header to pads
2286*45916cd2Sjpk 		 */
2287*45916cd2Sjpk 		optbuf[reservelen] = IP6OPT_PADN;
2288*45916cd2Sjpk 		optbuf[reservelen + 1] = 0;
2289*45916cd2Sjpk 		/*
2290*45916cd2Sjpk 		 * Set the Length of the hop-by-hop header, number of 8
2291*45916cd2Sjpk 		 * byte-words following the 1st 8 bytes
2292*45916cd2Sjpk 		 */
2293*45916cd2Sjpk 		optbuf[1] = (reservelen + inlen - 1) >> 3;
2294*45916cd2Sjpk 	}
2295*45916cd2Sjpk 	*optbufp = optbuf;
2296*45916cd2Sjpk 	*optlenp = inlen + reservelen;
2297*45916cd2Sjpk 	return (0);
2298*45916cd2Sjpk }
2299