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