1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 /* Copyright (c) 1990 Mentat Inc. */ 25 26 /* 27 * This file contains common code for handling Options Management requests 28 * for SNMP/MIB. 29 */ 30 31 #include <sys/types.h> 32 #include <sys/stream.h> 33 #include <sys/stropts.h> 34 #include <sys/errno.h> 35 #define _SUN_TPI_VERSION 2 36 #include <sys/tihdr.h> 37 #include <sys/ddi.h> 38 #include <sys/cmn_err.h> 39 #include <sys/policy.h> 40 41 #include <sys/socket.h> 42 #include <netinet/in.h> 43 44 #include <inet/common.h> 45 #include <inet/mi.h> 46 #include <inet/mib2.h> 47 #include <inet/optcom.h> 48 #include <inet/snmpcom.h> 49 50 #include <inet/ip.h> 51 #include <sys/brand.h> 52 53 #define DEFAULT_LENGTH sizeof (long) 54 #define DATA_MBLK_SIZE 1024 55 #define TOAHDR_SIZE (sizeof (struct T_optmgmt_ack) +\ 56 sizeof (struct opthdr)) 57 58 /* SNMP Option Request Structure */ 59 typedef struct sor_s { 60 int sor_group; 61 int sor_code; /* MIB2 index value */ 62 int sor_size; 63 } sor_t; 64 65 /* 66 * Validation Table for set requests. 67 */ 68 static sor_t req_arr[] = { 69 { MIB2_IP, 1, sizeof (int) }, 70 { MIB2_IP, 2, sizeof (int) }, 71 { MIB2_IP, 21, sizeof (mib2_ipRouteEntry_t) }, 72 { MIB2_IP, 22, sizeof (mib2_ipNetToMediaEntry_t)}, 73 { MIB2_TCP, 13, sizeof (mib2_tcpConnEntry_t) } 74 }; 75 76 /* 77 * Binary compatibility to what used to be T_CURRENT in older releases. 78 * Unfortunately, the binary chosen for it was different and used by 79 * T_PARTSUCCESS in the new name space. However T_PARTSUCESS is only 80 * anticiapted in new T_OPTMGM_REQ (and not O_T_OPTMGMT_REQ messages). 81 * Only a test for TBADFLAG which uses one of the MIB option levels 82 * may have trouble with this provision for binary compatibility. 83 */ 84 #define OLD_T_CURRENT 0x100 /* same value as T_PARTSUCCESS */ 85 86 /* 87 * MIB info returned in data part of M_PROTO msg. All info for a single 88 * request is appended in a chain of mblk's off of the M_PROTO T_OPTMGMT_ACK 89 * ctl buffer. 90 */ 91 int 92 snmp_append_data(mblk_t *mpdata, char *blob, int len) 93 { 94 95 if (!mpdata) 96 return (0); 97 while (mpdata->b_cont) 98 mpdata = mpdata->b_cont; 99 if (mpdata->b_wptr + len >= mpdata->b_datap->db_lim) { 100 mpdata->b_cont = allocb(DATA_MBLK_SIZE, BPRI_HI); 101 mpdata = mpdata->b_cont; 102 if (!mpdata) 103 return (0); 104 } 105 bcopy(blob, (char *)mpdata->b_wptr, len); 106 mpdata->b_wptr += len; 107 return (1); 108 } 109 110 int 111 snmp_append_mblk(mblk_t *mpdata, mblk_t *mblk) 112 { 113 if (!mpdata || !mblk) 114 return (0); 115 while (mpdata->b_cont) 116 mpdata = mpdata->b_cont; 117 mpdata->b_cont = mblk; 118 return (1); 119 } 120 121 /* 122 * Need a form which avoids O(n^2) behavior locating the end of the 123 * chain every time. This is it. 124 */ 125 int 126 snmp_append_data2(mblk_t *mpdata, mblk_t **last_mpp, char *blob, int len) 127 { 128 129 if (!mpdata) 130 return (0); 131 if (*last_mpp == NULL) { 132 while (mpdata->b_cont) 133 mpdata = mpdata->b_cont; 134 *last_mpp = mpdata; 135 } 136 if ((*last_mpp)->b_wptr + len >= (*last_mpp)->b_datap->db_lim) { 137 (*last_mpp)->b_cont = allocb(DATA_MBLK_SIZE, BPRI_HI); 138 *last_mpp = (*last_mpp)->b_cont; 139 if (!*last_mpp) 140 return (0); 141 } 142 bcopy(blob, (char *)(*last_mpp)->b_wptr, len); 143 (*last_mpp)->b_wptr += len; 144 return (1); 145 } 146 147 int 148 snmp_append_mblk2(mblk_t *mpdata, mblk_t **last_mpp, mblk_t *mblk) 149 { 150 if (!mpdata || !mblk) 151 return (0); 152 if (*last_mpp == NULL) { 153 while (mpdata->b_cont) 154 mpdata = mpdata->b_cont; 155 *last_mpp = mpdata; 156 } 157 (*last_mpp)->b_cont = mblk; 158 *last_mpp = (*last_mpp)->b_cont; 159 return (1); 160 } 161 162 /* 163 * SNMP requests are issued using putmsg() on a stream containing all 164 * relevant modules. The ctl part contains a O_T_OPTMGMT_REQ message, 165 * and the data part is NULL 166 * to process this msg. If snmpcom_req() returns FALSE, then the module 167 * will try optcom_req to see if its some sort of SOCKET or IP option. 168 * snmpcom_req returns TRUE whenever the first option is recognized as 169 * an SNMP request, even if a bad one. 170 * 171 * "get" is done by a single O_T_OPTMGMT_REQ with MGMT_flags set to T_CURRENT. 172 * All modules respond with one or msg's about what they know. Responses 173 * are in T_OPTMGMT_ACK format. The opthdr level/name fields identify what 174 * is begin returned, the len field how big it is (in bytes). The info 175 * itself is in the data portion of the msg. Fixed length info returned 176 * in one msg; each table in a separate msg. 177 * 178 * setfn() returns 1 if things ok, 0 if set request invalid or otherwise 179 * messed up. 180 * 181 * If the passed q is at the bottom of the module chain (q_next == NULL, 182 * a ctl msg with req->name, level, len all zero is sent upstream. This 183 * is and EOD flag to the caller. 184 * 185 * IMPORTANT: 186 * - The msg type is M_PROTO, not M_PCPROTO!!! This is by design, 187 * since multiple messages will be sent to stream head and we want 188 * them queued for reading, not discarded. 189 * - All requests which match a table entry are sent to all get/set functions 190 * of each module. The functions must simply ignore requests not meant 191 * for them: getfn() returns 0, setfn() returns 1. 192 */ 193 boolean_t 194 snmpcom_req(queue_t *q, mblk_t *mp, pfi_t setfn, pfi_t getfn, cred_t *credp) 195 { 196 mblk_t *mpctl; 197 struct opthdr *req; 198 struct opthdr *next_req; 199 struct opthdr *req_end; 200 struct opthdr *req_start; 201 sor_t *sreq; 202 struct T_optmgmt_req *tor = (struct T_optmgmt_req *)mp->b_rptr; 203 struct T_optmgmt_ack *toa; 204 boolean_t legacy_req; 205 206 if (mp->b_cont) { /* don't deal with multiple mblk's */ 207 freemsg(mp->b_cont); 208 mp->b_cont = (mblk_t *)0; 209 optcom_err_ack(q, mp, TSYSERR, EBADMSG); 210 return (B_TRUE); 211 } 212 if ((mp->b_wptr - mp->b_rptr) < sizeof (struct T_optmgmt_req) || 213 !(req_start = (struct opthdr *)mi_offset_param(mp, 214 tor->OPT_offset, tor->OPT_length))) 215 goto bad_req1; 216 if (! __TPI_OPT_ISALIGNED(req_start)) 217 goto bad_req1; 218 219 /* 220 * if first option not in the MIB2 or EXPER range, return false so 221 * optcom_req can scope things out. Otherwise it's passed to each 222 * calling module to process or ignore as it sees fit. 223 */ 224 if ((!(req_start->level >= MIB2_RANGE_START && 225 req_start->level <= MIB2_RANGE_END)) && 226 (!(req_start->level >= EXPER_RANGE_START && 227 req_start->level <= EXPER_RANGE_END))) 228 return (B_FALSE); 229 230 switch (tor->MGMT_flags) { 231 232 case T_NEGOTIATE: 233 if (secpolicy_ip_config(credp, B_FALSE) != 0) { 234 optcom_err_ack(q, mp, TACCES, 0); 235 return (B_TRUE); 236 } 237 req_end = (struct opthdr *)((uchar_t *)req_start + 238 tor->OPT_length); 239 for (req = req_start; req < req_end; req = next_req) { 240 next_req = 241 (struct opthdr *)((uchar_t *)&req[1] + 242 _TPI_ALIGN_OPT(req->len)); 243 if (next_req > req_end) 244 goto bad_req2; 245 for (sreq = req_arr; sreq < A_END(req_arr); sreq++) { 246 if (req->level == sreq->sor_group && 247 req->name == sreq->sor_code) 248 break; 249 } 250 if (sreq >= A_END(req_arr)) 251 goto bad_req3; 252 if (!(*setfn)(q, req->level, req->name, 253 (uchar_t *)&req[1], req->len)) 254 goto bad_req4; 255 } 256 if (q->q_next != NULL) 257 putnext(q, mp); 258 else 259 freemsg(mp); 260 return (B_TRUE); 261 262 case OLD_T_CURRENT: 263 case T_CURRENT: 264 mpctl = allocb(TOAHDR_SIZE, BPRI_MED); 265 if (!mpctl) { 266 optcom_err_ack(q, mp, TSYSERR, ENOMEM); 267 return (B_TRUE); 268 } 269 mpctl->b_cont = allocb(DATA_MBLK_SIZE, BPRI_MED); 270 if (!mpctl->b_cont) { 271 freemsg(mpctl); 272 optcom_err_ack(q, mp, TSYSERR, ENOMEM); 273 return (B_TRUE); 274 } 275 mpctl->b_datap->db_type = M_PROTO; 276 mpctl->b_wptr += TOAHDR_SIZE; 277 toa = (struct T_optmgmt_ack *)mpctl->b_rptr; 278 toa->PRIM_type = T_OPTMGMT_ACK; 279 toa->OPT_offset = sizeof (struct T_optmgmt_ack); 280 toa->OPT_length = sizeof (struct opthdr); 281 toa->MGMT_flags = T_SUCCESS; 282 /* 283 * If the current process is running inside a solaris10- 284 * branded zone and len is 0 then it's a request for 285 * legacy data. 286 */ 287 if (PROC_IS_BRANDED(curproc) && 288 (strcmp(curproc->p_brand->b_name, "solaris10") == 0) && 289 (req_start->len == 0)) 290 legacy_req = B_TRUE; 291 else 292 legacy_req = B_FALSE; 293 if (!(*getfn)(q, mpctl, req_start->level, legacy_req)) 294 freemsg(mpctl); 295 /* 296 * all data for this module has now been sent upstream. If 297 * this is bottom module of stream, send up an EOD ctl msg, 298 * otherwise pass onto the next guy for processing. 299 */ 300 if (q->q_next != NULL) { 301 putnext(q, mp); 302 return (B_TRUE); 303 } 304 if (mp->b_cont) { 305 freemsg(mp->b_cont); 306 mp->b_cont = NULL; 307 } 308 mpctl = reallocb(mp, TOAHDR_SIZE, 1); 309 if (!mpctl) { 310 optcom_err_ack(q, mp, TSYSERR, ENOMEM); 311 return (B_TRUE); 312 } 313 mpctl->b_datap->db_type = M_PROTO; 314 mpctl->b_wptr = mpctl->b_rptr + TOAHDR_SIZE; 315 toa = (struct T_optmgmt_ack *)mpctl->b_rptr; 316 toa->PRIM_type = T_OPTMGMT_ACK; 317 toa->OPT_offset = sizeof (struct T_optmgmt_ack); 318 toa->OPT_length = sizeof (struct opthdr); 319 toa->MGMT_flags = T_SUCCESS; 320 req = (struct opthdr *)&toa[1]; 321 req->level = 0; 322 req->name = 0; 323 req->len = 0; 324 qreply(q, mpctl); 325 return (B_TRUE); 326 327 default: 328 optcom_err_ack(q, mp, TBADFLAG, 0); 329 return (B_TRUE); 330 } 331 332 bad_req1:; 333 printf("snmpcom bad_req1\n"); 334 goto bad_req; 335 bad_req2:; 336 printf("snmpcom bad_req2\n"); 337 goto bad_req; 338 bad_req3:; 339 printf("snmpcom bad_req3\n"); 340 goto bad_req; 341 bad_req4:; 342 printf("snmpcom bad_req4\n"); 343 /* FALLTHRU */ 344 bad_req:; 345 optcom_err_ack(q, mp, TBADOPT, 0); 346 return (B_TRUE); 347 348 }