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