xref: /openbsd-src/usr.sbin/snmpd/smi.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
1 /*	$OpenBSD: smi.c,v 1.31 2022/06/30 09:42:19 martijn 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/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 #include "application.h"
50 
51 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
52 
53 RB_HEAD(oidtree, oid);
54 RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp);
55 struct oidtree smi_oidtree;
56 
57 RB_HEAD(keytree, oid);
58 RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp);
59 struct keytree smi_keytree;
60 
61 u_long
62 smi_getticks(void)
63 {
64 	struct timeval	 now, run;
65 	u_long		 ticks;
66 
67 	gettimeofday(&now, NULL);
68 	if (timercmp(&now, &snmpd_env->sc_starttime, <=))
69 		return (0);
70 	timersub(&now, &snmpd_env->sc_starttime, &run);
71 	ticks = run.tv_sec * 100;
72 	if (run.tv_usec)
73 		ticks += run.tv_usec / 10000;
74 
75 	return (ticks);
76 }
77 
78 void
79 smi_oidlen(struct ber_oid *o)
80 {
81 	size_t	 i;
82 
83 	for (i = 0; i < BER_MAX_OID_LEN && o->bo_id[i] != 0; i++)
84 		;
85 	o->bo_n = i;
86 }
87 
88 void
89 smi_scalar_oidlen(struct ber_oid *o)
90 {
91 	smi_oidlen(o);
92 
93 	/* Append .0. */
94 	if (o->bo_n < BER_MAX_OID_LEN)
95 		o->bo_n++;
96 }
97 
98 char *
99 smi_oid2string(struct ber_oid *o, char *buf, size_t len, size_t skip)
100 {
101 	char		 str[256];
102 	struct oid	*value, key;
103 	size_t		 i, lookup = 1;
104 
105 	bzero(buf, len);
106 	bzero(&key, sizeof(key));
107 	bcopy(o, &key.o_id, sizeof(struct ber_oid));
108 	key.o_flags |= OID_KEY;		/* do not match wildcards */
109 
110 	if (snmpd_env->sc_flags & SNMPD_F_NONAMES)
111 		lookup = 0;
112 
113 	for (i = 0; i < o->bo_n; i++) {
114 		key.o_oidlen = i + 1;
115 		if (lookup && skip > i)
116 			continue;
117 		if (lookup &&
118 		    (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL)
119 			snprintf(str, sizeof(str), "%s", value->o_name);
120 		else
121 			snprintf(str, sizeof(str), "%d", key.o_oid[i]);
122 		strlcat(buf, str, len);
123 		if (i < (o->bo_n - 1))
124 			strlcat(buf, ".", len);
125 	}
126 
127 	return (buf);
128 }
129 
130 int
131 smi_string2oid(const char *oidstr, struct ber_oid *o)
132 {
133 	char			*sp, *p, str[BUFSIZ];
134 	const char		*errstr;
135 	struct oid		*oid;
136 	struct ber_oid		 ko;
137 
138 	if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
139 		return (-1);
140 	bzero(o, sizeof(*o));
141 
142 	/*
143 	 * Parse OID strings in the common form n.n.n or n-n-n.
144 	 * Based on ober_string2oid with additional support for symbolic names.
145 	 */
146 	for (p = sp = str; p != NULL; sp = p) {
147 		if ((p = strpbrk(p, ".-")) != NULL)
148 			*p++ = '\0';
149 		if ((oid = smi_findkey(sp)) != NULL) {
150 			bcopy(&oid->o_id, &ko, sizeof(ko));
151 			if (o->bo_n && ober_oid_cmp(o, &ko) != 2)
152 				return (-1);
153 			bcopy(&ko, o, sizeof(*o));
154 			errstr = NULL;
155 		} else {
156 			o->bo_id[o->bo_n++] =
157 			    strtonum(sp, 0, UINT_MAX, &errstr);
158 		}
159 		if (errstr || o->bo_n > BER_MAX_OID_LEN)
160 			return (-1);
161 	}
162 
163 	return (0);
164 }
165 
166 void
167 smi_delete(struct oid *oid)
168 {
169 	struct oid	 key, *value;
170 
171 	bzero(&key, sizeof(key));
172 	bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid));
173 	if ((value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL &&
174 	    value == oid)
175 		RB_REMOVE(oidtree, &smi_oidtree, value);
176 
177 	free(oid->o_data);
178 	if (oid->o_flags & OID_DYNAMIC) {
179 		free(oid->o_name);
180 		free(oid);
181 	}
182 }
183 
184 int
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 		return (-1);
197 
198 	RB_INSERT(oidtree, &smi_oidtree, oid);
199 	return (0);
200 }
201 
202 void
203 smi_mibtree(struct oid *oids)
204 {
205 	struct oid	*oid, *decl;
206 	size_t		 i;
207 
208 	for (i = 0; oids[i].o_oid[0] != 0; i++) {
209 		oid = &oids[i];
210 		smi_oidlen(&oid->o_id);
211 		if (oid->o_name != NULL) {
212 			if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL)
213 				fatalx("smi_mibtree: invalid MIB table");
214 			RB_INSERT(oidtree, &smi_oidtree, oid);
215 			RB_INSERT(keytree, &smi_keytree, oid);
216 			continue;
217 		}
218 		decl = RB_FIND(oidtree, &smi_oidtree, oid);
219 		if (decl == NULL)
220 			fatalx("smi_mibtree: undeclared MIB");
221 		decl->o_flags = oid->o_flags;
222 		decl->o_get = oid->o_get;
223 		decl->o_set = oid->o_set;
224 		decl->o_table = oid->o_table;
225 		decl->o_val = oid->o_val;
226 		decl->o_data = oid->o_data;
227 	}
228 }
229 
230 int
231 smi_init(void)
232 {
233 	/* Initialize the Structure of Managed Information (SMI) */
234 	RB_INIT(&smi_oidtree);
235 	mib_init();
236 	return (0);
237 }
238 
239 struct oid *
240 smi_find(struct oid *oid)
241 {
242 	return (RB_FIND(oidtree, &smi_oidtree, oid));
243 }
244 
245 struct oid *
246 smi_nfind(struct oid *oid)
247 {
248 	return (RB_NFIND(oidtree, &smi_oidtree, oid));
249 }
250 
251 struct oid *
252 smi_findkey(char *name)
253 {
254 	struct oid	oid;
255 	if (name == NULL)
256 		return (NULL);
257 	oid.o_name = name;
258 	return (RB_FIND(keytree, &smi_keytree, &oid));
259 }
260 
261 struct oid *
262 smi_next(struct oid *oid)
263 {
264 	return (RB_NEXT(oidtree, &smi_oidtree, oid));
265 }
266 
267 struct oid *
268 smi_foreach(struct oid *oid, u_int flags)
269 {
270 	/*
271 	 * Traverse the tree of MIBs with the option to check
272 	 * for specific OID flags.
273 	 */
274 	if (oid == NULL) {
275 		oid = RB_MIN(oidtree, &smi_oidtree);
276 		if (oid == NULL)
277 			return (NULL);
278 		if (flags == 0 || (oid->o_flags & flags))
279 			return (oid);
280 	}
281 	for (;;) {
282 		oid = RB_NEXT(oidtree, &smi_oidtree, oid);
283 		if (oid == NULL)
284 			break;
285 		if (flags == 0 || (oid->o_flags & flags))
286 			return (oid);
287 	}
288 
289 	return (oid);
290 }
291 
292 #ifdef DEBUG
293 void
294 smi_debug_elements(struct ber_element *root)
295 {
296 	static int	 indent = 0;
297 	char		*value;
298 	int		 constructed;
299 
300 	/* calculate lengths */
301 	ober_calc_len(root);
302 
303 	switch (root->be_encoding) {
304 	case BER_TYPE_SEQUENCE:
305 	case BER_TYPE_SET:
306 		constructed = root->be_encoding;
307 		break;
308 	default:
309 		constructed = 0;
310 		break;
311 	}
312 
313 	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
314 	switch (root->be_class) {
315 	case BER_CLASS_UNIVERSAL:
316 		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
317 		switch (root->be_type) {
318 		case BER_TYPE_EOC:
319 			fprintf(stderr, "end-of-content");
320 			break;
321 		case BER_TYPE_INTEGER:
322 			fprintf(stderr, "integer");
323 			break;
324 		case BER_TYPE_BITSTRING:
325 			fprintf(stderr, "bit-string");
326 			break;
327 		case BER_TYPE_OCTETSTRING:
328 			fprintf(stderr, "octet-string");
329 			break;
330 		case BER_TYPE_NULL:
331 			fprintf(stderr, "null");
332 			break;
333 		case BER_TYPE_OBJECT:
334 			fprintf(stderr, "object");
335 			break;
336 		case BER_TYPE_ENUMERATED:
337 			fprintf(stderr, "enumerated");
338 			break;
339 		case BER_TYPE_SEQUENCE:
340 			fprintf(stderr, "sequence");
341 			break;
342 		case BER_TYPE_SET:
343 			fprintf(stderr, "set");
344 			break;
345 		}
346 		break;
347 	case BER_CLASS_APPLICATION:
348 		fprintf(stderr, "class: application(%u) type: ",
349 		    root->be_class);
350 		switch (root->be_type) {
351 		case SNMP_T_IPADDR:
352 			fprintf(stderr, "ipaddr");
353 			break;
354 		case SNMP_T_COUNTER32:
355 			fprintf(stderr, "counter32");
356 			break;
357 		case SNMP_T_GAUGE32:
358 			fprintf(stderr, "gauge32");
359 			break;
360 		case SNMP_T_TIMETICKS:
361 			fprintf(stderr, "timeticks");
362 			break;
363 		case SNMP_T_OPAQUE:
364 			fprintf(stderr, "opaque");
365 			break;
366 		case SNMP_T_COUNTER64:
367 			fprintf(stderr, "counter64");
368 			break;
369 		}
370 		break;
371 	case BER_CLASS_CONTEXT:
372 		fprintf(stderr, "class: context(%u) type: ",
373 		    root->be_class);
374 		switch (root->be_type) {
375 		case SNMP_C_GETREQ:
376 			fprintf(stderr, "getreq");
377 			break;
378 		case SNMP_C_GETNEXTREQ:
379 			fprintf(stderr, "getnextreq");
380 			break;
381 		case SNMP_C_RESPONSE:
382 			fprintf(stderr, "response");
383 			break;
384 		case SNMP_C_SETREQ:
385 			fprintf(stderr, "setreq");
386 			break;
387 		case SNMP_C_TRAP:
388 			fprintf(stderr, "trap");
389 			break;
390 		case SNMP_C_GETBULKREQ:
391 			fprintf(stderr, "getbulkreq");
392 			break;
393 		case SNMP_C_INFORMREQ:
394 			fprintf(stderr, "informreq");
395 			break;
396 		case SNMP_C_TRAPV2:
397 			fprintf(stderr, "trapv2");
398 			break;
399 		case SNMP_C_REPORT:
400 			fprintf(stderr, "report");
401 			break;
402 		}
403 		break;
404 	case BER_CLASS_PRIVATE:
405 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
406 		break;
407 	default:
408 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
409 		break;
410 	}
411 	fprintf(stderr, "(%u) encoding %u ",
412 	    root->be_type, root->be_encoding);
413 
414 	if ((value = smi_print_element(root)) == NULL)
415 		goto invalid;
416 
417 	switch (root->be_encoding) {
418 	case BER_TYPE_INTEGER:
419 	case BER_TYPE_ENUMERATED:
420 		fprintf(stderr, "value %s", value);
421 		break;
422 	case BER_TYPE_BITSTRING:
423 		fprintf(stderr, "hexdump %s", value);
424 		break;
425 	case BER_TYPE_OBJECT:
426 		fprintf(stderr, "oid %s", value);
427 		break;
428 	case BER_TYPE_OCTETSTRING:
429 		if (root->be_class == BER_CLASS_APPLICATION &&
430 		    root->be_type == SNMP_T_IPADDR) {
431 			fprintf(stderr, "addr %s", value);
432 		} else {
433 			fprintf(stderr, "string %s", value);
434 		}
435 		break;
436 	case BER_TYPE_NULL:	/* no payload */
437 	case BER_TYPE_EOC:
438 	case BER_TYPE_SEQUENCE:
439 	case BER_TYPE_SET:
440 	default:
441 		fprintf(stderr, "%s", value);
442 		break;
443 	}
444 
445  invalid:
446 	if (value == NULL)
447 		fprintf(stderr, "<INVALID>");
448 	else
449 		free(value);
450 	fprintf(stderr, "\n");
451 
452 	if (constructed)
453 		root->be_encoding = constructed;
454 
455 	if (constructed && root->be_sub) {
456 		indent += 2;
457 		smi_debug_elements(root->be_sub);
458 		indent -= 2;
459 	}
460 	if (root->be_next)
461 		smi_debug_elements(root->be_next);
462 }
463 #endif
464 
465 /* Keep around so trap handle scripts don't break */
466 char *
467 smi_print_element_legacy(struct ber_element *root)
468 {
469 	char		*str = NULL, *buf, *p;
470 	size_t		 len, i;
471 	long long	 v;
472 	struct ber_oid	 o;
473 	char		 strbuf[BUFSIZ];
474 
475 	switch (root->be_encoding) {
476 	case BER_TYPE_INTEGER:
477 	case BER_TYPE_ENUMERATED:
478 		if (ober_get_integer(root, &v) == -1)
479 			goto fail;
480 		if (asprintf(&str, "%lld", v) == -1)
481 			goto fail;
482 		break;
483 	case BER_TYPE_BITSTRING:
484 		if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
485 			goto fail;
486 		if ((str = calloc(1, len * 2 + 1)) == NULL)
487 			goto fail;
488 		for (p = str, i = 0; i < len; i++) {
489 			snprintf(p, 3, "%02x", buf[i]);
490 			p += 2;
491 		}
492 		break;
493 	case BER_TYPE_OBJECT:
494 		if (ober_get_oid(root, &o) == -1)
495 			goto fail;
496 		if (asprintf(&str, "%s",
497 		    smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1)
498 			goto fail;
499 		break;
500 	case BER_TYPE_OCTETSTRING:
501 		if (ober_get_string(root, &buf) == -1)
502 			goto fail;
503 		if (root->be_class == BER_CLASS_APPLICATION &&
504 		    root->be_type == SNMP_T_IPADDR) {
505 			if (asprintf(&str, "%s",
506 			    inet_ntoa(*(struct in_addr *)buf)) == -1)
507 				goto fail;
508 		} else {
509 			if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL)
510 				goto fail;
511 			strvisx(p, buf, root->be_len, VIS_NL);
512 			if (asprintf(&str, "\"%s\"", p) == -1) {
513 				free(p);
514 				goto fail;
515 			}
516 			free(p);
517 		}
518 		break;
519 	case BER_TYPE_NULL:	/* no payload */
520 	case BER_TYPE_EOC:
521 	case BER_TYPE_SEQUENCE:
522 	case BER_TYPE_SET:
523 	default:
524 		str = strdup("");
525 		break;
526 	}
527 
528 	return (str);
529 
530  fail:
531 	free(str);
532 	return (NULL);
533 }
534 
535 char *
536 smi_print_element(struct ber_element *root)
537 {
538 	char		*str = NULL, *buf, *p;
539 	long long	 v;
540 	struct ber_oid	 o;
541 	char		 strbuf[BUFSIZ];
542 
543 	switch (root->be_class) {
544 	case BER_CLASS_UNIVERSAL:
545 		switch (root->be_type) {
546 		case BER_TYPE_INTEGER:
547 			if (ober_get_integer(root, &v) == -1)
548 				goto fail;
549 			if (asprintf(&str, "%lld", v) == -1)
550 				goto fail;
551 			break;
552 		case BER_TYPE_OBJECT:
553 			if (ober_get_oid(root, &o) == -1)
554 				goto fail;
555 			if (asprintf(&str, "%s", smi_oid2string(&o, strbuf,
556 			    sizeof(strbuf), 0)) == -1)
557 				goto fail;
558 			break;
559 		case BER_TYPE_OCTETSTRING:
560 			if (ober_get_string(root, &buf) == -1)
561 				goto fail;
562 			p = reallocarray(NULL, 4, root->be_len + 1);
563 			if (p == NULL)
564 				goto fail;
565 			strvisx(p, buf, root->be_len, VIS_NL);
566 			if (asprintf(&str, "\"%s\"", p) == -1) {
567 				free(p);
568 				goto fail;
569 			}
570 			free(p);
571 			break;
572 		case BER_TYPE_NULL:
573 			if (asprintf(&str, "null") == -1)
574 				goto fail;
575 			break;
576 		default:
577 			/* Should not happen in a valid SNMP packet */
578 			if (asprintf(&str, "[U/%u]", root->be_type) == -1)
579 				goto fail;
580 			break;
581 		}
582 		break;
583 	case BER_CLASS_APPLICATION:
584 		switch (root->be_type) {
585 		case SNMP_T_IPADDR:
586 			if (ober_get_string(root, &buf) == -1)
587 				goto fail;
588 			if (asprintf(&str, "%s",
589 			    inet_ntoa(*(struct in_addr *)buf)) == -1)
590 					goto fail;
591 			break;
592 		case SNMP_T_COUNTER32:
593 			if (ober_get_integer(root, &v) == -1)
594 				goto fail;
595 			if (asprintf(&str, "%lld(c32)", v) == -1)
596 				goto fail;
597 			break;
598 		case SNMP_T_GAUGE32:
599 			if (ober_get_integer(root, &v) == -1)
600 				goto fail;
601 			if (asprintf(&str, "%lld(g32)", v) == -1)
602 				goto fail;
603 			break;
604 		case SNMP_T_TIMETICKS:
605 			if (ober_get_integer(root, &v) == -1)
606 				goto fail;
607 			if (asprintf(&str, "%lld.%llds", v/100, v%100) == -1)
608 				goto fail;
609 			break;
610 		case SNMP_T_OPAQUE:
611 			if (ober_get_string(root, &buf) == -1)
612 				goto fail;
613 			p = reallocarray(NULL, 4, root->be_len + 1);
614 			if (p == NULL)
615 				goto fail;
616 			strvisx(p, buf, root->be_len, VIS_NL);
617 			if (asprintf(&str, "\"%s\"(opaque)", p) == -1) {
618 				free(p);
619 				goto fail;
620 			}
621 			free(p);
622 			break;
623 		case SNMP_T_COUNTER64:
624 			if (ober_get_integer(root, &v) == -1)
625 				goto fail;
626 			if (asprintf(&str, "%lld(c64)", v) == -1)
627 				goto fail;
628 			break;
629 		default:
630 			/* Should not happen in a valid SNMP packet */
631 			if (asprintf(&str, "[A/%u]", root->be_type) == -1)
632 				goto fail;
633 			break;
634 		}
635 		break;
636 	case BER_CLASS_CONTEXT:
637 		switch (root->be_type) {
638 		case SNMP_V_NOSUCHOBJECT:
639 			str = strdup("noSuchObject");
640 			break;
641 		case SNMP_V_NOSUCHINSTANCE:
642 			str = strdup("noSuchInstance");
643 			break;
644 		case SNMP_V_ENDOFMIBVIEW:
645 			str = strdup("endOfMibView");
646 			break;
647 		default:
648 			/* Should not happen in a valid SNMP packet */
649 			if (asprintf(&str, "[C/%u]", root->be_type) == -1)
650 				goto fail;
651 			break;
652 		}
653 		break;
654 	default:
655 		/* Should not happen in a valid SNMP packet */
656 		if (asprintf(&str, "[%hhu/%u]", root->be_class,
657 		    root->be_type) == -1)
658 			goto fail;
659 		break;
660 	}
661 
662 	return (str);
663 
664  fail:
665 	free(str);
666 	return (NULL);
667 }
668 
669 unsigned int
670 smi_application(struct ber_element *elm)
671 {
672 	if (elm->be_class != BER_CLASS_APPLICATION)
673 		return (BER_TYPE_OCTETSTRING);
674 
675 	switch (elm->be_type) {
676 	case SNMP_T_IPADDR:
677 		return (BER_TYPE_OCTETSTRING);
678 	case SNMP_T_COUNTER32:
679 	case SNMP_T_GAUGE32:
680 	case SNMP_T_TIMETICKS:
681 	case SNMP_T_OPAQUE:
682 	case SNMP_T_COUNTER64:
683 		return (BER_TYPE_INTEGER);
684 	default:
685 		break;
686 	}
687 	return (BER_TYPE_OCTETSTRING);
688 }
689 
690 int
691 smi_oid_cmp(struct oid *a, struct oid *b)
692 {
693 	size_t	 i;
694 
695 	for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++)
696 		if (a->o_oid[i] != b->o_oid[i])
697 			return (a->o_oid[i] - b->o_oid[i]);
698 
699 	/*
700 	 * Return success if the matched object is a table
701 	 * or a MIB registered by a subagent
702 	 * (it will match any sub-elements)
703 	 */
704 	if ((b->o_flags & OID_TABLE ||
705 	    b->o_flags & OID_REGISTERED) &&
706 	    (a->o_flags & OID_KEY) == 0 &&
707 	    (a->o_oidlen > b->o_oidlen))
708 		return (0);
709 
710 	return (a->o_oidlen - b->o_oidlen);
711 }
712 
713 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp);
714 
715 int
716 smi_key_cmp(struct oid *a, struct oid *b)
717 {
718 	if (a->o_name == NULL || b->o_name == NULL)
719 		return (-1);
720 	return (strcasecmp(a->o_name, b->o_name));
721 }
722 
723 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp);
724