xref: /illumos-gate/usr/src/uts/common/inet/optcom.c (revision bd670b35)
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
545916cd2Sjpk  * Common Development and Distribution License (the "License").
645916cd2Sjpk  * 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 /*
22de8c4a14SErik Nordmark  * Copyright 2009 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 /*
287c478bd9Sstevel@tonic-gate  * This file contains common code for handling Options Management requests.
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/types.h>
327c478bd9Sstevel@tonic-gate #include <sys/stream.h>
337c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
347c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
357c478bd9Sstevel@tonic-gate #include <sys/errno.h>
367c478bd9Sstevel@tonic-gate #define	_SUN_TPI_VERSION 2
377c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
387c478bd9Sstevel@tonic-gate #include <sys/socket.h>
390f1702c5SYu Xiangning #include <sys/socketvar.h>
407c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
417c478bd9Sstevel@tonic-gate #include <sys/debug.h>		/* for ASSERT */
427c478bd9Sstevel@tonic-gate #include <sys/policy.h>
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate #include <inet/common.h>
457c478bd9Sstevel@tonic-gate #include <inet/mi.h>
467c478bd9Sstevel@tonic-gate #include <inet/nd.h>
477c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
487c478bd9Sstevel@tonic-gate #include <inet/ip.h>
497c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
507c478bd9Sstevel@tonic-gate #include <netinet/in.h>
517c478bd9Sstevel@tonic-gate #include "optcom.h"
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate #include <inet/optcom.h>
540f1702c5SYu Xiangning #include <inet/ipclassifier.h>
550f1702c5SYu Xiangning #include <inet/proto_set.h>
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate  * Function prototypes
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate static t_scalar_t process_topthdrs_first_pass(mblk_t *, cred_t *, optdb_obj_t *,
61*bd670b35SErik Nordmark     size_t *);
627c478bd9Sstevel@tonic-gate static t_scalar_t do_options_second_pass(queue_t *q, mblk_t *reqmp,
637c478bd9Sstevel@tonic-gate     mblk_t *ack_mp, cred_t *, optdb_obj_t *dbobjp,
64*bd670b35SErik Nordmark     t_uscalar_t *worst_statusp);
657c478bd9Sstevel@tonic-gate static t_uscalar_t get_worst_status(t_uscalar_t, t_uscalar_t);
667c478bd9Sstevel@tonic-gate static int do_opt_default(queue_t *, struct T_opthdr *, uchar_t **,
677c478bd9Sstevel@tonic-gate     t_uscalar_t *, cred_t *, optdb_obj_t *);
687c478bd9Sstevel@tonic-gate static void do_opt_current(queue_t *, struct T_opthdr *, uchar_t **,
697c478bd9Sstevel@tonic-gate     t_uscalar_t *, cred_t *cr, optdb_obj_t *);
70*bd670b35SErik Nordmark static void do_opt_check_or_negotiate(queue_t *q, struct T_opthdr *reqopt,
717c478bd9Sstevel@tonic-gate     uint_t optset_context, uchar_t **resptrp, t_uscalar_t *worst_statusp,
72*bd670b35SErik Nordmark     cred_t *, optdb_obj_t *dbobjp);
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);
75*bd670b35SErik Nordmark static boolean_t opt_length_ok(opdes_t *, t_uscalar_t optlen);
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
optcom_err_ack(queue_t * q,mblk_t * mp,t_scalar_t t_error,int sys_error)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  */
180*bd670b35SErik Nordmark void
svr4_optcom_req(queue_t * q,mblk_t * mp,cred_t * cr,optdb_obj_t * dbobjp)181*bd670b35SErik Nordmark svr4_optcom_req(queue_t *q, mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp)
1827c478bd9Sstevel@tonic-gate {
1837c478bd9Sstevel@tonic-gate 	pfi_t	deffn = dbobjp->odb_deffn;
1847c478bd9Sstevel@tonic-gate 	pfi_t	getfn = dbobjp->odb_getfn;
1857c478bd9Sstevel@tonic-gate 	opt_set_fn setfn = dbobjp->odb_setfn;
1867c478bd9Sstevel@tonic-gate 	opdes_t	*opt_arr = dbobjp->odb_opt_des_arr;
1877c478bd9Sstevel@tonic-gate 	uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
1887c478bd9Sstevel@tonic-gate 	t_uscalar_t max_optbuf_len;
1897c478bd9Sstevel@tonic-gate 	int len;
1907c478bd9Sstevel@tonic-gate 	mblk_t	*mp1 = NULL;
1917c478bd9Sstevel@tonic-gate 	struct opthdr *next_opt;
1927c478bd9Sstevel@tonic-gate 	struct opthdr *opt;
1937c478bd9Sstevel@tonic-gate 	struct opthdr *opt1;
1947c478bd9Sstevel@tonic-gate 	struct opthdr *opt_end;
1957c478bd9Sstevel@tonic-gate 	struct opthdr *opt_start;
1967c478bd9Sstevel@tonic-gate 	opdes_t	*optd;
1977c478bd9Sstevel@tonic-gate 	struct T_optmgmt_ack *toa;
1987c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor;
1990f1702c5SYu Xiangning 	int error;
2007c478bd9Sstevel@tonic-gate 
2017c478bd9Sstevel@tonic-gate 	tor = (struct T_optmgmt_req *)mp->b_rptr;
2027c478bd9Sstevel@tonic-gate 	/* Verify message integrity. */
2037c478bd9Sstevel@tonic-gate 	if (mp->b_wptr - mp->b_rptr < sizeof (struct T_optmgmt_req))
2047c478bd9Sstevel@tonic-gate 		goto bad_opt;
2057c478bd9Sstevel@tonic-gate 	/* Verify MGMT_flags legal */
2067c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
2077c478bd9Sstevel@tonic-gate 	case T_DEFAULT:
2087c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
2097c478bd9Sstevel@tonic-gate 	case T_CURRENT:
2107c478bd9Sstevel@tonic-gate 	case T_CHECK:
2117c478bd9Sstevel@tonic-gate 		/* OK - legal request flags */
2127c478bd9Sstevel@tonic-gate 		break;
2137c478bd9Sstevel@tonic-gate 	default:
2147c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
215*bd670b35SErik Nordmark 		return;
2167c478bd9Sstevel@tonic-gate 	}
2177c478bd9Sstevel@tonic-gate 	if (tor->MGMT_flags == T_DEFAULT) {
2187c478bd9Sstevel@tonic-gate 		/* Is it a request for default option settings? */
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 		/*
2217c478bd9Sstevel@tonic-gate 		 * Note: XXX TLI and TPI specification was unclear about
2227c478bd9Sstevel@tonic-gate 		 * semantics of T_DEFAULT and the following historical note
2237c478bd9Sstevel@tonic-gate 		 * and its interpretation is incorrect (it implies a request
2247c478bd9Sstevel@tonic-gate 		 * for default values of only the identified options not all.
2257c478bd9Sstevel@tonic-gate 		 * The semantics have been explained better in XTI spec.)
2267c478bd9Sstevel@tonic-gate 		 * However, we do not modify (comment or code) here to keep
2277c478bd9Sstevel@tonic-gate 		 * compatibility.
2287c478bd9Sstevel@tonic-gate 		 * We can rethink this if it ever becomes an issue.
2297c478bd9Sstevel@tonic-gate 		 * ----historical comment start------
2307c478bd9Sstevel@tonic-gate 		 * As we understand it, the input buffer is meaningless
2317c478bd9Sstevel@tonic-gate 		 * so we ditch the message.  A T_DEFAULT request is a
2327c478bd9Sstevel@tonic-gate 		 * request to obtain a buffer containing defaults for
2337c478bd9Sstevel@tonic-gate 		 * all supported options, so we allocate a maximum length
2347c478bd9Sstevel@tonic-gate 		 * reply.
2357c478bd9Sstevel@tonic-gate 		 * ----historical comment end -------
2367c478bd9Sstevel@tonic-gate 		 */
2377c478bd9Sstevel@tonic-gate 		/* T_DEFAULT not passed down */
2387c478bd9Sstevel@tonic-gate 		freemsg(mp);
2397c478bd9Sstevel@tonic-gate 		max_optbuf_len = optcom_max_optbuf_len(opt_arr,
2407c478bd9Sstevel@tonic-gate 		    opt_arr_cnt);
2417c478bd9Sstevel@tonic-gate 		mp = allocb(max_optbuf_len, BPRI_MED);
2427c478bd9Sstevel@tonic-gate 		if (!mp) {
2437c478bd9Sstevel@tonic-gate no_mem:;
2447c478bd9Sstevel@tonic-gate 			optcom_err_ack(q, mp, TSYSERR, ENOMEM);
245*bd670b35SErik Nordmark 			return;
2467c478bd9Sstevel@tonic-gate 		}
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 		/* Initialize the T_optmgmt_ack header. */
2497c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)mp->b_rptr;
2507c478bd9Sstevel@tonic-gate 		bzero((char *)toa, max_optbuf_len);
2517c478bd9Sstevel@tonic-gate 		toa->PRIM_type = T_OPTMGMT_ACK;
2527c478bd9Sstevel@tonic-gate 		toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
2537c478bd9Sstevel@tonic-gate 		/* TODO: Is T_DEFAULT the right thing to put in MGMT_flags? */
2547c478bd9Sstevel@tonic-gate 		toa->MGMT_flags = T_DEFAULT;
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 		/* Now walk the table of options passed in */
2577c478bd9Sstevel@tonic-gate 		opt = (struct opthdr *)&toa[1];
2587c478bd9Sstevel@tonic-gate 		for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
2597c478bd9Sstevel@tonic-gate 			/*
2607c478bd9Sstevel@tonic-gate 			 * All the options in the table of options passed
2617c478bd9Sstevel@tonic-gate 			 * in are by definition supported by the protocol
2627c478bd9Sstevel@tonic-gate 			 * calling this function.
2637c478bd9Sstevel@tonic-gate 			 */
2647c478bd9Sstevel@tonic-gate 			if (!OA_READ_PERMISSION(optd, cr))
2657c478bd9Sstevel@tonic-gate 				continue;
2667c478bd9Sstevel@tonic-gate 			opt->level = optd->opdes_level;
2677c478bd9Sstevel@tonic-gate 			opt->name = optd->opdes_name;
2687c478bd9Sstevel@tonic-gate 			if (!(optd->opdes_props & OP_DEF_FN) ||
2697c478bd9Sstevel@tonic-gate 			    ((len = (*deffn)(q, opt->level,
270fc80c0dfSnordmark 			    opt->name, (uchar_t *)&opt[1])) < 0)) {
2717c478bd9Sstevel@tonic-gate 				/*
2727c478bd9Sstevel@tonic-gate 				 * Fill length and value from table.
2737c478bd9Sstevel@tonic-gate 				 *
2747c478bd9Sstevel@tonic-gate 				 * Default value not instantiated from function
2757c478bd9Sstevel@tonic-gate 				 * (or the protocol specific function failed it;
2767c478bd9Sstevel@tonic-gate 				 * In this interpretation of T_DEFAULT, this is
2777c478bd9Sstevel@tonic-gate 				 * the best we can do)
2787c478bd9Sstevel@tonic-gate 				 */
2797c478bd9Sstevel@tonic-gate 				switch (optd->opdes_size) {
2807c478bd9Sstevel@tonic-gate 				/*
2817c478bd9Sstevel@tonic-gate 				 * Since options are guaranteed aligned only
2827c478bd9Sstevel@tonic-gate 				 * on a 4 byte boundary (t_scalar_t) any
2837c478bd9Sstevel@tonic-gate 				 * option that is greater in size will default
2847c478bd9Sstevel@tonic-gate 				 * to the bcopy below
2857c478bd9Sstevel@tonic-gate 				 */
2867c478bd9Sstevel@tonic-gate 				case sizeof (int32_t):
2877c478bd9Sstevel@tonic-gate 					*(int32_t *)&opt[1] =
2887c478bd9Sstevel@tonic-gate 					    (int32_t)optd->opdes_default;
2897c478bd9Sstevel@tonic-gate 					break;
2907c478bd9Sstevel@tonic-gate 				case sizeof (int16_t):
2917c478bd9Sstevel@tonic-gate 					*(int16_t *)&opt[1] =
2927c478bd9Sstevel@tonic-gate 					    (int16_t)optd->opdes_default;
2937c478bd9Sstevel@tonic-gate 					break;
2947c478bd9Sstevel@tonic-gate 				case sizeof (int8_t):
2957c478bd9Sstevel@tonic-gate 					*(int8_t *)&opt[1] =
2967c478bd9Sstevel@tonic-gate 					    (int8_t)optd->opdes_default;
2977c478bd9Sstevel@tonic-gate 					break;
2987c478bd9Sstevel@tonic-gate 				default:
2997c478bd9Sstevel@tonic-gate 					/*
3007c478bd9Sstevel@tonic-gate 					 * other length but still assume
3017c478bd9Sstevel@tonic-gate 					 * fixed - use bcopy
3027c478bd9Sstevel@tonic-gate 					 */
3037c478bd9Sstevel@tonic-gate 					bcopy(optd->opdes_defbuf,
3047c478bd9Sstevel@tonic-gate 					    &opt[1], optd->opdes_size);
3057c478bd9Sstevel@tonic-gate 					break;
3067c478bd9Sstevel@tonic-gate 				}
3077c478bd9Sstevel@tonic-gate 				opt->len = optd->opdes_size;
3087c478bd9Sstevel@tonic-gate 			}
3097c478bd9Sstevel@tonic-gate 			else
3107c478bd9Sstevel@tonic-gate 				opt->len = (t_uscalar_t)len;
3117c478bd9Sstevel@tonic-gate 			opt = (struct opthdr *)((char *)&opt[1] +
3127c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt->len));
3137c478bd9Sstevel@tonic-gate 		}
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 		/* Now record the final length. */
3167c478bd9Sstevel@tonic-gate 		toa->OPT_length = (t_scalar_t)((char *)opt - (char *)&toa[1]);
3177c478bd9Sstevel@tonic-gate 		mp->b_wptr = (uchar_t *)opt;
3187c478bd9Sstevel@tonic-gate 		mp->b_datap->db_type = M_PCPROTO;
3197c478bd9Sstevel@tonic-gate 		/* Ship it back. */
3207c478bd9Sstevel@tonic-gate 		qreply(q, mp);
321*bd670b35SErik Nordmark 		return;
3227c478bd9Sstevel@tonic-gate 	}
3237c478bd9Sstevel@tonic-gate 	/* T_DEFAULT processing complete - no more T_DEFAULT */
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	/*
3267c478bd9Sstevel@tonic-gate 	 * For T_NEGOTIATE, T_CURRENT, and T_CHECK requests, we make a
3277c478bd9Sstevel@tonic-gate 	 * pass through the input buffer validating the details and
3287c478bd9Sstevel@tonic-gate 	 * making sure each option is supported by the protocol.
3297c478bd9Sstevel@tonic-gate 	 */
3307c478bd9Sstevel@tonic-gate 	if ((opt_start = (struct opthdr *)mi_offset_param(mp,
3317c478bd9Sstevel@tonic-gate 	    tor->OPT_offset, tor->OPT_length)) == NULL)
3327c478bd9Sstevel@tonic-gate 		goto bad_opt;
3337c478bd9Sstevel@tonic-gate 	if (!__TPI_OPT_ISALIGNED(opt_start))
3347c478bd9Sstevel@tonic-gate 		goto bad_opt;
3357c478bd9Sstevel@tonic-gate 
3367c478bd9Sstevel@tonic-gate 	opt_end = (struct opthdr *)((uchar_t *)opt_start +
3377c478bd9Sstevel@tonic-gate 	    tor->OPT_length);
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	for (opt = opt_start; opt < opt_end; opt = next_opt) {
3407c478bd9Sstevel@tonic-gate 		/*
3417c478bd9Sstevel@tonic-gate 		 * Verify we have room to reference the option header
3427c478bd9Sstevel@tonic-gate 		 * fields in the option buffer.
3437c478bd9Sstevel@tonic-gate 		 */
3447c478bd9Sstevel@tonic-gate 		if ((uchar_t *)opt + sizeof (struct opthdr) >
3457c478bd9Sstevel@tonic-gate 		    (uchar_t *)opt_end)
3467c478bd9Sstevel@tonic-gate 			goto bad_opt;
3477c478bd9Sstevel@tonic-gate 		/*
3487c478bd9Sstevel@tonic-gate 		 * We now compute pointer to next option in buffer 'next_opt'
3497c478bd9Sstevel@tonic-gate 		 * The next_opt computation above below 'opt->len' initialized
3507c478bd9Sstevel@tonic-gate 		 * by application which cannot be trusted. The usual value
3517c478bd9Sstevel@tonic-gate 		 * too large will be captured by the loop termination condition
3527c478bd9Sstevel@tonic-gate 		 * above. We check for the following which it will miss.
3537c478bd9Sstevel@tonic-gate 		 * 	-pointer space wraparound arithmetic overflow
3547c478bd9Sstevel@tonic-gate 		 *	-last option in buffer with 'opt->len' being too large
3557c478bd9Sstevel@tonic-gate 		 *	 (only reason 'next_opt' should equal or exceed
3567c478bd9Sstevel@tonic-gate 		 *	 'opt_end' for last option is roundup unless length is
3577c478bd9Sstevel@tonic-gate 		 *	 too-large/invalid)
3587c478bd9Sstevel@tonic-gate 		 */
3597c478bd9Sstevel@tonic-gate 		next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
3607c478bd9Sstevel@tonic-gate 		    _TPI_ALIGN_OPT(opt->len));
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 		if ((uchar_t *)next_opt < (uchar_t *)&opt[1] ||
3637c478bd9Sstevel@tonic-gate 		    ((next_opt >= opt_end) &&
364fc80c0dfSnordmark 		    (((uchar_t *)next_opt - (uchar_t *)opt_end) >=
365fc80c0dfSnordmark 		    __TPI_ALIGN_SIZE)))
3667c478bd9Sstevel@tonic-gate 			goto bad_opt;
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 		/* sanity check */
3697c478bd9Sstevel@tonic-gate 		if (opt->name == T_ALLOPT)
3707c478bd9Sstevel@tonic-gate 			goto bad_opt;
3717c478bd9Sstevel@tonic-gate 
3720f1702c5SYu Xiangning 		error = proto_opt_check(opt->level, opt->name, opt->len, NULL,
373*bd670b35SErik Nordmark 		    opt_arr, opt_arr_cnt,
3740f1702c5SYu Xiangning 		    tor->MGMT_flags == T_NEGOTIATE, tor->MGMT_flags == T_CHECK,
3750f1702c5SYu Xiangning 		    cr);
3760f1702c5SYu Xiangning 		if (error < 0) {
3770f1702c5SYu Xiangning 			optcom_err_ack(q, mp, -error, 0);
378*bd670b35SErik Nordmark 			return;
3790f1702c5SYu Xiangning 		} else if (error > 0) {
3800f1702c5SYu Xiangning 			optcom_err_ack(q, mp, TSYSERR, error);
381*bd670b35SErik Nordmark 			return;
3827c478bd9Sstevel@tonic-gate 		}
3837c478bd9Sstevel@tonic-gate 	} /* end for loop scanning option buffer */
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 	/* Now complete the operation as required. */
3867c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
3877c478bd9Sstevel@tonic-gate 	case T_CHECK:
3887c478bd9Sstevel@tonic-gate 		/*
3897c478bd9Sstevel@tonic-gate 		 * Historically used same as T_CURRENT (which was added to
3907c478bd9Sstevel@tonic-gate 		 * standard later). Code retained for compatibility.
3917c478bd9Sstevel@tonic-gate 		 */
3927c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
3937c478bd9Sstevel@tonic-gate 	case T_CURRENT:
3947c478bd9Sstevel@tonic-gate 		/*
3957c478bd9Sstevel@tonic-gate 		 * Allocate a maximum size reply.  Perhaps we are supposed to
3967c478bd9Sstevel@tonic-gate 		 * assume that the input buffer includes space for the answers
3977c478bd9Sstevel@tonic-gate 		 * as well as the opthdrs, but we don't know that for sure.
3987c478bd9Sstevel@tonic-gate 		 * So, instead, we create a new output buffer, using the
3997c478bd9Sstevel@tonic-gate 		 * input buffer only as a list of options.
4007c478bd9Sstevel@tonic-gate 		 */
4017c478bd9Sstevel@tonic-gate 		max_optbuf_len = optcom_max_optbuf_len(opt_arr,
4027c478bd9Sstevel@tonic-gate 		    opt_arr_cnt);
403de8c4a14SErik Nordmark 		mp1 = allocb_tmpl(max_optbuf_len, mp);
4047c478bd9Sstevel@tonic-gate 		if (!mp1)
4057c478bd9Sstevel@tonic-gate 			goto no_mem;
4067c478bd9Sstevel@tonic-gate 		/* Initialize the header. */
4077c478bd9Sstevel@tonic-gate 		mp1->b_datap->db_type = M_PCPROTO;
4087c478bd9Sstevel@tonic-gate 		mp1->b_wptr = &mp1->b_rptr[sizeof (struct T_optmgmt_ack)];
4097c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)mp1->b_rptr;
4107c478bd9Sstevel@tonic-gate 		toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
4117c478bd9Sstevel@tonic-gate 		toa->MGMT_flags = tor->MGMT_flags;
4127c478bd9Sstevel@tonic-gate 		/*
4137c478bd9Sstevel@tonic-gate 		 * Walk through the input buffer again, this time adding
4147c478bd9Sstevel@tonic-gate 		 * entries to the output buffer for each option requested.
4157c478bd9Sstevel@tonic-gate 		 * Note, sanity of option header, last option etc, verified
4167c478bd9Sstevel@tonic-gate 		 * in first pass.
4177c478bd9Sstevel@tonic-gate 		 */
4187c478bd9Sstevel@tonic-gate 		opt1 = (struct opthdr *)&toa[1];
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 		for (opt = opt_start; opt < opt_end; opt = next_opt) {
4217c478bd9Sstevel@tonic-gate 
422fc80c0dfSnordmark 			next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
423fc80c0dfSnordmark 			    _TPI_ALIGN_OPT(opt->len));
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 			opt1->name = opt->name;
4267c478bd9Sstevel@tonic-gate 			opt1->level = opt->level;
4277c478bd9Sstevel@tonic-gate 			len = (*getfn)(q, opt->level,
4287c478bd9Sstevel@tonic-gate 			    opt->name, (uchar_t *)&opt1[1]);
4297c478bd9Sstevel@tonic-gate 			/*
4307c478bd9Sstevel@tonic-gate 			 * Failure means option is not recognized. Copy input
4317c478bd9Sstevel@tonic-gate 			 * buffer as is
4327c478bd9Sstevel@tonic-gate 			 */
4337c478bd9Sstevel@tonic-gate 			if (len < 0) {
4347c478bd9Sstevel@tonic-gate 				opt1->len = opt->len;
4357c478bd9Sstevel@tonic-gate 				bcopy(&opt[1], &opt1[1], opt->len);
436ff550d0eSmasputra 			} else {
4377c478bd9Sstevel@tonic-gate 				opt1->len = (t_uscalar_t)len;
438ff550d0eSmasputra 			}
4397c478bd9Sstevel@tonic-gate 			opt1 = (struct opthdr *)((uchar_t *)&opt1[1] +
4407c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt1->len));
4417c478bd9Sstevel@tonic-gate 		} /* end for loop */
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 		/* Record the final length. */
4447c478bd9Sstevel@tonic-gate 		toa->OPT_length = (t_scalar_t)((uchar_t *)opt1 -
4457c478bd9Sstevel@tonic-gate 		    (uchar_t *)&toa[1]);
4467c478bd9Sstevel@tonic-gate 		mp1->b_wptr = (uchar_t *)opt1;
4477c478bd9Sstevel@tonic-gate 		/* Ditch the input buffer. */
4487c478bd9Sstevel@tonic-gate 		freemsg(mp);
4497c478bd9Sstevel@tonic-gate 		mp = mp1;
4507c478bd9Sstevel@tonic-gate 		break;
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
4537c478bd9Sstevel@tonic-gate 		/*
4547c478bd9Sstevel@tonic-gate 		 * Here we are expecting that the response buffer is exactly
4557c478bd9Sstevel@tonic-gate 		 * the same size as the input buffer.  We pass each opthdr
4567c478bd9Sstevel@tonic-gate 		 * to the protocol's set function.  If the protocol doesn't
4577c478bd9Sstevel@tonic-gate 		 * like it, it can update the value in it return argument.
4587c478bd9Sstevel@tonic-gate 		 */
4597c478bd9Sstevel@tonic-gate 		/*
4607c478bd9Sstevel@tonic-gate 		 * Pass each negotiated option through the protocol set
4617c478bd9Sstevel@tonic-gate 		 * function.
4627c478bd9Sstevel@tonic-gate 		 * Note: sanity check on option header values done in first
4637c478bd9Sstevel@tonic-gate 		 * pass and not repeated here.
4647c478bd9Sstevel@tonic-gate 		 */
4657c478bd9Sstevel@tonic-gate 		toa = (struct T_optmgmt_ack *)tor;
4667c478bd9Sstevel@tonic-gate 
467*bd670b35SErik Nordmark 		for (opt = opt_start; opt < opt_end; opt = next_opt) {
4687c478bd9Sstevel@tonic-gate 			int error;
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 			next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
4717c478bd9Sstevel@tonic-gate 			    _TPI_ALIGN_OPT(opt->len));
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 			error = (*setfn)(q, SETFN_OPTCOM_NEGOTIATE,
4747c478bd9Sstevel@tonic-gate 			    opt->level, opt->name,
4757c478bd9Sstevel@tonic-gate 			    opt->len, (uchar_t *)&opt[1],
476*bd670b35SErik Nordmark 			    &opt->len, (uchar_t *)&opt[1], NULL, cr);
4777c478bd9Sstevel@tonic-gate 			/*
4787c478bd9Sstevel@tonic-gate 			 * Treat positive "errors" as real.
4797c478bd9Sstevel@tonic-gate 			 * Note: negative errors are to be treated as
4807c478bd9Sstevel@tonic-gate 			 * non-fatal by svr4_optcom_req() and are
4817c478bd9Sstevel@tonic-gate 			 * returned by setfn() when it is passed an
4827c478bd9Sstevel@tonic-gate 			 * option it does not handle. Since the option
4830f1702c5SYu Xiangning 			 * passed proto_opt_lookup(), it is implied that
4847c478bd9Sstevel@tonic-gate 			 * it is valid but was either handled upstream
4857c478bd9Sstevel@tonic-gate 			 * or will be handled downstream.
4867c478bd9Sstevel@tonic-gate 			 */
487*bd670b35SErik Nordmark 			if (error > 0) {
4887c478bd9Sstevel@tonic-gate 				optcom_err_ack(q, mp, TSYSERR, error);
489*bd670b35SErik Nordmark 				return;
4907c478bd9Sstevel@tonic-gate 			}
491fc80c0dfSnordmark 			/*
492fc80c0dfSnordmark 			 * error < 0 means option is not recognized.
493fc80c0dfSnordmark 			 */
4947c478bd9Sstevel@tonic-gate 		}
4957c478bd9Sstevel@tonic-gate 		break;
4967c478bd9Sstevel@tonic-gate 	default:
4977c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
498*bd670b35SErik Nordmark 		return;
4997c478bd9Sstevel@tonic-gate 	}
5007c478bd9Sstevel@tonic-gate 
501*bd670b35SErik Nordmark 	/* Set common fields in the header. */
502*bd670b35SErik Nordmark 	toa->MGMT_flags = T_SUCCESS;
503*bd670b35SErik Nordmark 	mp->b_datap->db_type = M_PCPROTO;
504*bd670b35SErik Nordmark 	toa->PRIM_type = T_OPTMGMT_ACK;
505*bd670b35SErik Nordmark 	qreply(q, mp);
506*bd670b35SErik Nordmark 	return;
5077c478bd9Sstevel@tonic-gate bad_opt:;
5087c478bd9Sstevel@tonic-gate 	optcom_err_ack(q, mp, TBADOPT, 0);
5097c478bd9Sstevel@tonic-gate }
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate /*
5127c478bd9Sstevel@tonic-gate  * New optcom_req inspired by TPI/XTI semantics
5137c478bd9Sstevel@tonic-gate  */
514*bd670b35SErik Nordmark void
tpi_optcom_req(queue_t * q,mblk_t * mp,cred_t * cr,optdb_obj_t * dbobjp)515*bd670b35SErik Nordmark tpi_optcom_req(queue_t *q, mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp)
5167c478bd9Sstevel@tonic-gate {
5177c478bd9Sstevel@tonic-gate 	t_scalar_t t_error;
5187c478bd9Sstevel@tonic-gate 	mblk_t *toa_mp;
5197c478bd9Sstevel@tonic-gate 	size_t toa_len;
5207c478bd9Sstevel@tonic-gate 	struct T_optmgmt_ack *toa;
5217c478bd9Sstevel@tonic-gate 	struct T_optmgmt_req *tor =
5227c478bd9Sstevel@tonic-gate 	    (struct T_optmgmt_req *)mp->b_rptr;
5237c478bd9Sstevel@tonic-gate 	t_uscalar_t worst_status;
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate 	/* Verify message integrity. */
5267c478bd9Sstevel@tonic-gate 	if ((mp->b_wptr - mp->b_rptr) < sizeof (struct T_optmgmt_req)) {
5277c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADOPT, 0);
528*bd670b35SErik Nordmark 		return;
5297c478bd9Sstevel@tonic-gate 	}
5307c478bd9Sstevel@tonic-gate 
5317c478bd9Sstevel@tonic-gate 	/* Verify MGMT_flags legal */
5327c478bd9Sstevel@tonic-gate 	switch (tor->MGMT_flags) {
5337c478bd9Sstevel@tonic-gate 	case T_DEFAULT:
5347c478bd9Sstevel@tonic-gate 	case T_NEGOTIATE:
5357c478bd9Sstevel@tonic-gate 	case T_CURRENT:
5367c478bd9Sstevel@tonic-gate 	case T_CHECK:
5377c478bd9Sstevel@tonic-gate 		/* OK - legal request flags */
5387c478bd9Sstevel@tonic-gate 		break;
5397c478bd9Sstevel@tonic-gate 	default:
5407c478bd9Sstevel@tonic-gate 		optcom_err_ack(q, mp, TBADFLAG, 0);
541*bd670b35SErik Nordmark 		return;
5427c478bd9Sstevel@tonic-gate 	}
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	/*
5457c478bd9Sstevel@tonic-gate 	 * In this design, there are two passes required on the input buffer
5467c478bd9Sstevel@tonic-gate 	 * mostly to accomodate variable length options and "T_ALLOPT" option
5477c478bd9Sstevel@tonic-gate 	 * which has the semantics "all options of the specified level".
5487c478bd9Sstevel@tonic-gate 	 *
5497c478bd9Sstevel@tonic-gate 	 * For T_DEFAULT, T_NEGOTIATE, T_CURRENT, and T_CHECK requests, we make
5507c478bd9Sstevel@tonic-gate 	 * a pass through the input buffer validating the details and making
5517c478bd9Sstevel@tonic-gate 	 * sure each option is supported by the protocol. We also determine the
5527c478bd9Sstevel@tonic-gate 	 * length of the option buffer to return. (Variable length options and
5537c478bd9Sstevel@tonic-gate 	 * T_ALLOPT mean that length can be different for output buffer).
5547c478bd9Sstevel@tonic-gate 	 */
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	toa_len = 0;		/* initial value */
5577c478bd9Sstevel@tonic-gate 
5587c478bd9Sstevel@tonic-gate 	/*
5597c478bd9Sstevel@tonic-gate 	 * First pass, we do the following
5607c478bd9Sstevel@tonic-gate 	 *	- estimate cumulative length needed for results
5617c478bd9Sstevel@tonic-gate 	 *	- set "status" field based on permissions, option header check
5627c478bd9Sstevel@tonic-gate 	 *	  etc.
5637c478bd9Sstevel@tonic-gate 	 */
5647c478bd9Sstevel@tonic-gate 	if ((t_error = process_topthdrs_first_pass(mp, cr, dbobjp,
565