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,
2707c478bd9Sstevel@tonic-gate 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) &&
3647c478bd9Sstevel@tonic-gate (((uchar_t *)next_opt - (uchar_t *)opt_end) >=
3657c478bd9Sstevel@tonic-gate __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
4227c478bd9Sstevel@tonic-gate next_opt = (struct opthdr *)((uchar_t *)&opt[1] +
4237c478bd9Sstevel@tonic-gate _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
5017c478bd9Sstevel@tonic-gate /* Set common fields in the header. */
5027c478bd9Sstevel@tonic-gate toa->MGMT_flags = T_SUCCESS;
5037c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_PCPROTO;
5047c478bd9Sstevel@tonic-gate toa->PRIM_type = T_OPTMGMT_ACK;
5057c478bd9Sstevel@tonic-gate 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*bd670b35SErik Nordmark &toa_len)) != 0) {
5667c478bd9Sstevel@tonic-gate optcom_err_ack(q, mp, t_error, 0);
567*bd670b35SErik Nordmark return;
5687c478bd9Sstevel@tonic-gate }
5697c478bd9Sstevel@tonic-gate
5707c478bd9Sstevel@tonic-gate /*
5717c478bd9Sstevel@tonic-gate * A validation phase of the input buffer is done. We have also
5727c478bd9Sstevel@tonic-gate * obtained the length requirement and and other details about the
5737c478bd9Sstevel@tonic-gate * input and we liked input buffer so far. We make another scan
5747c478bd9Sstevel@tonic-gate * through the input now and generate the output necessary to complete
5757c478bd9Sstevel@tonic-gate * the operation.
5767c478bd9Sstevel@tonic-gate */
5777c478bd9Sstevel@tonic-gate
578de8c4a14SErik Nordmark toa_mp = allocb_tmpl(toa_len, mp);
5797c478bd9Sstevel@tonic-gate if (!toa_mp) {
5807c478bd9Sstevel@tonic-gate optcom_err_ack(q, mp, TSYSERR, ENOMEM);
581*bd670b35SErik Nordmark return;
5827c478bd9Sstevel@tonic-gate }
5837c478bd9Sstevel@tonic-gate
5847c478bd9Sstevel@tonic-gate /*
5857c478bd9Sstevel@tonic-gate * Set initial values for generating output.
5867c478bd9Sstevel@tonic-gate */
587*bd670b35SErik Nordmark worst_status = T_SUCCESS; /* initial value */
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate /*
5907c478bd9Sstevel@tonic-gate * This routine makes another pass through the option buffer this
5917c478bd9Sstevel@tonic-gate * time acting on the request based on "status" result in the
5927c478bd9Sstevel@tonic-gate * first pass. It also performs "expansion" of T_ALLOPT into
5937c478bd9Sstevel@tonic-gate * all options of a certain level and acts on each for this request.
5947c478bd9Sstevel@tonic-gate */
5957c478bd9Sstevel@tonic-gate if ((t_error = do_options_second_pass(q, mp, toa_mp, cr, dbobjp,
596*bd670b35SErik Nordmark &worst_status)) != 0) {
5977c478bd9Sstevel@tonic-gate freemsg(toa_mp);
5987c478bd9Sstevel@tonic-gate optcom_err_ack(q, mp, t_error, 0);
599*bd670b35SErik Nordmark return;
6007c478bd9Sstevel@tonic-gate }
6017c478bd9Sstevel@tonic-gate
6027c478bd9Sstevel@tonic-gate /*
6037c478bd9Sstevel@tonic-gate * Following code relies on the coincidence that T_optmgmt_req
6047c478bd9Sstevel@tonic-gate * and T_optmgmt_ack are identical in binary representation
6057c478bd9Sstevel@tonic-gate */
6067c478bd9Sstevel@tonic-gate toa = (struct T_optmgmt_ack *)toa_mp->b_rptr;
6077c478bd9Sstevel@tonic-gate toa->OPT_length = (t_scalar_t)(toa_mp->b_wptr - (toa_mp->b_rptr +
6087c478bd9Sstevel@tonic-gate sizeof (struct T_optmgmt_ack)));
6097c478bd9Sstevel@tonic-gate toa->OPT_offset = (t_scalar_t)sizeof (struct T_optmgmt_ack);
6107c478bd9Sstevel@tonic-gate
6117c478bd9Sstevel@tonic-gate toa->MGMT_flags = tor->MGMT_flags;
6127c478bd9Sstevel@tonic-gate
6137c478bd9Sstevel@tonic-gate freemsg(mp); /* free input mblk */
6147c478bd9Sstevel@tonic-gate
6157c478bd9Sstevel@tonic-gate toa->PRIM_type = T_OPTMGMT_ACK;
6167c478bd9Sstevel@tonic-gate toa_mp->b_datap->db_type = M_PCPROTO;
6177c478bd9Sstevel@tonic-gate toa->MGMT_flags |= worst_status; /* XXX "worst" or "OR" TPI ? */
6187c478bd9Sstevel@tonic-gate qreply(q, toa_mp);
6197c478bd9Sstevel@tonic-gate }
6207c478bd9Sstevel@tonic-gate
6217c478bd9Sstevel@tonic-gate
6227c478bd9Sstevel@tonic-gate /*
6237c478bd9Sstevel@tonic-gate * Following routine makes a pass through option buffer in mp and performs the
6247c478bd9Sstevel@tonic-gate * following tasks.
6257c478bd9Sstevel@tonic-gate * - estimate cumulative length needed for results
6267c478bd9Sstevel@tonic-gate * - set "status" field based on permissions, option header check
6277c478bd9Sstevel@tonic-gate * etc.
6287c478bd9Sstevel@tonic-gate */
6297c478bd9Sstevel@tonic-gate
6307c478bd9Sstevel@tonic-gate static t_scalar_t
process_topthdrs_first_pass(mblk_t * mp,cred_t * cr,optdb_obj_t * dbobjp,size_t * toa_lenp)6317c478bd9Sstevel@tonic-gate process_topthdrs_first_pass(mblk_t *mp, cred_t *cr, optdb_obj_t *dbobjp,
632*bd670b35SErik Nordmark size_t *toa_lenp)
6337c478bd9Sstevel@tonic-gate {
6347c478bd9Sstevel@tonic-gate opdes_t *opt_arr = dbobjp->odb_opt_des_arr;
6357c478bd9Sstevel@tonic-gate uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
6367c478bd9Sstevel@tonic-gate optlevel_t *valid_level_arr = dbobjp->odb_valid_levels_arr;
6377c478bd9Sstevel@tonic-gate uint_t valid_level_arr_cnt = dbobjp->odb_valid_levels_arr_cnt;
6387c478bd9Sstevel@tonic-gate struct T_opthdr *opt;
6397c478bd9Sstevel@tonic-gate struct T_opthdr *opt_start, *opt_end;
6407c478bd9Sstevel@tonic-gate opdes_t *optd;
6417c478bd9Sstevel@tonic-gate size_t allopt_len;
6427c478bd9Sstevel@tonic-gate struct T_optmgmt_req *tor =
6437c478bd9Sstevel@tonic-gate (struct T_optmgmt_req *)mp->b_rptr;
6447c478bd9Sstevel@tonic-gate
6457c478bd9Sstevel@tonic-gate *toa_lenp = sizeof (struct T_optmgmt_ack); /* initial value */
6467c478bd9Sstevel@tonic-gate
6477c478bd9Sstevel@tonic-gate if ((opt_start = (struct T_opthdr *)
6487c478bd9Sstevel@tonic-gate mi_offset_param(mp, tor->OPT_offset, tor->OPT_length)) == NULL) {
6497c478bd9Sstevel@tonic-gate return (TBADOPT);
6507c478bd9Sstevel@tonic-gate }
6517c478bd9Sstevel@tonic-gate if (!__TPI_TOPT_ISALIGNED(opt_start))
6527c478bd9Sstevel@tonic-gate return (TBADOPT);
6537c478bd9Sstevel@tonic-gate
6547c478bd9Sstevel@tonic-gate opt_end = (struct T_opthdr *)((uchar_t *)opt_start + tor->OPT_length);
6557c478bd9Sstevel@tonic-gate
6567c478bd9Sstevel@tonic-gate for (opt = opt_start; opt && (opt < opt_end);
6577c478bd9Sstevel@tonic-gate opt = _TPI_TOPT_NEXTHDR(opt_start, tor->OPT_length, opt)) {
6587c478bd9Sstevel@tonic-gate /*
6597c478bd9Sstevel@tonic-gate * Validate the option for length and alignment
6607c478bd9Sstevel@tonic-gate * before accessing anything in it.
6617c478bd9Sstevel@tonic-gate */
6627c478bd9Sstevel@tonic-gate if (!(_TPI_TOPT_VALID(opt, opt_start, opt_end)))
6637c478bd9Sstevel@tonic-gate return (TBADOPT);
6647c478bd9Sstevel@tonic-gate
6657c478bd9Sstevel@tonic-gate /* Find the option in the opt_arr. */
6667c478bd9Sstevel@tonic-gate if (opt->name != T_ALLOPT) {
6670f1702c5SYu Xiangning optd = proto_opt_lookup(opt->level, opt->name,
6687c478bd9Sstevel@tonic-gate opt_arr, opt_arr_cnt);
6697c478bd9Sstevel@tonic-gate if (optd == NULL) {
6707c478bd9Sstevel@tonic-gate /*
6717c478bd9Sstevel@tonic-gate * Option not found
6727c478bd9Sstevel@tonic-gate *
6737c478bd9Sstevel@tonic-gate * Verify if level is "valid" or not.
6747c478bd9Sstevel@tonic-gate * Note: This check is required by XTI
6757c478bd9Sstevel@tonic-gate *
6767c478bd9Sstevel@tonic-gate * TPI provider always initializes
6777c478bd9Sstevel@tonic-gate * the "not supported" (or whatever) status
6787c478bd9Sstevel@tonic-gate * for the options. Other levels leave status
6797c478bd9Sstevel@tonic-gate * unchanged if they do not understand an
6807c478bd9Sstevel@tonic-gate * option.
6817c478bd9Sstevel@tonic-gate */
6827c478bd9Sstevel@tonic-gate if (!opt_level_valid(opt->level,
683*bd670b35SErik Nordmark valid_level_arr, valid_level_arr_cnt))
6847c478bd9Sstevel@tonic-gate return (TBADOPT);
6857c478bd9Sstevel@tonic-gate /*
6867c478bd9Sstevel@tonic-gate * level is valid - initialize
6877c478bd9Sstevel@tonic-gate * option as not supported
6887c478bd9Sstevel@tonic-gate */
6897c478bd9Sstevel@tonic-gate opt->status = T_NOTSUPPORT;
6907c478bd9Sstevel@tonic-gate *toa_lenp += _TPI_ALIGN_TOPT(opt->len);
6917c478bd9Sstevel@tonic-gate continue;
6927c478bd9Sstevel@tonic-gate }
6937c478bd9Sstevel@tonic-gate } else {
6947c478bd9Sstevel@tonic-gate /*
6957c478bd9Sstevel@tonic-gate * Handle T_ALLOPT case as a special case.
6967c478bd9Sstevel@tonic-gate * Note: T_ALLOPT does not mean anything
6977c478bd9Sstevel@tonic-gate * for T_CHECK operation.
6987c478bd9Sstevel@tonic-gate */
6997c478bd9Sstevel@tonic-gate allopt_len = 0;
7007c478bd9Sstevel@tonic-gate if (tor->MGMT_flags == T_CHECK ||
7017c478bd9Sstevel@tonic-gate ((allopt_len = opt_level_allopts_lengths(opt->level,
7027c478bd9Sstevel@tonic-gate opt_arr, opt_arr_cnt)) == 0)) {
7037c478bd9Sstevel@tonic-gate /*
7047c478bd9Sstevel@tonic-gate * This is confusing but correct !
7057c478bd9Sstevel@tonic-gate * It is not valid to to use T_ALLOPT with
7067c478bd9Sstevel@tonic-gate * T_CHECK flag.
7077c478bd9Sstevel@tonic-gate *
7087c478bd9Sstevel@tonic-gate * opt_level_allopts_lengths() is used to verify
7097c478bd9Sstevel@tonic-gate * that "level" associated with the T_ALLOPT is
7107c478bd9Sstevel@tonic-gate * supported.
7117c478bd9Sstevel@tonic-gate *
7127c478bd9Sstevel@tonic-gate */
7137c478bd9Sstevel@tonic-gate opt->status = T_FAILURE;
7147c478bd9Sstevel@tonic-gate *toa_lenp += _TPI_ALIGN_TOPT(opt->len);
7157c478bd9Sstevel@tonic-gate continue;
7167c478bd9Sstevel@tonic-gate }
7177c478bd9Sstevel@tonic-gate ASSERT(allopt_len != 0); /* remove ? */
7187c478bd9Sstevel@tonic-gate
7197c478bd9Sstevel@tonic-gate *toa_lenp += allopt_len;
7207c478bd9Sstevel@tonic-gate opt->status = T_SUCCESS;
7217c478bd9Sstevel@tonic-gate continue;
7227c478bd9Sstevel@tonic-gate }
7237c478bd9Sstevel@tonic-gate
7247c478bd9Sstevel@tonic-gate /* Additional checks dependent on operation. */
7257c478bd9Sstevel@tonic-gate switch (tor->MGMT_flags) {
7267c478bd9Sstevel@tonic-gate case T_DEFAULT:
7277c478bd9Sstevel@tonic-gate case T_CURRENT:
7287c478bd9Sstevel@tonic-gate
7297c478bd9Sstevel@tonic-gate /*
7300f1702c5SYu Xiangning * The proto_opt_lookup() routine call above approved of
7317c478bd9Sstevel@tonic-gate * this option so we can work on the status for it
7327c478bd9Sstevel@tonic-gate * based on the permissions for the operation. (This
7337c478bd9Sstevel@tonic-gate * can override any status for it set at higher levels)
7347c478bd9Sstevel@tonic-gate * We assume this override is OK since chkfn at this
7357c478bd9Sstevel@tonic-gate * level approved of this option.
7367c478bd9Sstevel@tonic-gate *
7377c478bd9Sstevel@tonic-gate * T_CURRENT semantics:
7387c478bd9Sstevel@tonic-gate * The read access is required. Else option
7397c478bd9Sstevel@tonic-gate * status is T_NOTSUPPORT.
7407c478bd9Sstevel@tonic-gate *
7417c478bd9Sstevel@tonic-gate * T_DEFAULT semantics:
7427c478bd9Sstevel@tonic-gate * Note: specification is not clear on this but we
7437c478bd9Sstevel@tonic-gate * interpret T_DEFAULT semantics such that access to
7447c478bd9Sstevel@tonic-gate * read value is required for access even the default
7457c478bd9Sstevel@tonic-gate * value. Otherwise the option status is T_NOTSUPPORT.
7467c478bd9Sstevel@tonic-gate */
7477c478bd9Sstevel@tonic-gate if (!OA_READ_PERMISSION(optd, cr)) {
7487c478bd9Sstevel@tonic-gate opt->status = T_NOTSUPPORT;
7497c478bd9Sstevel@tonic-gate *toa_lenp += _TPI_ALIGN_TOPT(opt->len);
7507c478bd9Sstevel@tonic-gate /* skip to next */
7517c478bd9Sstevel@tonic-gate continue;
7527c478bd9Sstevel@tonic-gate }
7537c478bd9Sstevel@tonic-gate
7547c478bd9Sstevel@tonic-gate /*
7557c478bd9Sstevel@tonic-gate * T_DEFAULT/T_CURRENT semantics:
7567c478bd9Sstevel@tonic-gate * We know that read access is set. If no other access
7577c478bd9Sstevel@tonic-gate * is set, then status is T_READONLY.
7587c478bd9Sstevel@tonic-gate */
7597c478bd9Sstevel@tonic-gate if (OA_READONLY_PERMISSION(optd, cr))
7607c478bd9Sstevel@tonic-gate opt->status = T_READONLY;
7617c478bd9Sstevel@tonic-gate else
7627c478bd9Sstevel@tonic-gate opt->status = T_SUCCESS;
7637c478bd9Sstevel@tonic-gate /*
7647c478bd9Sstevel@tonic-gate * Option passes all checks. Make room for it in the
7657c478bd9Sstevel@tonic-gate * ack. Note: size stored in table does not include
7667c478bd9Sstevel@tonic-gate * space for option header.
7677c478bd9Sstevel@tonic-gate */
7687c478bd9Sstevel@tonic-gate *toa_lenp += sizeof (struct T_opthdr) +
7697c478bd9Sstevel@tonic-gate _TPI_ALIGN_TOPT(optd->opdes_size);
7707c478bd9Sstevel@tonic-gate break;
7717c478bd9Sstevel@tonic-gate
7727c478bd9Sstevel@tonic-gate case T_CHECK:
7737c478bd9Sstevel@tonic-gate case T_NEGOTIATE:
7747c478bd9Sstevel@tonic-gate
7757c478bd9Sstevel@tonic-gate /*
7767c478bd9Sstevel@tonic-gate * T_NEGOTIATE semantics:
7777c478bd9Sstevel@tonic-gate * If for fixed length option value on input is not the
7787c478bd9Sstevel@tonic-gate * same as value supplied, then status is T_FAILURE.
7797c478bd9Sstevel@tonic-gate *
7807c478bd9Sstevel@tonic-gate * T_CHECK semantics:
7817c478bd9Sstevel@tonic-gate * If value is supplied, semantics same as T_NEGOTIATE.
7827c478bd9Sstevel@tonic-gate * It is however ok not to supply a value with T_CHECK.
7837c478bd9Sstevel@tonic-gate */
7847c478bd9Sstevel@tonic-gate
7857c478bd9Sstevel@tonic-gate if (tor->MGMT_flags == T_NEGOTIATE ||
7867c478bd9Sstevel@tonic-gate (opt->len != sizeof (struct T_opthdr))) {
7877c478bd9Sstevel@tonic-gate /*
7887c478bd9Sstevel@tonic-gate * Implies "value" is specified in T_CHECK or
7897c478bd9Sstevel@tonic-gate * it is a T_NEGOTIATE request.
7907c478bd9Sstevel@tonic-gate * Verify size.
7917c478bd9Sstevel@tonic-gate * Note: This can override anything about this
7927c478bd9Sstevel@tonic-gate * option request done at a higher level.
7937c478bd9Sstevel@tonic-gate */
794*bd670b35SErik Nordmark if (opt->len < sizeof (struct T_opthdr) ||
795*bd670b35SErik Nordmark !opt_length_ok(optd,
796*bd670b35SErik Nordmark opt->len - sizeof (struct T_opthdr))) {
7977c478bd9Sstevel@tonic-gate /* bad size */
7987c478bd9Sstevel@tonic-gate *toa_lenp += _TPI_ALIGN_TOPT(opt->len);
7997c478bd9Sstevel@tonic-gate opt->status = T_FAILURE;
8007c478bd9Sstevel@tonic-gate continue;
8017c478bd9Sstevel@tonic-gate }
8027c478bd9Sstevel@tonic-gate }
8037c478bd9Sstevel@tonic-gate /*
8040f1702c5SYu Xiangning * The proto_opt_lookup() routine above() approved of
8057c478bd9Sstevel@tonic-gate * this option so we can work on the status for it based
8067c478bd9Sstevel@tonic-gate * on the permissions for the operation. (This can
8077c478bd9Sstevel@tonic-gate * override anything set at a higher level).
8087c478bd9Sstevel@tonic-gate *
8097c478bd9Sstevel@tonic-gate * T_CHECK/T_NEGOTIATE semantics:
8107c478bd9Sstevel@tonic-gate * Set status to T_READONLY if read is the only access
8117c478bd9Sstevel@tonic-gate * permitted
8127c478bd9Sstevel@tonic-gate */
8137c478bd9Sstevel@tonic-gate if (OA_READONLY_PERMISSION(optd, cr)) {
8147c478bd9Sstevel@tonic-gate opt->status = T_READONLY;
8157c478bd9Sstevel@tonic-gate *toa_lenp += _TPI_ALIGN_TOPT(opt->len);
8167c478bd9Sstevel@tonic-gate /* skip to next */
8177c478bd9Sstevel@tonic-gate continue;
8187c478bd9Sstevel@tonic-gate }
8197c478bd9Sstevel@tonic-gate
8207c478bd9Sstevel@tonic-gate /*
8217c478bd9Sstevel@tonic-gate * T_CHECK/T_NEGOTIATE semantics:
8227c478bd9Sstevel@tonic-gate * If write (or execute) access is not set, then status
8237c478bd9Sstevel@tonic-gate * is T_NOTSUPPORT.
8247c478bd9Sstevel@tonic-gate */
8257c478bd9Sstevel@tonic-gate if (!OA_WRITE_OR_EXECUTE(optd, cr)) {
8267c478bd9Sstevel@tonic-gate opt->status = T_NOTSUPPORT;
8277c478bd9Sstevel@tonic-gate *toa_lenp += _TPI_ALIGN_TOPT(opt->len);
8287c478bd9Sstevel@tonic-gate /* skip to next option */
8297c478bd9Sstevel@tonic-gate continue;
8307c478bd9Sstevel@tonic-gate }
8317c478bd9Sstevel@tonic-gate /*
8327c478bd9Sstevel@tonic-gate * Option passes all checks. Make room for it in the
8337c478bd9Sstevel@tonic-gate * ack and set success in status.
8347c478bd9Sstevel@tonic-gate * Note: size stored in table does not include header
8357c478bd9Sstevel@tonic-gate * length.
8367c478bd9Sstevel@tonic-gate */
8377c478bd9Sstevel@tonic-gate opt->status = T_SUCCESS;
8387c478bd9Sstevel@tonic-gate *toa_lenp += sizeof (struct T_opthdr) +
8397c478bd9Sstevel@tonic-gate _TPI_ALIGN_TOPT(optd->opdes_size);
8407c478bd9Sstevel@tonic-gate break;
8417c478bd9Sstevel@tonic-gate
8427c478bd9Sstevel@tonic-gate default:
8437c478bd9Sstevel@tonic-gate return (TBADFLAG);
8447c478bd9Sstevel@tonic-gate }
8457c478bd9Sstevel@tonic-gate } /* for loop scanning input buffer */
8467c478bd9Sstevel@tonic-gate
8477c478bd9Sstevel@tonic-gate return (0); /* OK return */
8487c478bd9Sstevel@tonic-gate }
8497c478bd9Sstevel@tonic-gate
8507c478bd9Sstevel@tonic-gate /*
8517c478bd9Sstevel@tonic-gate * This routine makes another pass through the option buffer this
8527c478bd9Sstevel@tonic-gate * time acting on the request based on "status" result in the
8537c478bd9Sstevel@tonic-gate * first pass. It also performs "expansion" of T_ALLOPT into
8547c478bd9Sstevel@tonic-gate * all options of a certain level and acts on each for this request.
8557c478bd9Sstevel@tonic-gate */
8567c478bd9Sstevel@tonic-gate static t_scalar_t
do_options_second_pass(queue_t * q,mblk_t * reqmp,mblk_t * ack_mp,cred_t * cr,optdb_obj_t * dbobjp,t_uscalar_t * worst_statusp)8577c478bd9Sstevel@tonic-gate do_options_second_pass(queue_t *q, mblk_t *reqmp, mblk_t *ack_mp, cred_t *cr,
858*bd670b35SErik Nordmark optdb_obj_t *dbobjp, t_uscalar_t *worst_statusp)
8597c478bd9Sstevel@tonic-gate {
8607c478bd9Sstevel@tonic-gate int failed_option;
8617c478bd9Sstevel@tonic-gate struct T_opthdr *opt;
862*bd670b35SErik Nordmark struct T_opthdr *opt_start, *opt_end;
8637c478bd9Sstevel@tonic-gate uchar_t *optr;
8647c478bd9Sstevel@tonic-gate uint_t optset_context;
8657c478bd9Sstevel@tonic-gate struct T_optmgmt_req *tor = (struct T_optmgmt_req *)reqmp->b_rptr;
8667c478bd9Sstevel@tonic-gate
8677c478bd9Sstevel@tonic-gate optr = (uchar_t *)ack_mp->b_rptr +
8687c478bd9Sstevel@tonic-gate sizeof (struct T_optmgmt_ack); /* assumed int32_t aligned */
8697c478bd9Sstevel@tonic-gate
8707c478bd9Sstevel@tonic-gate /*
8717c478bd9Sstevel@tonic-gate * Set initial values for scanning input
8727c478bd9Sstevel@tonic-gate */
8737c478bd9Sstevel@tonic-gate opt_start = (struct T_opthdr *)mi_offset_param(reqmp,
8747c478bd9Sstevel@tonic-gate tor->OPT_offset, tor->OPT_length);
8757c478bd9Sstevel@tonic-gate if (opt_start == NULL)
8767c478bd9Sstevel@tonic-gate return (TBADOPT);
877*bd670b35SErik Nordmark opt_end = (struct T_opthdr *)((uchar_t *)opt_start + tor->OPT_length);
8787c478bd9Sstevel@tonic-gate ASSERT(__TPI_TOPT_ISALIGNED(opt_start)); /* verified in first pass */
8797c478bd9Sstevel@tonic-gate
880*bd670b35SErik Nordmark for (opt = opt_start; opt && (opt < opt_end);
8817c478bd9Sstevel@tonic-gate opt = _TPI_TOPT_NEXTHDR(opt_start, tor->OPT_length, opt)) {
882*bd670b35SErik Nordmark
8837c478bd9Sstevel@tonic-gate /* verified in first pass */
8847c478bd9Sstevel@tonic-gate ASSERT(_TPI_TOPT_VALID(opt, opt_start, opt_end));
8857c478bd9Sstevel@tonic-gate
8867c478bd9Sstevel@tonic-gate /*
8877c478bd9Sstevel@tonic-gate * If the first pass in process_topthdrs_first_pass()
8887c478bd9Sstevel@tonic-gate * has marked the option as a failure case for the MGMT_flags
8897c478bd9Sstevel@tonic-gate * semantics then there is not much to do.
8907c478bd9Sstevel@tonic-gate *
8917c478bd9Sstevel@tonic-gate * Note: For all practical purposes, T_READONLY status is
8927c478bd9Sstevel@tonic-gate * a "success" for T_DEFAULT/T_CURRENT and "failure" for
8937c478bd9Sstevel@tonic-gate * T_CHECK/T_NEGOTIATE
8947c478bd9Sstevel@tonic-gate */
8957c478bd9Sstevel@tonic-gate failed_option =
8967c478bd9Sstevel@tonic-gate (opt->status == T_NOTSUPPORT) ||
8977c478bd9Sstevel@tonic-gate (opt->status == T_FAILURE) ||
8987c478bd9Sstevel@tonic-gate ((tor->MGMT_flags & (T_NEGOTIATE|T_CHECK)) &&
8997c478bd9Sstevel@tonic-gate (opt->status == T_READONLY));
9007c478bd9Sstevel@tonic-gate
9017c478bd9Sstevel@tonic-gate if (failed_option) {
9027c478bd9Sstevel@tonic-gate /*
9037c478bd9Sstevel@tonic-gate * According to T_DEFAULT/T_CURRENT semantics, the
9047c478bd9Sstevel@tonic-gate * input values, even if present, are to be ignored.
9057c478bd9Sstevel@tonic-gate * Note: Specification is not clear on this, but we
9067c478bd9Sstevel@tonic-gate * interpret that even though we ignore the values, we
9077c478bd9Sstevel@tonic-gate * can return them as is. So we process them similar to
9087c478bd9Sstevel@tonic-gate * T_CHECK/T_NEGOTIATE case which has the semantics to
9097c478bd9Sstevel@tonic-gate * return the values as is. XXX If interpretation is
9107c478bd9Sstevel@tonic-gate * ever determined incorrect fill in appropriate code
9117c478bd9Sstevel@tonic-gate * here to treat T_DEFAULT/T_CURRENT differently.
9127c478bd9Sstevel@tonic-gate *
9137c478bd9Sstevel@tonic-gate * According to T_CHECK/T_NEGOTIATE semantics,
9147c478bd9Sstevel@tonic-gate * in the case of T_NOTSUPPORT/T_FAILURE/T_READONLY,
9157c478bd9Sstevel@tonic-gate * the semantics are to return the "value" part of
9167c478bd9Sstevel@tonic-gate * option untouched. So here we copy the option
9177c478bd9Sstevel@tonic-gate * head including value part if any to output.
9187c478bd9Sstevel@tonic-gate */
9197c478bd9Sstevel@tonic-gate
9207c478bd9Sstevel@tonic-gate bcopy(opt, optr, opt->len);
9217c478bd9Sstevel@tonic-gate optr += _TPI_ALIGN_TOPT(opt->len);
9227c478bd9Sstevel@tonic-gate
9237c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(opt->status,
9247c478bd9Sstevel@tonic-gate *worst_statusp);
9257c478bd9Sstevel@tonic-gate
9267c478bd9Sstevel@tonic-gate /* skip to process next option in buffer */
9277c478bd9Sstevel@tonic-gate continue;
9287c478bd9Sstevel@tonic-gate
9297c478bd9Sstevel@tonic-gate } /* end if "failed option" */
9307c478bd9Sstevel@tonic-gate /*
9317c478bd9Sstevel@tonic-gate * The status is T_SUCCESS or T_READONLY
9327c478bd9Sstevel@tonic-gate * We process the value part here
9337c478bd9Sstevel@tonic-gate */
9347c478bd9Sstevel@tonic-gate ASSERT(opt->status == T_SUCCESS || opt->status == T_READONLY);
9357c478bd9Sstevel@tonic-gate switch (tor->MGMT_flags) {
9367c478bd9Sstevel@tonic-gate case T_DEFAULT:
9377c478bd9Sstevel@tonic-gate /*
9387c478bd9Sstevel@tonic-gate * We fill default value from table or protocol specific
9397c478bd9Sstevel@tonic-gate * function. If this call fails, we pass input through.
9407c478bd9Sstevel@tonic-gate */
9417c478bd9Sstevel@tonic-gate if (do_opt_default(q, opt, &optr, worst_statusp,
9427c478bd9Sstevel@tonic-gate cr, dbobjp) < 0) {
9437c478bd9Sstevel@tonic-gate opt->status = T_FAILURE;
9447c478bd9Sstevel@tonic-gate bcopy(opt, optr, opt->len);
9457c478bd9Sstevel@tonic-gate optr += _TPI_ALIGN_TOPT(opt->len);
9467c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(opt->status,
9477c478bd9Sstevel@tonic-gate *worst_statusp);
9487c478bd9Sstevel@tonic-gate }
9497c478bd9Sstevel@tonic-gate break;
9507c478bd9Sstevel@tonic-gate
9517c478bd9Sstevel@tonic-gate case T_CURRENT:
9527c478bd9Sstevel@tonic-gate
9537c478bd9Sstevel@tonic-gate do_opt_current(q, opt, &optr, worst_statusp, cr,
9547c478bd9Sstevel@tonic-gate dbobjp);
9557c478bd9Sstevel@tonic-gate break;
9567c478bd9Sstevel@tonic-gate
9577c478bd9Sstevel@tonic-gate case T_CHECK:
9587c478bd9Sstevel@tonic-gate case T_NEGOTIATE:
9597c478bd9Sstevel@tonic-gate if (tor->MGMT_flags == T_CHECK)
9607c478bd9Sstevel@tonic-gate optset_context = SETFN_OPTCOM_CHECKONLY;
9617c478bd9Sstevel@tonic-gate else /* T_NEGOTIATE */
9627c478bd9Sstevel@tonic-gate optset_context = SETFN_OPTCOM_NEGOTIATE;
963*bd670b35SErik Nordmark do_opt_check_or_negotiate(q, opt, optset_context,
964*bd670b35SErik Nordmark &optr, worst_statusp, cr, dbobjp);
9657c478bd9Sstevel@tonic-gate break;
9667c478bd9Sstevel@tonic-gate default:
9677c478bd9Sstevel@tonic-gate return (TBADFLAG);
9687c478bd9Sstevel@tonic-gate }
9697c478bd9Sstevel@tonic-gate } /* end for loop scanning option buffer */
9707c478bd9Sstevel@tonic-gate
9717c478bd9Sstevel@tonic-gate ack_mp->b_wptr = optr;
9727c478bd9Sstevel@tonic-gate ASSERT(ack_mp->b_wptr <= ack_mp->b_datap->db_lim);
9737c478bd9Sstevel@tonic-gate
9747c478bd9Sstevel@tonic-gate return (0); /* OK return */
9757c478bd9Sstevel@tonic-gate }
9767c478bd9Sstevel@tonic-gate
9777c478bd9Sstevel@tonic-gate
9787c478bd9Sstevel@tonic-gate static t_uscalar_t
get_worst_status(t_uscalar_t status,t_uscalar_t current_worst_status)9797c478bd9Sstevel@tonic-gate get_worst_status(t_uscalar_t status, t_uscalar_t current_worst_status)
9807c478bd9Sstevel@tonic-gate {
9817c478bd9Sstevel@tonic-gate /*
9827c478bd9Sstevel@tonic-gate * Return the "worst" among the arguments "status" and
9837c478bd9Sstevel@tonic-gate * "current_worst_status".
9847c478bd9Sstevel@tonic-gate *
9857c478bd9Sstevel@tonic-gate * Note: Tracking "worst_status" can be made a bit simpler
9867c478bd9Sstevel@tonic-gate * if we use the property that status codes are bitwise
9877c478bd9Sstevel@tonic-gate * distinct.
9887c478bd9Sstevel@tonic-gate *
9897c478bd9Sstevel@tonic-gate * The pecking order is
9907c478bd9Sstevel@tonic-gate *
9917c478bd9Sstevel@tonic-gate * T_SUCCESS ..... best
9927c478bd9Sstevel@tonic-gate * T_PARTSUCCESS
9937c478bd9Sstevel@tonic-gate * T_FAILURE
9947c478bd9Sstevel@tonic-gate * T_READONLY
9957c478bd9Sstevel@tonic-gate * T_NOTSUPPORT... worst
9967c478bd9Sstevel@tonic-gate */
9977c478bd9Sstevel@tonic-gate if (status == current_worst_status)
9987c478bd9Sstevel@tonic-gate return (current_worst_status);
9997c478bd9Sstevel@tonic-gate switch (current_worst_status) {
10007c478bd9Sstevel@tonic-gate case T_SUCCESS:
10017c478bd9Sstevel@tonic-gate if (status == T_PARTSUCCESS)
10027c478bd9Sstevel@tonic-gate return (T_PARTSUCCESS);
10037c478bd9Sstevel@tonic-gate /* FALLTHROUGH */
10047c478bd9Sstevel@tonic-gate case T_PARTSUCCESS:
10057c478bd9Sstevel@tonic-gate if (status == T_FAILURE)
10067c478bd9Sstevel@tonic-gate return (T_FAILURE);
10077c478bd9Sstevel@tonic-gate /* FALLTHROUGH */
10087c478bd9Sstevel@tonic-gate case T_FAILURE:
10097c478bd9Sstevel@tonic-gate if (status == T_READONLY)
10107c478bd9Sstevel@tonic-gate return (T_READONLY);
10117c478bd9Sstevel@tonic-gate /* FALLTHROUGH */
10127c478bd9Sstevel@tonic-gate case T_READONLY:
10137c478bd9Sstevel@tonic-gate if (status == T_NOTSUPPORT)
10147c478bd9Sstevel@tonic-gate return (T_NOTSUPPORT);
10157c478bd9Sstevel@tonic-gate /* FALLTHROUGH */
10167c478bd9Sstevel@tonic-gate case T_NOTSUPPORT:
10177c478bd9Sstevel@tonic-gate default:
10187c478bd9Sstevel@tonic-gate return (current_worst_status);
10197c478bd9Sstevel@tonic-gate }
10207c478bd9Sstevel@tonic-gate }
10217c478bd9Sstevel@tonic-gate
10227c478bd9Sstevel@tonic-gate static int
do_opt_default(queue_t * q,struct T_opthdr * reqopt,uchar_t ** resptrp,t_uscalar_t * worst_statusp,cred_t * cr,optdb_obj_t * dbobjp)10237c478bd9Sstevel@tonic-gate do_opt_default(queue_t *q, struct T_opthdr *reqopt, uchar_t **resptrp,
10247c478bd9Sstevel@tonic-gate t_uscalar_t *worst_statusp, cred_t *cr, optdb_obj_t *dbobjp)
10257c478bd9Sstevel@tonic-gate {
10267c478bd9Sstevel@tonic-gate pfi_t deffn = dbobjp->odb_deffn;
10277c478bd9Sstevel@tonic-gate opdes_t *opt_arr = dbobjp->odb_opt_des_arr;
10287c478bd9Sstevel@tonic-gate uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
10297c478bd9Sstevel@tonic-gate
10307c478bd9Sstevel@tonic-gate struct T_opthdr *topth;
10317c478bd9Sstevel@tonic-gate opdes_t *optd;
10327c478bd9Sstevel@tonic-gate
10337c478bd9Sstevel@tonic-gate if (reqopt->name != T_ALLOPT) {
10347c478bd9Sstevel@tonic-gate /*
10357c478bd9Sstevel@tonic-gate * lookup the option in the table and fill default value
10367c478bd9Sstevel@tonic-gate */
10370f1702c5SYu Xiangning optd = proto_opt_lookup(reqopt->level, reqopt->name,
10387c478bd9Sstevel@tonic-gate opt_arr, opt_arr_cnt);
10397c478bd9Sstevel@tonic-gate
1040*bd670b35SErik Nordmark /* Calling routine should have verified it it exists */
1041*bd670b35SErik Nordmark ASSERT(optd != NULL);
10427c478bd9Sstevel@tonic-gate
10437c478bd9Sstevel@tonic-gate topth = (struct T_opthdr *)(*resptrp);
10447c478bd9Sstevel@tonic-gate topth->level = reqopt->level;
10457c478bd9Sstevel@tonic-gate topth->name = reqopt->name;
10467c478bd9Sstevel@tonic-gate topth->status = reqopt->status;
10477c478bd9Sstevel@tonic-gate
10487c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(reqopt->status,
10497c478bd9Sstevel@tonic-gate *worst_statusp);
10507c478bd9Sstevel@tonic-gate
10517c478bd9Sstevel@tonic-gate if (optd->opdes_props & OP_NODEFAULT) {
10527c478bd9Sstevel@tonic-gate /* header only, no default "value" part */
10537c478bd9Sstevel@tonic-gate topth->len = sizeof (struct T_opthdr);
10547c478bd9Sstevel@tonic-gate *resptrp += sizeof (struct T_opthdr);
10557c478bd9Sstevel@tonic-gate } else {
10567c478bd9Sstevel@tonic-gate int deflen;
10577c478bd9Sstevel@tonic-gate
10587c478bd9Sstevel@tonic-gate if (optd->opdes_props & OP_DEF_FN) {
10597c478bd9Sstevel@tonic-gate deflen = (*deffn)(q, reqopt->level,
10607c478bd9Sstevel@tonic-gate reqopt->name, _TPI_TOPT_DATA(topth));
10617c478bd9Sstevel@tonic-gate if (deflen >= 0) {
10627c478bd9Sstevel@tonic-gate topth->len = (t_uscalar_t)
10637c478bd9Sstevel@tonic-gate (sizeof (struct T_opthdr) + deflen);
10647c478bd9Sstevel@tonic-gate } else {
10657c478bd9Sstevel@tonic-gate /*
10667c478bd9Sstevel@tonic-gate * return error, this should 'pass
10677c478bd9Sstevel@tonic-gate * through' the option and maybe some
10687c478bd9Sstevel@tonic-gate * other level will fill it in or
10697c478bd9Sstevel@tonic-gate * already did.
10707c478bd9Sstevel@tonic-gate * (No change in 'resptrp' upto here)
10717c478bd9Sstevel@tonic-gate */
10727c478bd9Sstevel@tonic-gate return (-1);
10737c478bd9Sstevel@tonic-gate }
10747c478bd9Sstevel@tonic-gate } else {
10757c478bd9Sstevel@tonic-gate /* fill length and value part */
10767c478bd9Sstevel@tonic-gate switch (optd->opdes_size) {
10777c478bd9Sstevel@tonic-gate /*
10787c478bd9Sstevel@tonic-gate * Since options are guaranteed aligned only
10797c478bd9Sstevel@tonic-gate * on a 4 byte boundary (t_scalar_t) any
10807c478bd9Sstevel@tonic-gate * option that is greater in size will default
10817c478bd9Sstevel@tonic-gate * to the bcopy below
10827c478bd9Sstevel@tonic-gate */
10837c478bd9Sstevel@tonic-gate case sizeof (int32_t):
10847c478bd9Sstevel@tonic-gate *(int32_t *)_TPI_TOPT_DATA(topth) =
10857c478bd9Sstevel@tonic-gate (int32_t)optd->opdes_default;
10867c478bd9Sstevel@tonic-gate break;
10877c478bd9Sstevel@tonic-gate case sizeof (int16_t):
10887c478bd9Sstevel@tonic-gate *(int16_t *)_TPI_TOPT_DATA(topth) =
10897c478bd9Sstevel@tonic-gate (int16_t)optd->opdes_default;
10907c478bd9Sstevel@tonic-gate break;
10917c478bd9Sstevel@tonic-gate case sizeof (int8_t):
10927c478bd9Sstevel@tonic-gate *(int8_t *)_TPI_TOPT_DATA(topth) =
10937c478bd9Sstevel@tonic-gate (int8_t)optd->opdes_default;
10947c478bd9Sstevel@tonic-gate break;
10957c478bd9Sstevel@tonic-gate default:
10967c478bd9Sstevel@tonic-gate /*
10977c478bd9Sstevel@tonic-gate * other length but still assume
10987c478bd9Sstevel@tonic-gate * fixed - use bcopy
10997c478bd9Sstevel@tonic-gate */
11007c478bd9Sstevel@tonic-gate bcopy(optd->opdes_defbuf,
11017c478bd9Sstevel@tonic-gate _TPI_TOPT_DATA(topth),
11027c478bd9Sstevel@tonic-gate optd->opdes_size);
11037c478bd9Sstevel@tonic-gate break;
11047c478bd9Sstevel@tonic-gate }
11057c478bd9Sstevel@tonic-gate topth->len = (t_uscalar_t)(optd->opdes_size +
11067c478bd9Sstevel@tonic-gate sizeof (struct T_opthdr));
11077c478bd9Sstevel@tonic-gate }
11087c478bd9Sstevel@tonic-gate *resptrp += _TPI_ALIGN_TOPT(topth->len);
11097c478bd9Sstevel@tonic-gate }
11107c478bd9Sstevel@tonic-gate return (0); /* OK return */
11117c478bd9Sstevel@tonic-gate }
11127c478bd9Sstevel@tonic-gate
11137c478bd9Sstevel@tonic-gate /*
11147c478bd9Sstevel@tonic-gate * T_ALLOPT processing
11157c478bd9Sstevel@tonic-gate *
11167c478bd9Sstevel@tonic-gate * lookup and stuff default values of all the options of the
11177c478bd9Sstevel@tonic-gate * level specified
11187c478bd9Sstevel@tonic-gate */
11197c478bd9Sstevel@tonic-gate for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
11207c478bd9Sstevel@tonic-gate if (reqopt->level != optd->opdes_level)
11217c478bd9Sstevel@tonic-gate continue;
11227c478bd9Sstevel@tonic-gate /*
11237c478bd9Sstevel@tonic-gate *
11247c478bd9Sstevel@tonic-gate * T_DEFAULT semantics:
11257c478bd9Sstevel@tonic-gate * XXX: we interpret T_DEFAULT semantics such that access to
11267c478bd9Sstevel@tonic-gate * read value is required for access even the default value.
11277c478bd9Sstevel@tonic-gate * Else option is ignored for T_ALLOPT request.
11287c478bd9Sstevel@tonic-gate */
11297c478bd9Sstevel@tonic-gate if (!OA_READ_PERMISSION(optd, cr))
11307c478bd9Sstevel@tonic-gate /* skip this one */
11317c478bd9Sstevel@tonic-gate continue;
11327c478bd9Sstevel@tonic-gate
11337c478bd9Sstevel@tonic-gate /*
11347c478bd9Sstevel@tonic-gate * Found option of same level as T_ALLOPT request
11357c478bd9Sstevel@tonic-gate * that we can return.
11367c478bd9Sstevel@tonic-gate */
11377c478bd9Sstevel@tonic-gate
11387c478bd9Sstevel@tonic-gate topth = (struct T_opthdr *)(*resptrp);
11397c478bd9Sstevel@tonic-gate topth->level = optd->opdes_level;
11407c478bd9Sstevel@tonic-gate topth->name = optd->opdes_name;
11417c478bd9Sstevel@tonic-gate
11427c478bd9Sstevel@tonic-gate /*
11437c478bd9Sstevel@tonic-gate * T_DEFAULT semantics:
11447c478bd9Sstevel@tonic-gate * We know that read access is set. If no other access is set,
11457c478bd9Sstevel@tonic-gate * then status is T_READONLY
11467c478bd9Sstevel@tonic-gate */
11477c478bd9Sstevel@tonic-gate if (OA_READONLY_PERMISSION(optd, cr)) {
11487c478bd9Sstevel@tonic-gate topth->status = T_READONLY;
11497c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(T_READONLY,
11507c478bd9Sstevel@tonic-gate *worst_statusp);
11517c478bd9Sstevel@tonic-gate } else {
11527c478bd9Sstevel@tonic-gate topth->status = T_SUCCESS;
11537c478bd9Sstevel@tonic-gate /*
11547c478bd9Sstevel@tonic-gate * Note: *worst_statusp has to be T_SUCCESS or
11557c478bd9Sstevel@tonic-gate * worse so no need to adjust
11567c478bd9Sstevel@tonic-gate */
11577c478bd9Sstevel@tonic-gate }
11587c478bd9Sstevel@tonic-gate
11597c478bd9Sstevel@tonic-gate if (optd->opdes_props & OP_NODEFAULT) {
11607c478bd9Sstevel@tonic-gate /* header only, no value part */
11617c478bd9Sstevel@tonic-gate topth->len = sizeof (struct T_opthdr);
11627c478bd9Sstevel@tonic-gate *resptrp += sizeof (struct T_opthdr);
11637c478bd9Sstevel@tonic-gate } else {
11647c478bd9Sstevel@tonic-gate int deflen;
11657c478bd9Sstevel@tonic-gate
11667c478bd9Sstevel@tonic-gate if (optd->opdes_props & OP_DEF_FN) {
11677c478bd9Sstevel@tonic-gate deflen = (*deffn)(q, reqopt->level,
11687c478bd9Sstevel@tonic-gate reqopt->name, _TPI_TOPT_DATA(topth));
11697c478bd9Sstevel@tonic-gate if (deflen >= 0) {
11707c478bd9Sstevel@tonic-gate topth->len = (t_uscalar_t)(deflen +
11717c478bd9Sstevel@tonic-gate sizeof (struct T_opthdr));
11727c478bd9Sstevel@tonic-gate } else {
11737c478bd9Sstevel@tonic-gate /*
11747c478bd9Sstevel@tonic-gate * deffn failed.
11757c478bd9Sstevel@tonic-gate * return just the header as T_ALLOPT
11767c478bd9Sstevel@tonic-gate * expansion.
11777c478bd9Sstevel@tonic-gate * Some other level deffn may
11787c478bd9Sstevel@tonic-gate * supply value part.
11797c478bd9Sstevel@tonic-gate */
11807c478bd9Sstevel@tonic-gate topth->len = sizeof (struct T_opthdr);
11817c478bd9Sstevel@tonic-gate topth->status = T_FAILURE;
11827c478bd9Sstevel@tonic-gate *worst_statusp =
11837c478bd9Sstevel@tonic-gate get_worst_status(T_FAILURE,
11847c478bd9Sstevel@tonic-gate *worst_statusp);
11857c478bd9Sstevel@tonic-gate }
11867c478bd9Sstevel@tonic-gate } else {
11877c478bd9Sstevel@tonic-gate /*
11887c478bd9Sstevel@tonic-gate * fill length and value part from
11897c478bd9Sstevel@tonic-gate * table
11907c478bd9Sstevel@tonic-gate */
11917c478bd9Sstevel@tonic-gate switch (optd->opdes_size) {
11927c478bd9Sstevel@tonic-gate /*
11937c478bd9Sstevel@tonic-gate * Since options are guaranteed aligned only
11947c478bd9Sstevel@tonic-gate * on a 4 byte boundary (t_scalar_t) any
11957c478bd9Sstevel@tonic-gate * option that is greater in size will default
11967c478bd9Sstevel@tonic-gate * to the bcopy below
11977c478bd9Sstevel@tonic-gate */
11987c478bd9Sstevel@tonic-gate case sizeof (int32_t):
11997c478bd9Sstevel@tonic-gate *(int32_t *)_TPI_TOPT_DATA(topth) =
12007c478bd9Sstevel@tonic-gate (int32_t)optd->opdes_default;
12017c478bd9Sstevel@tonic-gate break;
12027c478bd9Sstevel@tonic-gate case sizeof (int16_t):
12037c478bd9Sstevel@tonic-gate *(int16_t *)_TPI_TOPT_DATA(topth) =
12047c478bd9Sstevel@tonic-gate (int16_t)optd->opdes_default;
12057c478bd9Sstevel@tonic-gate break;
12067c478bd9Sstevel@tonic-gate case sizeof (int8_t):
12077c478bd9Sstevel@tonic-gate *(int8_t *)_TPI_TOPT_DATA(topth) =
12087c478bd9Sstevel@tonic-gate (int8_t)optd->opdes_default;
12097c478bd9Sstevel@tonic-gate break;
12107c478bd9Sstevel@tonic-gate default:
12117c478bd9Sstevel@tonic-gate /*
12127c478bd9Sstevel@tonic-gate * other length but still assume
12137c478bd9Sstevel@tonic-gate * fixed - use bcopy
12147c478bd9Sstevel@tonic-gate */
12157c478bd9Sstevel@tonic-gate bcopy(optd->opdes_defbuf,
12167c478bd9Sstevel@tonic-gate _TPI_TOPT_DATA(topth),
12177c478bd9Sstevel@tonic-gate optd->opdes_size);
12187c478bd9Sstevel@tonic-gate }
12197c478bd9Sstevel@tonic-gate topth->len = (t_uscalar_t)(optd->opdes_size +
12207c478bd9Sstevel@tonic-gate sizeof (struct T_opthdr));
12217c478bd9Sstevel@tonic-gate }
12227c478bd9Sstevel@tonic-gate *resptrp += _TPI_ALIGN_TOPT(topth->len);
12237c478bd9Sstevel@tonic-gate }
12247c478bd9Sstevel@tonic-gate }
12257c478bd9Sstevel@tonic-gate return (0);
12267c478bd9Sstevel@tonic-gate }
12277c478bd9Sstevel@tonic-gate
12287c478bd9Sstevel@tonic-gate static void
do_opt_current(queue_t * q,struct T_opthdr * reqopt,uchar_t ** resptrp,t_uscalar_t * worst_statusp,cred_t * cr,optdb_obj_t * dbobjp)12297c478bd9Sstevel@tonic-gate do_opt_current(queue_t *q, struct T_opthdr *reqopt, uchar_t **resptrp,
12307c478bd9Sstevel@tonic-gate t_uscalar_t *worst_statusp, cred_t *cr, optdb_obj_t *dbobjp)
12317c478bd9Sstevel@tonic-gate {
12327c478bd9Sstevel@tonic-gate pfi_t getfn = dbobjp->odb_getfn;
12337c478bd9Sstevel@tonic-gate opdes_t *opt_arr = dbobjp->odb_opt_des_arr;
12347c478bd9Sstevel@tonic-gate uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
12357c478bd9Sstevel@tonic-gate struct T_opthdr *topth;
12367c478bd9Sstevel@tonic-gate opdes_t *optd;
12377c478bd9Sstevel@tonic-gate int optlen;
12387c478bd9Sstevel@tonic-gate uchar_t *initptr = *resptrp;
12397c478bd9Sstevel@tonic-gate
12407c478bd9Sstevel@tonic-gate /*
12417c478bd9Sstevel@tonic-gate * We call getfn to get the current value of an option. The call may
12427c478bd9Sstevel@tonic-gate * fail in which case we copy the values from the input buffer. Maybe
12437c478bd9Sstevel@tonic-gate * something downstream will fill it in or something upstream did.
12447c478bd9Sstevel@tonic-gate */
12457c478bd9Sstevel@tonic-gate
12467c478bd9Sstevel@tonic-gate if (reqopt->name != T_ALLOPT) {
12477c478bd9Sstevel@tonic-gate topth = (struct T_opthdr *)*resptrp;
12487c478bd9Sstevel@tonic-gate *resptrp += sizeof (struct T_opthdr);
12497c478bd9Sstevel@tonic-gate optlen = (*getfn)(q, reqopt->level, reqopt->name, *resptrp);
12507c478bd9Sstevel@tonic-gate if (optlen >= 0) {
12517c478bd9Sstevel@tonic-gate topth->len = (t_uscalar_t)(optlen +
12527c478bd9Sstevel@tonic-gate sizeof (struct T_opthdr));
12537c478bd9Sstevel@tonic-gate topth->level = reqopt->level;
12547c478bd9Sstevel@tonic-gate topth->name = reqopt->name;
12557c478bd9Sstevel@tonic-gate topth->status = reqopt->status;
12567c478bd9Sstevel@tonic-gate *resptrp += _TPI_ALIGN_TOPT(optlen);
12577c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(topth->status,
12587c478bd9Sstevel@tonic-gate *worst_statusp);
12597c478bd9Sstevel@tonic-gate } else {
12607c478bd9Sstevel@tonic-gate /* failed - reset "*resptrp" pointer */
12617c478bd9Sstevel@tonic-gate *resptrp -= sizeof (struct T_opthdr);
12627c478bd9Sstevel@tonic-gate }
12637c478bd9Sstevel@tonic-gate } else { /* T_ALLOPT processing */
12647c478bd9Sstevel@tonic-gate /* scan and get all options */
12657c478bd9Sstevel@tonic-gate for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
12667c478bd9Sstevel@tonic-gate /* skip other levels */
12677c478bd9Sstevel@tonic-gate if (reqopt->level != optd->opdes_level)
12687c478bd9Sstevel@tonic-gate continue;
12697c478bd9Sstevel@tonic-gate
12707c478bd9Sstevel@tonic-gate if (!OA_READ_PERMISSION(optd, cr))
12717c478bd9Sstevel@tonic-gate /* skip this one */
12727c478bd9Sstevel@tonic-gate continue;
12737c478bd9Sstevel@tonic-gate
12747c478bd9Sstevel@tonic-gate topth = (struct T_opthdr *)*resptrp;
12757c478bd9Sstevel@tonic-gate *resptrp += sizeof (struct T_opthdr);
12767c478bd9Sstevel@tonic-gate
12777c478bd9Sstevel@tonic-gate /* get option of this level */
12787c478bd9Sstevel@tonic-gate optlen = (*getfn)(q, reqopt->level, optd->opdes_name,
12797c478bd9Sstevel@tonic-gate *resptrp);
12807c478bd9Sstevel@tonic-gate if (optlen >= 0) {
12817c478bd9Sstevel@tonic-gate /* success */
12827c478bd9Sstevel@tonic-gate topth->len = (t_uscalar_t)(optlen +
12837c478bd9Sstevel@tonic-gate sizeof (struct T_opthdr));
12847c478bd9Sstevel@tonic-gate topth->level = reqopt->level;
12857c478bd9Sstevel@tonic-gate topth->name = optd->opdes_name;
12867c478bd9Sstevel@tonic-gate if (OA_READONLY_PERMISSION(optd, cr))
12877c478bd9Sstevel@tonic-gate topth->status = T_READONLY;
12887c478bd9Sstevel@tonic-gate else
12897c478bd9Sstevel@tonic-gate topth->status = T_SUCCESS;
12907c478bd9Sstevel@tonic-gate *resptrp += _TPI_ALIGN_TOPT(optlen);
12917c478bd9Sstevel@tonic-gate } else {
12927c478bd9Sstevel@tonic-gate /*
12937c478bd9Sstevel@tonic-gate * failed, return as T_FAILURE and null value
12947c478bd9Sstevel@tonic-gate * part. Maybe something downstream will
12957c478bd9Sstevel@tonic-gate * handle this one and fill in a value. Here
12967c478bd9Sstevel@tonic-gate * it is just part of T_ALLOPT expansion.
12977c478bd9Sstevel@tonic-gate */
12987c478bd9Sstevel@tonic-gate topth->len = sizeof (struct T_opthdr);
12997c478bd9Sstevel@tonic-gate topth->level = reqopt->level;
13007c478bd9Sstevel@tonic-gate topth->name = optd->opdes_name;
13017c478bd9Sstevel@tonic-gate topth->status = T_FAILURE;
13027c478bd9Sstevel@tonic-gate }
13037c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(topth->status,
13047c478bd9Sstevel@tonic-gate *worst_statusp);
13057c478bd9Sstevel@tonic-gate } /* end for loop */
13067c478bd9Sstevel@tonic-gate }
13077c478bd9Sstevel@tonic-gate if (*resptrp == initptr) {
13087c478bd9Sstevel@tonic-gate /*
1309*bd670b35SErik Nordmark * getfn failed and does not want to handle this option.
13107c478bd9Sstevel@tonic-gate */
13117c478bd9Sstevel@tonic-gate reqopt->status = T_FAILURE;
13127c478bd9Sstevel@tonic-gate bcopy(reqopt, *resptrp, reqopt->len);
13137c478bd9Sstevel@tonic-gate *resptrp += _TPI_ALIGN_TOPT(reqopt->len);
13147c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(reqopt->status,
13157c478bd9Sstevel@tonic-gate *worst_statusp);
13167c478bd9Sstevel@tonic-gate }
13177c478bd9Sstevel@tonic-gate }
13187c478bd9Sstevel@tonic-gate
1319*bd670b35SErik Nordmark static void
do_opt_check_or_negotiate(queue_t * q,struct T_opthdr * reqopt,uint_t optset_context,uchar_t ** resptrp,t_uscalar_t * worst_statusp,cred_t * cr,optdb_obj_t * dbobjp)13207c478bd9Sstevel@tonic-gate do_opt_check_or_negotiate(queue_t *q, struct T_opthdr *reqopt,
13217c478bd9Sstevel@tonic-gate uint_t optset_context, uchar_t **resptrp, t_uscalar_t *worst_statusp,
1322*bd670b35SErik Nordmark cred_t *cr, optdb_obj_t *dbobjp)
13237c478bd9Sstevel@tonic-gate {
13247c478bd9Sstevel@tonic-gate pfi_t deffn = dbobjp->odb_deffn;
13257c478bd9Sstevel@tonic-gate opt_set_fn setfn = dbobjp->odb_setfn;
13267c478bd9Sstevel@tonic-gate opdes_t *opt_arr = dbobjp->odb_opt_des_arr;
13277c478bd9Sstevel@tonic-gate uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
13287c478bd9Sstevel@tonic-gate struct T_opthdr *topth;
13297c478bd9Sstevel@tonic-gate opdes_t *optd;
13307c478bd9Sstevel@tonic-gate int error;
13317c478bd9Sstevel@tonic-gate t_uscalar_t optlen;
13327c478bd9Sstevel@tonic-gate t_scalar_t optsize;
13337c478bd9Sstevel@tonic-gate uchar_t *initptr = *resptrp;
13347c478bd9Sstevel@tonic-gate
13357c478bd9Sstevel@tonic-gate ASSERT(reqopt->status == T_SUCCESS);
13367c478bd9Sstevel@tonic-gate
13377c478bd9Sstevel@tonic-gate if (reqopt->name != T_ALLOPT) {
13387c478bd9Sstevel@tonic-gate topth = (struct T_opthdr *)*resptrp;
13397c478bd9Sstevel@tonic-gate *resptrp += sizeof (struct T_opthdr);
13407c478bd9Sstevel@tonic-gate error = (*setfn)(q, optset_context, reqopt->level, reqopt->name,
13417c478bd9Sstevel@tonic-gate reqopt->len - sizeof (struct T_opthdr),
13427c478bd9Sstevel@tonic-gate _TPI_TOPT_DATA(reqopt), &optlen, _TPI_TOPT_DATA(topth),
1343*bd670b35SErik Nordmark NULL, cr);
13447c478bd9Sstevel@tonic-gate if (error) {
13457c478bd9Sstevel@tonic-gate /* failed - reset "*resptrp" */
13467c478bd9Sstevel@tonic-gate *resptrp -= sizeof (struct T_opthdr);
13477c478bd9Sstevel@tonic-gate } else {
13487c478bd9Sstevel@tonic-gate /*
13497c478bd9Sstevel@tonic-gate * success - "value" already filled in setfn()
13507c478bd9Sstevel@tonic-gate */
13517c478bd9Sstevel@tonic-gate topth->len = (t_uscalar_t)(optlen +
13527c478bd9Sstevel@tonic-gate sizeof (struct T_opthdr));
13537c478bd9Sstevel@tonic-gate topth->level = reqopt->level;
13547c478bd9Sstevel@tonic-gate topth->name = reqopt->name;
13557c478bd9Sstevel@tonic-gate topth->status = reqopt->status;
13567c478bd9Sstevel@tonic-gate *resptrp += _TPI_ALIGN_TOPT(optlen);
13577c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(topth->status,
13587c478bd9Sstevel@tonic-gate *worst_statusp);
13597c478bd9Sstevel@tonic-gate }
13607c478bd9Sstevel@tonic-gate } else { /* T_ALLOPT processing */
13617c478bd9Sstevel@tonic-gate /* only for T_NEGOTIATE case */
13627c478bd9Sstevel@tonic-gate ASSERT(optset_context == SETFN_OPTCOM_NEGOTIATE);
13637c478bd9Sstevel@tonic-gate
13647c478bd9Sstevel@tonic-gate /* scan and set all options to default value */
13657c478bd9Sstevel@tonic-gate for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
13667c478bd9Sstevel@tonic-gate
13677c478bd9Sstevel@tonic-gate /* skip other levels */
13687c478bd9Sstevel@tonic-gate if (reqopt->level != optd->opdes_level)
13697c478bd9Sstevel@tonic-gate continue;
13707c478bd9Sstevel@tonic-gate
13717c478bd9Sstevel@tonic-gate if (OA_EXECUTE_PERMISSION(optd, cr) ||
13727c478bd9Sstevel@tonic-gate OA_NO_PERMISSION(optd, cr)) {
13737c478bd9Sstevel@tonic-gate /*
13747c478bd9Sstevel@tonic-gate * skip this one too. Does not make sense to
13757c478bd9Sstevel@tonic-gate * set anything to default value for "execute"
13767c478bd9Sstevel@tonic-gate * options.
13777c478bd9Sstevel@tonic-gate */
13787c478bd9Sstevel@tonic-gate continue;
13797c478bd9Sstevel@tonic-gate }
13807c478bd9Sstevel@tonic-gate
13817c478bd9Sstevel@tonic-gate if (OA_READONLY_PERMISSION(optd, cr)) {
13827c478bd9Sstevel@tonic-gate /*
13837c478bd9Sstevel@tonic-gate * Return with T_READONLY status (and no value
13847c478bd9Sstevel@tonic-gate * part). Note: spec is not clear but
13857c478bd9Sstevel@tonic-gate * XTI test suite needs this.
13867c478bd9Sstevel@tonic-gate */
13877c478bd9Sstevel@tonic-gate topth = (struct T_opthdr *)*resptrp;
13887c478bd9Sstevel@tonic-gate topth->len = sizeof (struct T_opthdr);
13897c478bd9Sstevel@tonic-gate *resptrp += topth->len;
13907c478bd9Sstevel@tonic-gate topth->level = reqopt->level;
13917c478bd9Sstevel@tonic-gate topth->name = optd->opdes_name;
13927c478bd9Sstevel@tonic-gate topth->status = T_READONLY;
13937c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(topth->status,
13947c478bd9Sstevel@tonic-gate *worst_statusp);
13957c478bd9Sstevel@tonic-gate continue;
13967c478bd9Sstevel@tonic-gate }
13977c478bd9Sstevel@tonic-gate
13987c478bd9Sstevel@tonic-gate /*
13997c478bd9Sstevel@tonic-gate * It is not read only or execute type
14007c478bd9Sstevel@tonic-gate * the it must have write permission
14017c478bd9Sstevel@tonic-gate */
14027c478bd9Sstevel@tonic-gate ASSERT(OA_WRITE_PERMISSION(optd, cr));
14037c478bd9Sstevel@tonic-gate
14047c478bd9Sstevel@tonic-gate topth = (struct T_opthdr *)*resptrp;
14057c478bd9Sstevel@tonic-gate *resptrp += sizeof (struct T_opthdr);
14067c478bd9Sstevel@tonic-gate
14077c478bd9Sstevel@tonic-gate topth->len = sizeof (struct T_opthdr);
14087c478bd9Sstevel@tonic-gate topth->level = reqopt->level;
14097c478bd9Sstevel@tonic-gate topth->name = optd->opdes_name;
14107c478bd9Sstevel@tonic-gate if (optd->opdes_props & OP_NODEFAULT) {
14117c478bd9Sstevel@tonic-gate /*
14127c478bd9Sstevel@tonic-gate * Option of "no default value" so it does not
14137c478bd9Sstevel@tonic-gate * make sense to try to set it. We just return
14147c478bd9Sstevel@tonic-gate * header with status of T_SUCCESS
14157c478bd9Sstevel@tonic-gate * XXX should this be failure ?
14167c478bd9Sstevel@tonic-gate */
14177c478bd9Sstevel@tonic-gate topth->status = T_SUCCESS;
14187c478bd9Sstevel@tonic-gate continue; /* skip setting */
14197c478bd9Sstevel@tonic-gate }
14207c478bd9Sstevel@tonic-gate if (optd->opdes_props & OP_DEF_FN) {
14217c478bd9Sstevel@tonic-gate if ((optd->opdes_props & OP_VARLEN) ||
14227c478bd9Sstevel@tonic-gate ((optsize = (*deffn)(q, reqopt->level,
14237c478bd9Sstevel@tonic-gate optd->opdes_name,
14247c478bd9Sstevel@tonic-gate (uchar_t *)optd->opdes_defbuf)) < 0)) {
14257c478bd9Sstevel@tonic-gate /* XXX - skip these too */
14267c478bd9Sstevel@tonic-gate topth->status = T_SUCCESS;
14277c478bd9Sstevel@tonic-gate continue; /* skip setting */
14287c478bd9Sstevel@tonic-gate }
14297c478bd9Sstevel@tonic-gate } else {
14307c478bd9Sstevel@tonic-gate optsize = optd->opdes_size;
14317c478bd9Sstevel@tonic-gate }
14327c478bd9Sstevel@tonic-gate
14337c478bd9Sstevel@tonic-gate
14347c478bd9Sstevel@tonic-gate /* set option of this level */
14357c478bd9Sstevel@tonic-gate error = (*setfn)(q, SETFN_OPTCOM_NEGOTIATE,
14367c478bd9Sstevel@tonic-gate reqopt->level, optd->opdes_name, optsize,
14377c478bd9Sstevel@tonic-gate (uchar_t *)optd->opdes_defbuf, &optlen,
1438*bd670b35SErik Nordmark _TPI_TOPT_DATA(topth), NULL, cr);
14397c478bd9Sstevel@tonic-gate if (error) {
14407c478bd9Sstevel@tonic-gate /*
14417c478bd9Sstevel@tonic-gate * failed, return as T_FAILURE and null value
14427c478bd9Sstevel@tonic-gate * part. Maybe something downstream will
14437c478bd9Sstevel@tonic-gate * handle this one and fill in a value. Here
14447c478bd9Sstevel@tonic-gate * it is just part of T_ALLOPT expansion.
14457c478bd9Sstevel@tonic-gate */
14467c478bd9Sstevel@tonic-gate topth->status = T_FAILURE;
14477c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(topth->status,
14487c478bd9Sstevel@tonic-gate *worst_statusp);
14497c478bd9Sstevel@tonic-gate } else {
14507c478bd9Sstevel@tonic-gate /* success */
14517c478bd9Sstevel@tonic-gate topth->len += optlen;
14527c478bd9Sstevel@tonic-gate topth->status = T_SUCCESS;
14537c478bd9Sstevel@tonic-gate *resptrp += _TPI_ALIGN_TOPT(optlen);
14547c478bd9Sstevel@tonic-gate }
14557c478bd9Sstevel@tonic-gate } /* end for loop */
14567c478bd9Sstevel@tonic-gate /* END T_ALLOPT */
14577c478bd9Sstevel@tonic-gate }
14587c478bd9Sstevel@tonic-gate
14597c478bd9Sstevel@tonic-gate if (*resptrp == initptr) {
14607c478bd9Sstevel@tonic-gate /*
1461*bd670b35SErik Nordmark * setfn failed and does not want to handle this option.
14627c478bd9Sstevel@tonic-gate */
14637c478bd9Sstevel@tonic-gate reqopt->status = T_FAILURE;
14647c478bd9Sstevel@tonic-gate bcopy(reqopt, *resptrp, reqopt->len);
14657c478bd9Sstevel@tonic-gate *resptrp += _TPI_ALIGN_TOPT(reqopt->len);
14667c478bd9Sstevel@tonic-gate *worst_statusp = get_worst_status(reqopt->status,
14677c478bd9Sstevel@tonic-gate *worst_statusp);
14687c478bd9Sstevel@tonic-gate }
14697c478bd9Sstevel@tonic-gate }
14707c478bd9Sstevel@tonic-gate
14717c478bd9Sstevel@tonic-gate /*
14727c478bd9Sstevel@tonic-gate * The following routines process options buffer passed with
14737c478bd9Sstevel@tonic-gate * T_CONN_REQ, T_CONN_RES and T_UNITDATA_REQ.
14747c478bd9Sstevel@tonic-gate * This routine does the consistency check applied to the
14757c478bd9Sstevel@tonic-gate * sanity of formatting of multiple options packed in the
14767c478bd9Sstevel@tonic-gate * buffer.
14777c478bd9Sstevel@tonic-gate *
14787c478bd9Sstevel@tonic-gate * XTI brain damage alert:
14797c478bd9Sstevel@tonic-gate * XTI interface adopts the notion of an option being an
14807c478bd9Sstevel@tonic-gate * "absolute requirement" from OSI transport service (but applies
14817c478bd9Sstevel@tonic-gate * it to all transports including Internet transports).
14827c478bd9Sstevel@tonic-gate * The main effect of that is action on failure to "negotiate" a
14837c478bd9Sstevel@tonic-gate * requested option to the exact requested value
14847c478bd9Sstevel@tonic-gate *
14857c478bd9Sstevel@tonic-gate * - if the option is an "absolute requirement", the primitive
14867c478bd9Sstevel@tonic-gate * is aborted (e.g T_DISCON_REQ or T_UDERR generated)
14877c478bd9Sstevel@tonic-gate * - if the option is NOT and "absolute requirement" it can
14887c478bd9Sstevel@tonic-gate * just be ignored.
14897c478bd9Sstevel@tonic-gate *
14907c478bd9Sstevel@tonic-gate * We would not support "negotiating" of options on connection
14917c478bd9Sstevel@tonic-gate * primitives for Internet transports. However just in case we
14927c478bd9Sstevel@tonic-gate * forced to in order to pass strange test suites, the design here
14937c478bd9Sstevel@tonic-gate * tries to support these notions.
14947c478bd9Sstevel@tonic-gate *
14957c478bd9Sstevel@tonic-gate * tpi_optcom_buf(q, mp, opt_lenp, opt_offset, cred, dbobjp, thisdg_attrs,
14967c478bd9Sstevel@tonic-gate * *is_absreq_failurep)
14977c478bd9Sstevel@tonic-gate *
14987c478bd9Sstevel@tonic-gate * - Verify the option buffer, if formatted badly, return error 1
14997c478bd9Sstevel@tonic-gate *
15007c478bd9Sstevel@tonic-gate * - If it is a "permissions" failure (read-only), return error 2
15017c478bd9Sstevel@tonic-gate *
15027c478bd9Sstevel@tonic-gate * - Else, process the option "in place", the following can happen,
15037c478bd9Sstevel@tonic-gate * - if a "privileged" option, mark it as "ignored".
15047c478bd9Sstevel@tonic-gate * - if "not supported", mark "ignored"
15057c478bd9Sstevel@tonic-gate * - if "supported" attempt negotiation and fill result in
15067c478bd9Sstevel@tonic-gate * the outcome
15077c478bd9Sstevel@tonic-gate * - if "absolute requirement", set "*is_absreq_failurep"
15087c478bd9Sstevel@tonic-gate * - if NOT an "absolute requirement", then our
15097c478bd9Sstevel@tonic-gate * interpretation is to mark is at ignored if
15107c478bd9Sstevel@tonic-gate * negotiation fails (Spec allows partial success
15117c478bd9Sstevel@tonic-gate * as in OSI protocols but not failure)
15127c478bd9Sstevel@tonic-gate *
15137c478bd9Sstevel@tonic-gate * Then delete "ignored" options from option buffer and return success.
15147c478bd9Sstevel@tonic-gate *
15157c478bd9Sstevel@tonic-gate */
15167c478bd9Sstevel@tonic-gate int
tpi_optcom_buf(queue_t * q,mblk_t * mp,t_scalar_t * opt_lenp,t_scalar_t opt_offset,cred_t * cr,optdb_obj_t * dbobjp,void * thisdg_attrs,int * is_absreq_failurep)15177c478bd9Sstevel@tonic-gate tpi_optcom_buf(queue_t *q, mblk_t *mp, t_scalar_t *opt_lenp,
15187c478bd9Sstevel@tonic-gate t_scalar_t opt_offset, cred_t *cr, optdb_obj_t *dbobjp,
15197c478bd9Sstevel@tonic-gate void *thisdg_attrs, int *is_absreq_failurep)
15207c478bd9Sstevel@tonic-gate {
15217c478bd9Sstevel@tonic-gate opt_set_fn setfn = dbobjp->odb_setfn;
15227c478bd9Sstevel@tonic-gate opdes_t *opt_arr = dbobjp->odb_opt_des_arr;
15237c478bd9Sstevel@tonic-gate uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
15247c478bd9Sstevel@tonic-gate struct T_opthdr *opt, *opt_start, *opt_end;
15257c478bd9Sstevel@tonic-gate mblk_t *copy_mp_head;
15267c478bd9Sstevel@tonic-gate uchar_t *optr, *init_optr;
15277c478bd9Sstevel@tonic-gate opdes_t *optd;
15287c478bd9Sstevel@tonic-gate uint_t optset_context;
15297c478bd9Sstevel@tonic-gate t_uscalar_t olen;
15307c478bd9Sstevel@tonic-gate int error = 0;
15317c478bd9Sstevel@tonic-gate
15327c478bd9Sstevel@tonic-gate ASSERT((uchar_t *)opt_lenp > mp->b_rptr &&
15337c478bd9Sstevel@tonic-gate (uchar_t *)opt_lenp < mp->b_wptr);
15347c478bd9Sstevel@tonic-gate
15357c478bd9Sstevel@tonic-gate copy_mp_head = NULL;
15367c478bd9Sstevel@tonic-gate *is_absreq_failurep = 0;
15377c478bd9Sstevel@tonic-gate switch (((union T_primitives *)mp->b_rptr)->type) {
15387c478bd9Sstevel@tonic-gate case T_CONN_REQ:
15397c478bd9Sstevel@tonic-gate case T_CONN_RES:
15407c478bd9Sstevel@tonic-gate optset_context = SETFN_CONN_NEGOTIATE;
15417c478bd9Sstevel@tonic-gate break;
15427c478bd9Sstevel@tonic-gate case T_UNITDATA_REQ:
15437c478bd9Sstevel@tonic-gate optset_context = SETFN_UD_NEGOTIATE;
15447c478bd9Sstevel@tonic-gate break;
15457c478bd9Sstevel@tonic-gate default:
15467c478bd9Sstevel@tonic-gate /*
15477c478bd9Sstevel@tonic-gate * should never get here, all possible TPI primitives
15487c478bd9Sstevel@tonic-gate * where this can be called from should be accounted
15497c478bd9Sstevel@tonic-gate * for in the cases above
15507c478bd9Sstevel@tonic-gate */
15517c478bd9Sstevel@tonic-gate return (EINVAL);
15527c478bd9Sstevel@tonic-gate }
15537c478bd9Sstevel@tonic-gate
15547c478bd9Sstevel@tonic-gate if ((opt_start = (struct T_opthdr *)
15557c478bd9Sstevel@tonic-gate mi_offset_param(mp, opt_offset, *opt_lenp)) == NULL) {
15567c478bd9Sstevel@tonic-gate error = ENOPROTOOPT;
15577c478bd9Sstevel@tonic-gate goto error_ret;
15587c478bd9Sstevel@tonic-gate }
15597c478bd9Sstevel@tonic-gate if (!__TPI_TOPT_ISALIGNED(opt_start)) {
15607c478bd9Sstevel@tonic-gate error = ENOPROTOOPT;
15617c478bd9Sstevel@tonic-gate goto error_ret;
15627c478bd9Sstevel@tonic-gate }
15637c478bd9Sstevel@tonic-gate
15647c478bd9Sstevel@tonic-gate opt_end = (struct T_opthdr *)((uchar_t *)opt_start
15657c478bd9Sstevel@tonic-gate + *opt_lenp);
15667c478bd9Sstevel@tonic-gate
15677c478bd9Sstevel@tonic-gate if ((copy_mp_head = copyb(mp)) == (mblk_t *)NULL) {
15687c478bd9Sstevel@tonic-gate error = ENOMEM;
15697c478bd9Sstevel@tonic-gate goto error_ret;
15707c478bd9Sstevel@tonic-gate }
15717c478bd9Sstevel@tonic-gate
15727c478bd9Sstevel@tonic-gate init_optr = optr = (uchar_t *)©_mp_head->b_rptr[opt_offset];
15737c478bd9Sstevel@tonic-gate
15747c478bd9Sstevel@tonic-gate for (opt = opt_start; opt && (opt < opt_end);
15757c478bd9Sstevel@tonic-gate opt = _TPI_TOPT_NEXTHDR(opt_start, *opt_lenp, opt)) {
15767c478bd9Sstevel@tonic-gate /*
15777c478bd9Sstevel@tonic-gate * Validate the option for length and alignment
15787c478bd9Sstevel@tonic-gate * before accessing anything in it
15797c478bd9Sstevel@tonic-gate */
15807c478bd9Sstevel@tonic-gate if (!_TPI_TOPT_VALID(opt, opt_start, opt_end)) {
15817c478bd9Sstevel@tonic-gate error = ENOPROTOOPT;
15827c478bd9Sstevel@tonic-gate goto error_ret;
15837c478bd9Sstevel@tonic-gate }
15847c478bd9Sstevel@tonic-gate
15857c478bd9Sstevel@tonic-gate /* Find the option in the opt_arr. */
15860f1702c5SYu Xiangning optd = proto_opt_lookup(opt->level, opt->name,
15877c478bd9Sstevel@tonic-gate opt_arr, opt_arr_cnt);
15887c478bd9Sstevel@tonic-gate
15897c478bd9Sstevel@tonic-gate if (optd == NULL) {
15907c478bd9Sstevel@tonic-gate /*
15917c478bd9Sstevel@tonic-gate * Option not found
15927c478bd9Sstevel@tonic-gate */
15937c478bd9Sstevel@tonic-gate opt->status = T_NOTSUPPORT;
15947c478bd9Sstevel@tonic-gate continue;
15957c478bd9Sstevel@tonic-gate }
15967c478bd9Sstevel@tonic-gate
15977c478bd9Sstevel@tonic-gate /*
15987c478bd9Sstevel@tonic-gate * Weird but as in XTI spec.
15997c478bd9Sstevel@tonic-gate * Sec 6.3.6 "Privileged and ReadOnly Options"
16007c478bd9Sstevel@tonic-gate * Permission problems (e.g.readonly) fail with bad access
16017c478bd9Sstevel@tonic-gate * BUT "privileged" option request from those NOT PRIVILEGED
16027c478bd9Sstevel@tonic-gate * are to be merely "ignored".
16037c478bd9Sstevel@tonic-gate * XXX Prevents "probing" of privileged options ?
16047c478bd9Sstevel@tonic-gate */
16057c478bd9Sstevel@tonic-gate if (OA_READONLY_PERMISSION(optd, cr)) {
16067c478bd9Sstevel@tonic-gate error = EACCES;
16077c478bd9Sstevel@tonic-gate goto error_ret;
16087c478bd9Sstevel@tonic-gate }
16097c478bd9Sstevel@tonic-gate if (OA_MATCHED_PRIV(optd, cr)) {
16107c478bd9Sstevel@tonic-gate /*
16117c478bd9Sstevel@tonic-gate * For privileged options, we DO perform
16127c478bd9Sstevel@tonic-gate * access checks as is common sense
16137c478bd9Sstevel@tonic-gate */
16147c478bd9Sstevel@tonic-gate if (!OA_WX_ANYPRIV(optd)) {
16157c478bd9Sstevel@tonic-gate error = EACCES;
16167c478bd9Sstevel@tonic-gate goto error_ret;
16177c478bd9Sstevel@tonic-gate }
16187c478bd9Sstevel@tonic-gate } else {
16197c478bd9Sstevel@tonic-gate /*
16207c478bd9Sstevel@tonic-gate * For non privileged, we fail instead following
16217c478bd9Sstevel@tonic-gate * "ignore" semantics dictated by XTI spec for
16227c478bd9Sstevel@tonic-gate * permissions problems.
16237c478bd9Sstevel@tonic-gate * Sec 6.3.6 "Privileged and ReadOnly Options"
16247c478bd9Sstevel@tonic-gate * XXX Should we do "ignore" semantics ?
16257c478bd9Sstevel@tonic-gate */
16267c478bd9Sstevel@tonic-gate if (!OA_WX_NOPRIV(optd)) { /* nopriv */
16277c478bd9Sstevel@tonic-gate opt->status = T_FAILURE;
16287c478bd9Sstevel@tonic-gate continue;
16297c478bd9Sstevel@tonic-gate }
16307c478bd9Sstevel@tonic-gate }
16317c478bd9Sstevel@tonic-gate /*
16327c478bd9Sstevel@tonic-gate *
16337c478bd9Sstevel@tonic-gate * If the negotiation fails, for options that
16347c478bd9Sstevel@tonic-gate * are "absolute requirement", it is a fatal error.
16357c478bd9Sstevel@tonic-gate * For options that are NOT "absolute requirements",
16367c478bd9Sstevel@tonic-gate * and the value fails to negotiate, the XTI spec
16377c478bd9Sstevel@tonic-gate * only considers the possibility of partial success
16387c478bd9Sstevel@tonic-gate * (T_PARTSUCCES - not likely for Internet protocols).
16397c478bd9Sstevel@tonic-gate * The spec is in denial about complete failure
16407c478bd9Sstevel@tonic-gate * (T_FAILURE) to negotiate for options that are
16417c478bd9Sstevel@tonic-gate * carried on T_CONN_REQ/T_CONN_RES/T_UNITDATA
16427c478bd9Sstevel@tonic-gate * We interpret the T_FAILURE to negotiate an option
16437c478bd9Sstevel@tonic-gate * that is NOT an absolute requirement that it is safe
16447c478bd9Sstevel@tonic-gate * to ignore it.
16457c478bd9Sstevel@tonic-gate */
16467c478bd9Sstevel@tonic-gate
16477c478bd9Sstevel@tonic-gate /* verify length */
1648*bd670b35SErik Nordmark if (opt->len < (t_uscalar_t)sizeof (struct T_opthdr) ||
1649*bd670b35SErik Nordmark !opt_length_ok(optd, opt->len - sizeof (struct T_opthdr))) {
16507c478bd9Sstevel@tonic-gate /* bad size */
16517c478bd9Sstevel@tonic-gate if ((optd->opdes_props & OP_NOT_ABSREQ) == 0) {
16527c478bd9Sstevel@tonic-gate /* option is absolute requirement */
16537c478bd9Sstevel@tonic-gate *is_absreq_failurep = 1;
16547c478bd9Sstevel@tonic-gate error = EINVAL;
16557c478bd9Sstevel@tonic-gate goto error_ret;
16567c478bd9Sstevel@tonic-gate }
16577c478bd9Sstevel@tonic-gate opt->status = T_FAILURE;
16587c478bd9Sstevel@tonic-gate continue;
16597c478bd9Sstevel@tonic-gate }
16607c478bd9Sstevel@tonic-gate
16617c478bd9Sstevel@tonic-gate /*
16627c478bd9Sstevel@tonic-gate * verified generic attributes. Now call set function.
16637c478bd9Sstevel@tonic-gate * Note: We assume the following to simplify code.
16647c478bd9Sstevel@tonic-gate * XXX If this is found not to be valid, this routine
16657c478bd9Sstevel@tonic-gate * will need to be rewritten. At this point it would
16667c478bd9Sstevel@tonic-gate * be premature to introduce more complexity than is
16677c478bd9Sstevel@tonic-gate * needed.
16687c478bd9Sstevel@tonic-gate * Assumption: For variable length options, we assume
16697c478bd9Sstevel@tonic-gate * that the value returned will be same or less length
16707c478bd9Sstevel@tonic-gate * (size does not increase). This makes it OK to pass the
16717c478bd9Sstevel@tonic-gate * same space for output as it is on input.
16727c478bd9Sstevel@tonic-gate */
16737c478bd9Sstevel@tonic-gate
16747c478bd9Sstevel@tonic-gate error = (*setfn)(q, optset_context, opt->level, opt->name,
16757c478bd9Sstevel@tonic-gate opt->len - (t_uscalar_t)sizeof (struct T_opthdr),
16767c478bd9Sstevel@tonic-gate _TPI_TOPT_DATA(opt), &olen, _TPI_TOPT_DATA(opt),
1677*bd670b35SErik Nordmark thisdg_attrs, cr);
16787c478bd9Sstevel@tonic-gate
16797c478bd9Sstevel@tonic-gate if (olen > (int)(opt->len - sizeof (struct T_opthdr))) {
16807c478bd9Sstevel@tonic-gate /*
16817c478bd9Sstevel@tonic-gate * Space on output more than space on input. Should
16827c478bd9Sstevel@tonic-gate * not happen and we consider it a bug/error.
16837c478bd9Sstevel@tonic-gate * More of a restriction than an error in our
16847c478bd9Sstevel@tonic-gate * implementation. Will see if we can live with this
16857c478bd9Sstevel@tonic-gate * otherwise code will get more hairy with multiple
16867c478bd9Sstevel@tonic-gate * passes.
16877c478bd9Sstevel@tonic-gate */
16887c478bd9Sstevel@tonic-gate error = EINVAL;
16897c478bd9Sstevel@tonic-gate goto error_ret;
16907c478bd9Sstevel@tonic-gate }
16917c478bd9Sstevel@tonic-gate if (error != 0) {
16927c478bd9Sstevel@tonic-gate if ((optd->opdes_props & OP_NOT_ABSREQ) == 0) {
16937c478bd9Sstevel@tonic-gate /* option is absolute requirement. */
16947c478bd9Sstevel@tonic-gate *is_absreq_failurep = 1;
16957c478bd9Sstevel@tonic-gate goto error_ret;
16967c478bd9Sstevel@tonic-gate }
16977c478bd9Sstevel@tonic-gate /*
16987c478bd9Sstevel@tonic-gate * failed - but option "not an absolute
16997c478bd9Sstevel@tonic-gate * requirement"
17007c478bd9Sstevel@tonic-gate */
17017c478bd9Sstevel@tonic-gate opt->status = T_FAILURE;
17027c478bd9Sstevel@tonic-gate continue;
17037c478bd9Sstevel@tonic-gate }
17047c478bd9Sstevel@tonic-gate /*
17057c478bd9Sstevel@tonic-gate * Fill in the only possible successful result
17067c478bd9Sstevel@tonic-gate * (Note: TPI allows for T_PARTSUCCESS - partial
17077c478bd9Sstevel@tonic-gate * sucess result code which is relevant in OSI world
17087c478bd9Sstevel@tonic-gate * and not possible in Internet code)
17097c478bd9Sstevel@tonic-gate */
17107c478bd9Sstevel@tonic-gate opt->status = T_SUCCESS;
17117c478bd9Sstevel@tonic-gate
17127c478bd9Sstevel@tonic-gate /*
17137c478bd9Sstevel@tonic-gate * Add T_SUCCESS result code options to the "output" options.
17147c478bd9Sstevel@tonic-gate * No T_FAILURES or T_NOTSUPPORT here as they are to be
17157c478bd9Sstevel@tonic-gate * ignored.
17167c478bd9Sstevel@tonic-gate * This code assumes output option buffer will
17177c478bd9Sstevel@tonic-gate * be <= input option buffer.
17187c478bd9Sstevel@tonic-gate *
17197c478bd9Sstevel@tonic-gate * Copy option header+value
17207c478bd9Sstevel@tonic-gate */
17217c478bd9Sstevel@tonic-gate bcopy(opt, optr, opt->len);
17227c478bd9Sstevel@tonic-gate optr += _TPI_ALIGN_TOPT(opt->len);
17237c478bd9Sstevel@tonic-gate }
17247c478bd9Sstevel@tonic-gate /*
17257c478bd9Sstevel@tonic-gate * Overwrite the input mblk option buffer now with the output
17267c478bd9Sstevel@tonic-gate * and update length, and contents in original mbl
17277c478bd9Sstevel@tonic-gate * (offset remains unchanged).
17287c478bd9Sstevel@tonic-gate */
17297c478bd9Sstevel@tonic-gate *opt_lenp = (t_scalar_t)(optr - init_optr);
17307c478bd9Sstevel@tonic-gate if (*opt_lenp > 0) {
17317c478bd9Sstevel@tonic-gate bcopy(init_optr, opt_start, *opt_lenp);
17327c478bd9Sstevel@tonic-gate }
17337c478bd9Sstevel@tonic-gate
17347c478bd9Sstevel@tonic-gate error_ret:
17357c478bd9Sstevel@tonic-gate if (copy_mp_head != NULL)
17367c478bd9Sstevel@tonic-gate freeb(copy_mp_head);
17377c478bd9Sstevel@tonic-gate return (error);
17387c478bd9Sstevel@tonic-gate }
17397c478bd9Sstevel@tonic-gate
17407c478bd9Sstevel@tonic-gate static boolean_t
opt_level_valid(t_uscalar_t level,optlevel_t * valid_level_arr,uint_t valid_level_arr_cnt)17417c478bd9Sstevel@tonic-gate opt_level_valid(t_uscalar_t level, optlevel_t *valid_level_arr,
17427c478bd9Sstevel@tonic-gate uint_t valid_level_arr_cnt)
17437c478bd9Sstevel@tonic-gate {
17447c478bd9Sstevel@tonic-gate optlevel_t *olp;
17457c478bd9Sstevel@tonic-gate
17467c478bd9Sstevel@tonic-gate for (olp = valid_level_arr;
17477c478bd9Sstevel@tonic-gate olp < &valid_level_arr[valid_level_arr_cnt];
17487c478bd9Sstevel@tonic-gate olp++) {
17497c478bd9Sstevel@tonic-gate if (level == (uint_t)(*olp))
17507c478bd9Sstevel@tonic-gate return (B_TRUE);
17517c478bd9Sstevel@tonic-gate }
17527c478bd9Sstevel@tonic-gate return (B_FALSE);
17537c478bd9Sstevel@tonic-gate }
17547c478bd9Sstevel@tonic-gate
17557c478bd9Sstevel@tonic-gate
17567c478bd9Sstevel@tonic-gate /*
17577c478bd9Sstevel@tonic-gate * Compute largest possible size for an option buffer containing
17587c478bd9Sstevel@tonic-gate * all options in one buffer.
17597c478bd9Sstevel@tonic-gate *
17607c478bd9Sstevel@tonic-gate * XXX TBD, investigate use of opt_bloated_maxsize() to avoid
17617c478bd9Sstevel@tonic-gate * wastefully large buffer allocation.
17627c478bd9Sstevel@tonic-gate */
17637c478bd9Sstevel@tonic-gate static size_t
opt_level_allopts_lengths(t_uscalar_t level,opdes_t * opt_arr,uint_t opt_arr_cnt)17647c478bd9Sstevel@tonic-gate opt_level_allopts_lengths(t_uscalar_t level, opdes_t *opt_arr,
17657c478bd9Sstevel@tonic-gate uint_t opt_arr_cnt)
17667c478bd9Sstevel@tonic-gate {
17677c478bd9Sstevel@tonic-gate opdes_t *optd;
17687c478bd9Sstevel@tonic-gate size_t allopt_len = 0; /* 0 implies no option at this level */
17697c478bd9Sstevel@tonic-gate
17707c478bd9Sstevel@tonic-gate /*
17717c478bd9Sstevel@tonic-gate * Scan opt_arr computing aggregate length
17727c478bd9Sstevel@tonic-gate * requirement for storing values of all
17737c478bd9Sstevel@tonic-gate * options.
17747c478bd9Sstevel@tonic-gate * Note: we do not filter for permissions
17757c478bd9Sstevel@tonic-gate * etc. This will be >= the real aggregate
17767c478bd9Sstevel@tonic-gate * length required (upper bound).
17777c478bd9Sstevel@tonic-gate */
17787c478bd9Sstevel@tonic-gate
17797c478bd9Sstevel@tonic-gate for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt];
17807c478bd9Sstevel@tonic-gate optd++) {
17817c478bd9Sstevel@tonic-gate if (level == optd->opdes_level) {
17827c478bd9Sstevel@tonic-gate allopt_len += sizeof (struct T_opthdr) +
17837c478bd9Sstevel@tonic-gate _TPI_ALIGN_TOPT(optd->opdes_size);
17847c478bd9Sstevel@tonic-gate }
17857c478bd9Sstevel@tonic-gate }
17867c478bd9Sstevel@tonic-gate return (allopt_len); /* 0 implies level not found */
17877c478bd9Sstevel@tonic-gate }
17887c478bd9Sstevel@tonic-gate
17897c478bd9Sstevel@tonic-gate /*
17907c478bd9Sstevel@tonic-gate * Compute largest possible size for an option buffer containing
17917c478bd9Sstevel@tonic-gate * all options in one buffer - a (theoretical?) worst case scenario
17927c478bd9Sstevel@tonic-gate * for certain cases.
17937c478bd9Sstevel@tonic-gate */
17947c478bd9Sstevel@tonic-gate t_uscalar_t
optcom_max_optbuf_len(opdes_t * opt_arr,uint_t opt_arr_cnt)17957c478bd9Sstevel@tonic-gate optcom_max_optbuf_len(opdes_t *opt_arr, uint_t opt_arr_cnt)
17967c478bd9Sstevel@tonic-gate {
17977c478bd9Sstevel@tonic-gate t_uscalar_t max_optbuf_len = sizeof (struct T_info_ack);
17987c478bd9Sstevel@tonic-gate opdes_t *optd;
17997c478bd9Sstevel@tonic-gate
18007c478bd9Sstevel@tonic-gate for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
18017c478bd9Sstevel@tonic-gate max_optbuf_len += (t_uscalar_t)sizeof (struct T_opthdr) +
18027c478bd9Sstevel@tonic-gate (t_uscalar_t)_TPI_ALIGN_TOPT(optd->opdes_size);
18037c478bd9Sstevel@tonic-gate }
18047c478bd9Sstevel@tonic-gate return (max_optbuf_len);
18057c478bd9Sstevel@tonic-gate }
18067c478bd9Sstevel@tonic-gate
18077c478bd9Sstevel@tonic-gate /*
18087c478bd9Sstevel@tonic-gate * Compute largest possible size for OPT_size for a transport.
18097c478bd9Sstevel@tonic-gate * Heuristic used is to add all but certain extremely large
18107c478bd9Sstevel@tonic-gate * size options; this is done by calling opt_bloated_maxsize().
18117c478bd9Sstevel@tonic-gate * It affects user level allocations in TLI/XTI code using t_alloc()
18127c478bd9Sstevel@tonic-gate * and other TLI/XTI implementation instance strucutures.
18137c478bd9Sstevel@tonic-gate * The large size options excluded are presumed to be
18147c478bd9Sstevel@tonic-gate * never accessed through the (theoretical?) worst case code paths
18157c478bd9Sstevel@tonic-gate * through TLI/XTI as they are currently IPv6 specific options.
18167c478bd9Sstevel@tonic-gate */
18177c478bd9Sstevel@tonic-gate
18187c478bd9Sstevel@tonic-gate t_uscalar_t
optcom_max_optsize(opdes_t * opt_arr,uint_t opt_arr_cnt)18197c478bd9Sstevel@tonic-gate optcom_max_optsize(opdes_t *opt_arr, uint_t opt_arr_cnt)
18207c478bd9Sstevel@tonic-gate {
18217c478bd9Sstevel@tonic-gate t_uscalar_t max_optbuf_len = sizeof (struct T_info_ack);
18227c478bd9Sstevel@tonic-gate opdes_t *optd;
18237c478bd9Sstevel@tonic-gate
18247c478bd9Sstevel@tonic-gate for (optd = opt_arr; optd < &opt_arr[opt_arr_cnt]; optd++) {
18257c478bd9Sstevel@tonic-gate if (!opt_bloated_maxsize(optd)) {
18267c478bd9Sstevel@tonic-gate max_optbuf_len +=
18277c478bd9Sstevel@tonic-gate (t_uscalar_t)sizeof (struct T_opthdr) +
18287c478bd9Sstevel@tonic-gate (t_uscalar_t)_TPI_ALIGN_TOPT(optd->opdes_size);
18297c478bd9Sstevel@tonic-gate }
18307c478bd9Sstevel@tonic-gate }
18317c478bd9Sstevel@tonic-gate return (max_optbuf_len);
18327c478bd9Sstevel@tonic-gate }
18337c478bd9Sstevel@tonic-gate
18347c478bd9Sstevel@tonic-gate /*
18357c478bd9Sstevel@tonic-gate * The theoretical model used in optcom_max_optsize() and
18367c478bd9Sstevel@tonic-gate * opt_level_allopts_lengths() accounts for the worst case of all
18377c478bd9Sstevel@tonic-gate * possible options for the theoretical cases and results in wasteful
18387c478bd9Sstevel@tonic-gate * memory allocations for certain theoretically correct usage scenarios.
18397c478bd9Sstevel@tonic-gate * In practice, the "features" they support are rarely, if ever,
18407c478bd9Sstevel@tonic-gate * used and even then only by test suites for those features (VSU, VST).
18417c478bd9Sstevel@tonic-gate * However, they result in large allocations due to the increased transport
18427c478bd9Sstevel@tonic-gate * T_INFO_ACK OPT_size field affecting t_alloc() users and TLI/XTI library
18437c478bd9Sstevel@tonic-gate * instance data structures for applications.
18447c478bd9Sstevel@tonic-gate *
18457c478bd9Sstevel@tonic-gate * The following routine opt_bloated_maxsize() supports a hack that avoids
18467c478bd9Sstevel@tonic-gate * paying the tax for the bloated options by excluding them and pretending
18477c478bd9Sstevel@tonic-gate * they don't exist for certain features without affecting features that
18487c478bd9Sstevel@tonic-gate * do use them.
18497c478bd9Sstevel@tonic-gate *
18507c478bd9Sstevel@tonic-gate * XXX Currently implemented only for optcom_max_optsize()
18517c478bd9Sstevel@tonic-gate * (to reduce risk late in release).
18527c478bd9Sstevel@tonic-gate * TBD for future, investigate use in optcom_level_allopts_lengths() and
18537c478bd9Sstevel@tonic-gate * all the instances of T_ALLOPT processing to exclude "bloated options".
18547c478bd9Sstevel@tonic-gate * Will not affect VSU/VST tests as they do not test with IPPROTO_IPV6
18557c478bd9Sstevel@tonic-gate * level options which are the only ones that fit the "bloated maxsize"
18567c478bd9Sstevel@tonic-gate * option profile now.
18577c478bd9Sstevel@tonic-gate */
18587c478bd9Sstevel@tonic-gate static boolean_t
opt_bloated_maxsize(opdes_t * optd)18597c478bd9Sstevel@tonic-gate opt_bloated_maxsize(opdes_t *optd)
18607c478bd9Sstevel@tonic-gate {
18617c478bd9Sstevel@tonic-gate if (optd->opdes_level != IPPROTO_IPV6)
18627c478bd9Sstevel@tonic-gate return (B_FALSE);
18637c478bd9Sstevel@tonic-gate switch (optd->opdes_name) {
18647c478bd9Sstevel@tonic-gate case IPV6_HOPOPTS:
18657c478bd9Sstevel@tonic-gate case IPV6_DSTOPTS:
18667c478bd9Sstevel@tonic-gate case IPV6_RTHDRDSTOPTS:
18677c478bd9Sstevel@tonic-gate case IPV6_RTHDR:
18687c478bd9Sstevel@tonic-gate case IPV6_PATHMTU:
18697c478bd9Sstevel@tonic-gate return (B_TRUE);
18707c478bd9Sstevel@tonic-gate default:
18717c478bd9Sstevel@tonic-gate break;
18727c478bd9Sstevel@tonic-gate }
18737c478bd9Sstevel@tonic-gate return (B_FALSE);
18747c478bd9Sstevel@tonic-gate }
18757c478bd9Sstevel@tonic-gate
1876*bd670b35SErik Nordmark /*
1877*bd670b35SErik Nordmark * optlen is the length of the option content
1878*bd670b35SErik Nordmark * Caller should check the optlen is at least sizeof (struct T_opthdr)
1879*bd670b35SErik Nordmark */
18807c478bd9Sstevel@tonic-gate static boolean_t
opt_length_ok(opdes_t * optd,t_uscalar_t optlen)1881*bd670b35SErik Nordmark opt_length_ok(opdes_t *optd, t_uscalar_t optlen)
18827c478bd9Sstevel@tonic-gate {
18837c478bd9Sstevel@tonic-gate /*
18847c478bd9Sstevel@tonic-gate * Verify length.
18857c478bd9Sstevel@tonic-gate * Value specified should match length of fixed length option or be
18867c478bd9Sstevel@tonic-gate * less than maxlen of variable length option.
18877c478bd9Sstevel@tonic-gate */
18887c478bd9Sstevel@tonic-gate if (optd->opdes_props & OP_VARLEN) {
1889*bd670b35SErik Nordmark if (optlen <= optd->opdes_size)
18907c478bd9Sstevel@tonic-gate return (B_TRUE);
18917c478bd9Sstevel@tonic-gate } else {
18927c478bd9Sstevel@tonic-gate /* fixed length option */
1893*bd670b35SErik Nordmark if (optlen == optd->opdes_size)
18947c478bd9Sstevel@tonic-gate return (B_TRUE);
18957c478bd9Sstevel@tonic-gate }
18967c478bd9Sstevel@tonic-gate return (B_FALSE);
18977c478bd9Sstevel@tonic-gate }
189845916cd2Sjpk
189945916cd2Sjpk /*
1900*bd670b35SErik Nordmark * This routine manages the allocation and free of the space for
1901*bd670b35SErik Nordmark * an extension header or option. Returns failure if memory
190245916cd2Sjpk * can not be allocated.
190345916cd2Sjpk */
190445916cd2Sjpk int
optcom_pkt_set(uchar_t * invalp,uint_t inlen,uchar_t ** optbufp,uint_t * optlenp)1905*bd670b35SErik Nordmark optcom_pkt_set(uchar_t *invalp, uint_t inlen,
1906*bd670b35SErik Nordmark uchar_t **optbufp, uint_t *optlenp)
190745916cd2Sjpk {
190845916cd2Sjpk uchar_t *optbuf;
190945916cd2Sjpk uchar_t *optp;
191045916cd2Sjpk
1911*bd670b35SErik Nordmark if (inlen == *optlenp) {
191245916cd2Sjpk /* Unchanged length - no need to reallocate */
1913*bd670b35SErik Nordmark optp = *optbufp;
191445916cd2Sjpk bcopy(invalp, optp, inlen);
191545916cd2Sjpk return (0);
191645916cd2Sjpk }
1917*bd670b35SErik Nordmark if (inlen > 0) {
191845916cd2Sjpk /* Allocate new buffer before free */
1919*bd670b35SErik Nordmark optbuf = kmem_alloc(inlen, KM_NOSLEEP);
192045916cd2Sjpk if (optbuf == NULL)
192145916cd2Sjpk return (ENOMEM);
192245916cd2Sjpk } else {
192345916cd2Sjpk optbuf = NULL;
192445916cd2Sjpk }
192545916cd2Sjpk
192645916cd2Sjpk /* Free old buffer */
192745916cd2Sjpk if (*optlenp != 0)
192845916cd2Sjpk kmem_free(*optbufp, *optlenp);
192945916cd2Sjpk
193045916cd2Sjpk if (inlen > 0)
1931*bd670b35SErik Nordmark bcopy(invalp, optbuf, inlen);
193245916cd2Sjpk
193345916cd2Sjpk *optbufp = optbuf;
1934*bd670b35SErik Nordmark *optlenp = inlen;
193545916cd2Sjpk return (0);
193645916cd2Sjpk }
19370f1702c5SYu Xiangning
19380f1702c5SYu Xiangning int
process_auxiliary_options(conn_t * connp,void * control,t_uscalar_t controllen,void * optbuf,optdb_obj_t * dbobjp,int (* opt_set_fn)(conn_t *,uint_t,int,int,uint_t,uchar_t *,uint_t *,uchar_t *,void *,cred_t *),cred_t * cr)19390f1702c5SYu Xiangning process_auxiliary_options(conn_t *connp, void *control, t_uscalar_t controllen,
1940*bd670b35SErik Nordmark void *optbuf, optdb_obj_t *dbobjp, int (*opt_set_fn)(conn_t *,
1941*bd670b35SErik Nordmark uint_t, int, int, uint_t, uchar_t *, uint_t *, uchar_t *, void *, cred_t *),
1942*bd670b35SErik Nordmark cred_t *cr)
19430f1702c5SYu Xiangning {
19440f1702c5SYu Xiangning struct cmsghdr *cmsg;
19450f1702c5SYu Xiangning opdes_t *optd;
19460f1702c5SYu Xiangning t_uscalar_t outlen;
19470f1702c5SYu Xiangning int error = EOPNOTSUPP;
19480f1702c5SYu Xiangning t_uscalar_t len;
19490f1702c5SYu Xiangning uint_t opt_arr_cnt = dbobjp->odb_opt_arr_cnt;
19500f1702c5SYu Xiangning opdes_t *opt_arr = dbobjp->odb_opt_des_arr;
19510f1702c5SYu Xiangning
19520f1702c5SYu Xiangning for (cmsg = (struct cmsghdr *)control;
19530f1702c5SYu Xiangning CMSG_VALID(cmsg, control, (uintptr_t)control + controllen);
19540f1702c5SYu Xiangning cmsg = CMSG_NEXT(cmsg)) {
19550f1702c5SYu Xiangning
19560f1702c5SYu Xiangning len = (t_uscalar_t)CMSG_CONTENTLEN(cmsg);
19570f1702c5SYu Xiangning /* Find the option in the opt_arr. */
19580f1702c5SYu Xiangning optd = proto_opt_lookup(cmsg->cmsg_level, cmsg->cmsg_type,
19590f1702c5SYu Xiangning opt_arr, opt_arr_cnt);
19600f1702c5SYu Xiangning if (optd == NULL) {
19610f1702c5SYu Xiangning return (EINVAL);
19620f1702c5SYu Xiangning }
1963de8c4a14SErik Nordmark if (OA_READONLY_PERMISSION(optd, cr)) {
19640f1702c5SYu Xiangning return (EACCES);
19650f1702c5SYu Xiangning }
1966de8c4a14SErik Nordmark if (OA_MATCHED_PRIV(optd, cr)) {
19670f1702c5SYu Xiangning /*
19680f1702c5SYu Xiangning * For privileged options, we DO perform
19690f1702c5SYu Xiangning * access checks as is common sense
19700f1702c5SYu Xiangning */
19710f1702c5SYu Xiangning if (!OA_WX_ANYPRIV(optd)) {
19720f1702c5SYu Xiangning return (EACCES);
19730f1702c5SYu Xiangning }
19740f1702c5SYu Xiangning } else {
19750f1702c5SYu Xiangning /*
19760f1702c5SYu Xiangning * For non privileged, we fail instead following
19770f1702c5SYu Xiangning * "ignore" semantics dictated by XTI spec for
19780f1702c5SYu Xiangning * permissions problems.
19790f1702c5SYu Xiangning */
19800f1702c5SYu Xiangning if (!OA_WX_NOPRIV(optd)) { /* nopriv */
19810f1702c5SYu Xiangning return (EACCES);
19820f1702c5SYu Xiangning }
19830f1702c5SYu Xiangning }
19840f1702c5SYu Xiangning error = opt_set_fn(connp, SETFN_UD_NEGOTIATE, optd->opdes_level,
19850f1702c5SYu Xiangning optd->opdes_name, len, (uchar_t *)CMSG_CONTENT(cmsg),
1986*bd670b35SErik Nordmark &outlen, (uchar_t *)CMSG_CONTENT(cmsg), optbuf, cr);
19870f1702c5SYu Xiangning if (error > 0) {
19880f1702c5SYu Xiangning return (error);
19890f1702c5SYu Xiangning } else if (outlen > len) {
19900f1702c5SYu Xiangning return (EINVAL);
19910f1702c5SYu Xiangning } else {
19920f1702c5SYu Xiangning /*
19930f1702c5SYu Xiangning * error can be -ve if the protocol wants to
19940f1702c5SYu Xiangning * pass the option to IP. We donot pass auxiliary
19950f1702c5SYu Xiangning * options to IP.
19960f1702c5SYu Xiangning */
19970f1702c5SYu Xiangning error = 0;
19980f1702c5SYu Xiangning }
19990f1702c5SYu Xiangning }
20000f1702c5SYu Xiangning return (error);
20010f1702c5SYu Xiangning }
2002