xref: /openbsd-src/usr.sbin/snmpd/smi.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 
2 
3 /*
4  * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/queue.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <sys/tree.h>
25 #include <sys/sysctl.h>
26 
27 #include <net/if.h>
28 #include <net/if_dl.h>
29 #include <net/if_arp.h>
30 #include <net/if_media.h>
31 #include <net/route.h>
32 #include <netinet/in.h>
33 #include <netinet/if_ether.h>
34 #include <arpa/inet.h>
35 
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <event.h>
40 #include <fcntl.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <limits.h>
44 #include <pwd.h>
45 #include <vis.h>
46 
47 #include "snmpd.h"
48 #include "mib.h"
49 
50 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
51 
52 extern struct snmpd *env;
53 
54 RB_HEAD(oidtree, oid);
55 RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp);
56 struct oidtree smi_oidtree;
57 
58 RB_HEAD(keytree, oid);
59 RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp);
60 struct keytree smi_keytree;
61 
62 u_long
63 smi_getticks(void)
64 {
65 	struct timeval	 now, run;
66 	u_long		 ticks;
67 
68 	gettimeofday(&now, NULL);
69 	if (timercmp(&now, &env->sc_starttime, <=))
70 		return (0);
71 	timersub(&now, &env->sc_starttime, &run);
72 	ticks = run.tv_sec * 100;
73 	if (run.tv_usec)
74 		ticks += run.tv_usec / 10000;
75 
76 	return (ticks);
77 }
78 
79 void
80 smi_oidlen(struct ber_oid *o)
81 {
82 	size_t	 i;
83 
84 	for (i = 0; i < BER_MAX_OID_LEN && o->bo_id[i] != 0; i++)
85 		;
86 	o->bo_n = i;
87 }
88 
89 void
90 smi_scalar_oidlen(struct ber_oid *o)
91 {
92 	smi_oidlen(o);
93 
94 	/* Append .0. */
95 	if (o->bo_n < BER_MAX_OID_LEN)
96 		o->bo_n++;
97 }
98 
99 char *
100 smi_oid2string(struct ber_oid *o, char *buf, size_t len, size_t skip)
101 {
102 	char		 str[256];
103 	struct oid	*value, key;
104 	size_t		 i, lookup = 1;
105 
106 	bzero(buf, len);
107 	bzero(&key, sizeof(key));
108 	bcopy(o, &key.o_id, sizeof(struct ber_oid));
109 	key.o_flags |= OID_KEY;		/* do not match wildcards */
110 
111 	if (env->sc_flags & SNMPD_F_NONAMES)
112 		lookup = 0;
113 
114 	for (i = 0; i < o->bo_n; i++) {
115 		key.o_oidlen = i + 1;
116 		if (lookup && skip > i)
117 			continue;
118 		if (lookup &&
119 		    (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL)
120 			snprintf(str, sizeof(str), "%s", value->o_name);
121 		else
122 			snprintf(str, sizeof(str), "%d", key.o_oid[i]);
123 		strlcat(buf, str, len);
124 		if (i < (o->bo_n - 1))
125 			strlcat(buf, ".", len);
126 	}
127 
128 	return (buf);
129 }
130 
131 int
132 smi_string2oid(const char *oidstr, struct ber_oid *o)
133 {
134 	char			*sp, *p, str[BUFSIZ];
135 	const char		*errstr;
136 	struct oid		*oid;
137 	struct ber_oid		 ko;
138 
139 	if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
140 		return (-1);
141 	bzero(o, sizeof(*o));
142 
143 	/*
144 	 * Parse OID strings in the common form n.n.n or n-n-n.
145 	 * Based on ber_string2oid with additional support for symbolic names.
146 	 */
147 	for (p = sp = str; p != NULL; sp = p) {
148 		if ((p = strpbrk(p, ".-")) != NULL)
149 			*p++ = '\0';
150 		if ((oid = smi_findkey(sp)) != NULL) {
151 			bcopy(&oid->o_id, &ko, sizeof(ko));
152 			if (o->bo_n && ber_oid_cmp(o, &ko) != 2)
153 				return (-1);
154 			bcopy(&ko, o, sizeof(*o));
155 			errstr = NULL;
156 		} else {
157 			o->bo_id[o->bo_n++] =
158 			    strtonum(sp, 0, UINT_MAX, &errstr);
159 		}
160 		if (errstr || o->bo_n > BER_MAX_OID_LEN)
161 			return (-1);
162 	}
163 
164 	return (0);
165 }
166 
167 void
168 smi_delete(struct oid *oid)
169 {
170 	struct oid	 key, *value;
171 
172 	bzero(&key, sizeof(key));
173 	bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid));
174 	if ((value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL &&
175 	    value == oid)
176 		RB_REMOVE(oidtree, &smi_oidtree, value);
177 
178 	free(oid->o_data);
179 	if (oid->o_flags & OID_DYNAMIC) {
180 		free(oid->o_name);
181 		free(oid);
182 	}
183 }
184 
185 int
186 smi_insert(struct oid *oid)
187 {
188 	struct oid		 key, *value;
189 
190 	if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL)
191 		fatalx("smi_insert: invalid MIB table");
192 
193 	bzero(&key, sizeof(key));
194 	bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid));
195 	value = RB_FIND(oidtree, &smi_oidtree, &key);
196 	if (value != NULL)
197 		return (-1);
198 
199 	RB_INSERT(oidtree, &smi_oidtree, oid);
200 	return (0);
201 }
202 
203 void
204 smi_mibtree(struct oid *oids)
205 {
206 	struct oid	*oid, *decl;
207 	size_t		 i;
208 
209 	for (i = 0; oids[i].o_oid[0] != 0; i++) {
210 		oid = &oids[i];
211 		smi_oidlen(&oid->o_id);
212 		if (oid->o_name != NULL) {
213 			if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL)
214 				fatalx("smi_mibtree: invalid MIB table");
215 			RB_INSERT(oidtree, &smi_oidtree, oid);
216 			RB_INSERT(keytree, &smi_keytree, oid);
217 			continue;
218 		}
219 		decl = RB_FIND(oidtree, &smi_oidtree, oid);
220 		if (decl == NULL)
221 			fatalx("smi_mibtree: undeclared MIB");
222 		decl->o_flags = oid->o_flags;
223 		decl->o_get = oid->o_get;
224 		decl->o_set = oid->o_set;
225 		decl->o_table = oid->o_table;
226 		decl->o_val = oid->o_val;
227 		decl->o_data = oid->o_data;
228 	}
229 }
230 
231 int
232 smi_init(void)
233 {
234 	/* Initialize the Structure of Managed Information (SMI) */
235 	RB_INIT(&smi_oidtree);
236 	mib_init();
237 	return (0);
238 }
239 
240 struct oid *
241 smi_find(struct oid *oid)
242 {
243 	return (RB_FIND(oidtree, &smi_oidtree, oid));
244 }
245 
246 struct oid *
247 smi_findkey(char *name)
248 {
249 	struct oid	oid;
250 	if (name == NULL)
251 		return (NULL);
252 	oid.o_name = name;
253 	return (RB_FIND(keytree, &smi_keytree, &oid));
254 }
255 
256 struct oid *
257 smi_next(struct oid *oid)
258 {
259 	return (RB_NEXT(oidtree, &smi_oidtree, oid));
260 }
261 
262 struct oid *
263 smi_foreach(struct oid *oid, u_int flags)
264 {
265 	/*
266 	 * Traverse the tree of MIBs with the option to check
267 	 * for specific OID flags.
268 	 */
269 	if (oid == NULL) {
270 		oid = RB_MIN(oidtree, &smi_oidtree);
271 		if (oid == NULL)
272 			return (NULL);
273 		if (flags == 0 || (oid->o_flags & flags))
274 			return (oid);
275 	}
276 	for (;;) {
277 		oid = RB_NEXT(oidtree, &smi_oidtree, oid);
278 		if (oid == NULL)
279 			break;
280 		if (flags == 0 || (oid->o_flags & flags))
281 			return (oid);
282 	}
283 
284 	return (oid);
285 }
286 
287 #ifdef DEBUG
288 void
289 smi_debug_elements(struct ber_element *root)
290 {
291 	static int	 indent = 0;
292 	char		*value;
293 	int		 constructed;
294 
295 	/* calculate lengths */
296 	ber_calc_len(root);
297 
298 	switch (root->be_encoding) {
299 	case BER_TYPE_SEQUENCE:
300 	case BER_TYPE_SET:
301 		constructed = root->be_encoding;
302 		break;
303 	default:
304 		constructed = 0;
305 		break;
306 	}
307 
308 	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
309 	switch (root->be_class) {
310 	case BER_CLASS_UNIVERSAL:
311 		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
312 		switch (root->be_type) {
313 		case BER_TYPE_EOC:
314 			fprintf(stderr, "end-of-content");
315 			break;
316 		case BER_TYPE_BOOLEAN:
317 			fprintf(stderr, "boolean");
318 			break;
319 		case BER_TYPE_INTEGER:
320 			fprintf(stderr, "integer");
321 			break;
322 		case BER_TYPE_BITSTRING:
323 			fprintf(stderr, "bit-string");
324 			break;
325 		case BER_TYPE_OCTETSTRING:
326 			fprintf(stderr, "octet-string");
327 			break;
328 		case BER_TYPE_NULL:
329 			fprintf(stderr, "null");
330 			break;
331 		case BER_TYPE_OBJECT:
332 			fprintf(stderr, "object");
333 			break;
334 		case BER_TYPE_ENUMERATED:
335 			fprintf(stderr, "enumerated");
336 			break;
337 		case BER_TYPE_SEQUENCE:
338 			fprintf(stderr, "sequence");
339 			break;
340 		case BER_TYPE_SET:
341 			fprintf(stderr, "set");
342 			break;
343 		}
344 		break;
345 	case BER_CLASS_APPLICATION:
346 		fprintf(stderr, "class: application(%u) type: ",
347 		    root->be_class);
348 		switch (root->be_type) {
349 		case SNMP_T_IPADDR:
350 			fprintf(stderr, "ipaddr");
351 			break;
352 		case SNMP_T_COUNTER32:
353 			fprintf(stderr, "counter32");
354 			break;
355 		case SNMP_T_GAUGE32:
356 			fprintf(stderr, "gauge32");
357 			break;
358 		case SNMP_T_TIMETICKS:
359 			fprintf(stderr, "timeticks");
360 			break;
361 		case SNMP_T_OPAQUE:
362 			fprintf(stderr, "opaque");
363 			break;
364 		case SNMP_T_COUNTER64:
365 			fprintf(stderr, "counter64");
366 			break;
367 		}
368 		break;
369 	case BER_CLASS_CONTEXT:
370 		fprintf(stderr, "class: context(%u) type: ",
371 		    root->be_class);
372 		switch (root->be_type) {
373 		case SNMP_C_GETREQ:
374 			fprintf(stderr, "getreq");
375 			break;
376 		case SNMP_C_GETNEXTREQ:
377 			fprintf(stderr, "nextreq");
378 			break;
379 		case SNMP_C_GETRESP:
380 			fprintf(stderr, "getresp");
381 			break;
382 		case SNMP_C_SETREQ:
383 			fprintf(stderr, "setreq");
384 			break;
385 		case SNMP_C_TRAP:
386 			fprintf(stderr, "trap");
387 			break;
388 		case SNMP_C_GETBULKREQ:
389 			fprintf(stderr, "getbulkreq");
390 			break;
391 		case SNMP_C_INFORMREQ:
392 			fprintf(stderr, "informreq");
393 			break;
394 		case SNMP_C_TRAPV2:
395 			fprintf(stderr, "trapv2");
396 			break;
397 		case SNMP_C_REPORT:
398 			fprintf(stderr, "report");
399 			break;
400 		}
401 		break;
402 	case BER_CLASS_PRIVATE:
403 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
404 		break;
405 	default:
406 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
407 		break;
408 	}
409 	fprintf(stderr, "(%lu) encoding %lu ",
410 	    root->be_type, root->be_encoding);
411 
412 	if ((value = smi_print_element(root)) == NULL)
413 		goto invalid;
414 
415 	switch (root->be_encoding) {
416 	case BER_TYPE_BOOLEAN:
417 		fprintf(stderr, "%s", value);
418 		break;
419 	case BER_TYPE_INTEGER:
420 	case BER_TYPE_ENUMERATED:
421 		fprintf(stderr, "value %s", value);
422 		break;
423 	case BER_TYPE_BITSTRING:
424 		fprintf(stderr, "hexdump %s", value);
425 		break;
426 	case BER_TYPE_OBJECT:
427 		fprintf(stderr, "oid %s", value);
428 		break;
429 	case BER_TYPE_OCTETSTRING:
430 		if (root->be_class == BER_CLASS_APPLICATION &&
431 		    root->be_type == SNMP_T_IPADDR) {
432 			fprintf(stderr, "addr %s", value);
433 		} else {
434 			fprintf(stderr, "string %s", value);
435 		}
436 		break;
437 	case BER_TYPE_NULL:	/* no payload */
438 	case BER_TYPE_EOC:
439 	case BER_TYPE_SEQUENCE:
440 	case BER_TYPE_SET:
441 	default:
442 		fprintf(stderr, "%s", value);
443 		break;
444 	}
445 
446  invalid:
447 	if (value == NULL)
448 		fprintf(stderr, "<INVALID>");
449 	else
450 		free(value);
451 	fprintf(stderr, "\n");
452 
453 	if (constructed)
454 		root->be_encoding = constructed;
455 
456 	if (constructed && root->be_sub) {
457 		indent += 2;
458 		smi_debug_elements(root->be_sub);
459 		indent -= 2;
460 	}
461 	if (root->be_next)
462 		smi_debug_elements(root->be_next);
463 }
464 #endif
465 
466 char *
467 smi_print_element(struct ber_element *root)
468 {
469 	char		*str = NULL, *buf, *p;
470 	size_t		 len, i;
471 	long long	 v;
472 	int		 d;
473 	struct ber_oid	 o;
474 	char		 strbuf[BUFSIZ];
475 
476 	switch (root->be_encoding) {
477 	case BER_TYPE_BOOLEAN:
478 		if (ber_get_boolean(root, &d) == -1)
479 			goto fail;
480 		if (asprintf(&str, "%s(%d)", d ? "true" : "false", d) == -1)
481 			goto fail;
482 		break;
483 	case BER_TYPE_INTEGER:
484 	case BER_TYPE_ENUMERATED:
485 		if (ber_get_integer(root, &v) == -1)
486 			goto fail;
487 		if (asprintf(&str, "%lld", v) == -1)
488 			goto fail;
489 		break;
490 	case BER_TYPE_BITSTRING:
491 		if (ber_get_bitstring(root, (void *)&buf, &len) == -1)
492 			goto fail;
493 		if ((str = calloc(1, len * 2 + 1)) == NULL)
494 			goto fail;
495 		for (p = str, i = 0; i < len; i++) {
496 			snprintf(p, 3, "%02x", buf[i]);
497 			p += 2;
498 		}
499 		break;
500 	case BER_TYPE_OBJECT:
501 		if (ber_get_oid(root, &o) == -1)
502 			goto fail;
503 		if (asprintf(&str, "%s",
504 		    smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1)
505 			goto fail;
506 		break;
507 	case BER_TYPE_OCTETSTRING:
508 		if (ber_get_string(root, &buf) == -1)
509 			goto fail;
510 		if (root->be_class == BER_CLASS_APPLICATION &&
511 		    root->be_type == SNMP_T_IPADDR) {
512 			if (asprintf(&str, "%s",
513 			    inet_ntoa(*(struct in_addr *)buf)) == -1)
514 				goto fail;
515 		} else {
516 			if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL)
517 				goto fail;
518 			strvisx(p, buf, root->be_len, VIS_NL);
519 			if (asprintf(&str, "\"%s\"", p) == -1) {
520 				free(p);
521 				goto fail;
522 			}
523 			free(p);
524 		}
525 		break;
526 	case BER_TYPE_NULL:	/* no payload */
527 	case BER_TYPE_EOC:
528 	case BER_TYPE_SEQUENCE:
529 	case BER_TYPE_SET:
530 	default:
531 		str = strdup("");
532 		break;
533 	}
534 
535 	return (str);
536 
537  fail:
538 	free(str);
539 	return (NULL);
540 }
541 
542 unsigned long
543 smi_application(struct ber_element *elm)
544 {
545 	if (elm->be_class != BER_CLASS_APPLICATION)
546 		return (BER_TYPE_OCTETSTRING);
547 
548 	switch (elm->be_type) {
549 	case SNMP_T_IPADDR:
550 		return (BER_TYPE_OCTETSTRING);
551 	case SNMP_T_COUNTER32:
552 	case SNMP_T_GAUGE32:
553 	case SNMP_T_TIMETICKS:
554 	case SNMP_T_OPAQUE:
555 	case SNMP_T_COUNTER64:
556 		return (BER_TYPE_INTEGER);
557 	default:
558 		break;
559 	}
560 	return (BER_TYPE_OCTETSTRING);
561 }
562 
563 int
564 smi_oid_cmp(struct oid *a, struct oid *b)
565 {
566 	size_t	 i;
567 
568 	for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++)
569 		if (a->o_oid[i] != b->o_oid[i])
570 			return (a->o_oid[i] - b->o_oid[i]);
571 
572 	/*
573 	 * Return success if the matched object is a table
574 	 * or a MIB registered by a subagent
575 	 * (it will match any sub-elements)
576 	 */
577 	if ((b->o_flags & OID_TABLE ||
578 	    b->o_flags & OID_REGISTERED) &&
579 	    (a->o_flags & OID_KEY) == 0 &&
580 	    (a->o_oidlen > b->o_oidlen))
581 		return (0);
582 
583 	return (a->o_oidlen - b->o_oidlen);
584 }
585 
586 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp);
587 
588 int
589 smi_key_cmp(struct oid *a, struct oid *b)
590 {
591 	if (a->o_name == NULL || b->o_name == NULL)
592 		return (-1);
593 	return (strcasecmp(a->o_name, b->o_name));
594 }
595 
596 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp);
597