xref: /openbsd-src/usr.sbin/snmpd/smi.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: smi.c,v 1.32 2022/10/06 14:41:08 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_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_nfind(struct oid *oid)
246 {
247 	return (RB_NFIND(oidtree, &smi_oidtree, oid));
248 }
249 
250 struct oid *
251 smi_findkey(char *name)
252 {
253 	struct oid	oid;
254 	if (name == NULL)
255 		return (NULL);
256 	oid.o_name = name;
257 	return (RB_FIND(keytree, &smi_keytree, &oid));
258 }
259 
260 struct oid *
261 smi_next(struct oid *oid)
262 {
263 	return (RB_NEXT(oidtree, &smi_oidtree, oid));
264 }
265 
266 struct oid *
267 smi_foreach(struct oid *oid, u_int flags)
268 {
269 	/*
270 	 * Traverse the tree of MIBs with the option to check
271 	 * for specific OID flags.
272 	 */
273 	if (oid == NULL) {
274 		oid = RB_MIN(oidtree, &smi_oidtree);
275 		if (oid == NULL)
276 			return (NULL);
277 		if (flags == 0 || (oid->o_flags & flags))
278 			return (oid);
279 	}
280 	for (;;) {
281 		oid = RB_NEXT(oidtree, &smi_oidtree, oid);
282 		if (oid == NULL)
283 			break;
284 		if (flags == 0 || (oid->o_flags & flags))
285 			return (oid);
286 	}
287 
288 	return (oid);
289 }
290 
291 #ifdef DEBUG
292 void
293 smi_debug_elements(struct ber_element *root)
294 {
295 	static int	 indent = 0;
296 	char		*value;
297 	int		 constructed;
298 
299 	/* calculate lengths */
300 	ober_calc_len(root);
301 
302 	switch (root->be_encoding) {
303 	case BER_TYPE_SEQUENCE:
304 	case BER_TYPE_SET:
305 		constructed = root->be_encoding;
306 		break;
307 	default:
308 		constructed = 0;
309 		break;
310 	}
311 
312 	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
313 	switch (root->be_class) {
314 	case BER_CLASS_UNIVERSAL:
315 		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
316 		switch (root->be_type) {
317 		case BER_TYPE_EOC:
318 			fprintf(stderr, "end-of-content");
319 			break;
320 		case BER_TYPE_INTEGER:
321 			fprintf(stderr, "integer");
322 			break;
323 		case BER_TYPE_BITSTRING:
324 			fprintf(stderr, "bit-string");
325 			break;
326 		case BER_TYPE_OCTETSTRING:
327 			fprintf(stderr, "octet-string");
328 			break;
329 		case BER_TYPE_NULL:
330 			fprintf(stderr, "null");
331 			break;
332 		case BER_TYPE_OBJECT:
333 			fprintf(stderr, "object");
334 			break;
335 		case BER_TYPE_ENUMERATED:
336 			fprintf(stderr, "enumerated");
337 			break;
338 		case BER_TYPE_SEQUENCE:
339 			fprintf(stderr, "sequence");
340 			break;
341 		case BER_TYPE_SET:
342 			fprintf(stderr, "set");
343 			break;
344 		}
345 		break;
346 	case BER_CLASS_APPLICATION:
347 		fprintf(stderr, "class: application(%u) type: ",
348 		    root->be_class);
349 		switch (root->be_type) {
350 		case SNMP_T_IPADDR:
351 			fprintf(stderr, "ipaddr");
352 			break;
353 		case SNMP_T_COUNTER32:
354 			fprintf(stderr, "counter32");
355 			break;
356 		case SNMP_T_GAUGE32:
357 			fprintf(stderr, "gauge32");
358 			break;
359 		case SNMP_T_TIMETICKS:
360 			fprintf(stderr, "timeticks");
361 			break;
362 		case SNMP_T_OPAQUE:
363 			fprintf(stderr, "opaque");
364 			break;
365 		case SNMP_T_COUNTER64:
366 			fprintf(stderr, "counter64");
367 			break;
368 		}
369 		break;
370 	case BER_CLASS_CONTEXT:
371 		fprintf(stderr, "class: context(%u) type: ",
372 		    root->be_class);
373 		switch (root->be_type) {
374 		case SNMP_C_GETREQ:
375 			fprintf(stderr, "getreq");
376 			break;
377 		case SNMP_C_GETNEXTREQ:
378 			fprintf(stderr, "getnextreq");
379 			break;
380 		case SNMP_C_RESPONSE:
381 			fprintf(stderr, "response");
382 			break;
383 		case SNMP_C_SETREQ:
384 			fprintf(stderr, "setreq");
385 			break;
386 		case SNMP_C_TRAP:
387 			fprintf(stderr, "trap");
388 			break;
389 		case SNMP_C_GETBULKREQ:
390 			fprintf(stderr, "getbulkreq");
391 			break;
392 		case SNMP_C_INFORMREQ:
393 			fprintf(stderr, "informreq");
394 			break;
395 		case SNMP_C_TRAPV2:
396 			fprintf(stderr, "trapv2");
397 			break;
398 		case SNMP_C_REPORT:
399 			fprintf(stderr, "report");
400 			break;
401 		}
402 		break;
403 	case BER_CLASS_PRIVATE:
404 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
405 		break;
406 	default:
407 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
408 		break;
409 	}
410 	fprintf(stderr, "(%u) encoding %u ",
411 	    root->be_type, root->be_encoding);
412 
413 	if ((value = smi_print_element(root)) == NULL)
414 		goto invalid;
415 
416 	switch (root->be_encoding) {
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 /* Keep around so trap handle scripts don't break */
465 char *
466 smi_print_element_legacy(struct ber_element *root)
467 {
468 	char		*str = NULL, *buf, *p;
469 	size_t		 len, i;
470 	long long	 v;
471 	struct ber_oid	 o;
472 	char		 strbuf[BUFSIZ];
473 
474 	switch (root->be_encoding) {
475 	case BER_TYPE_INTEGER:
476 	case BER_TYPE_ENUMERATED:
477 		if (ober_get_integer(root, &v) == -1)
478 			goto fail;
479 		if (asprintf(&str, "%lld", v) == -1)
480 			goto fail;
481 		break;
482 	case BER_TYPE_BITSTRING:
483 		if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
484 			goto fail;
485 		if ((str = calloc(1, len * 2 + 1)) == NULL)
486 			goto fail;
487 		for (p = str, i = 0; i < len; i++) {
488 			snprintf(p, 3, "%02x", buf[i]);
489 			p += 2;
490 		}
491 		break;
492 	case BER_TYPE_OBJECT:
493 		if (ober_get_oid(root, &o) == -1)
494 			goto fail;
495 		if (asprintf(&str, "%s",
496 		    smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1)
497 			goto fail;
498 		break;
499 	case BER_TYPE_OCTETSTRING:
500 		if (ober_get_string(root, &buf) == -1)
501 			goto fail;
502 		if (root->be_class == BER_CLASS_APPLICATION &&
503 		    root->be_type == SNMP_T_IPADDR) {
504 			if (asprintf(&str, "%s",
505 			    inet_ntoa(*(struct in_addr *)buf)) == -1)
506 				goto fail;
507 		} else {
508 			if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL)
509 				goto fail;
510 			strvisx(p, buf, root->be_len, VIS_NL);
511 			if (asprintf(&str, "\"%s\"", p) == -1) {
512 				free(p);
513 				goto fail;
514 			}
515 			free(p);
516 		}
517 		break;
518 	case BER_TYPE_NULL:	/* no payload */
519 	case BER_TYPE_EOC:
520 	case BER_TYPE_SEQUENCE:
521 	case BER_TYPE_SET:
522 	default:
523 		str = strdup("");
524 		break;
525 	}
526 
527 	return (str);
528 
529  fail:
530 	free(str);
531 	return (NULL);
532 }
533 
534 char *
535 smi_print_element(struct ber_element *root)
536 {
537 	char		*str = NULL, *buf, *p;
538 	long long	 v;
539 	struct ber_oid	 o;
540 	char		 strbuf[BUFSIZ];
541 
542 	switch (root->be_class) {
543 	case BER_CLASS_UNIVERSAL:
544 		switch (root->be_type) {
545 		case BER_TYPE_INTEGER:
546 			if (ober_get_integer(root, &v) == -1)
547 				goto fail;
548 			if (asprintf(&str, "%lld", v) == -1)
549 				goto fail;
550 			break;
551 		case BER_TYPE_OBJECT:
552 			if (ober_get_oid(root, &o) == -1)
553 				goto fail;
554 			if (asprintf(&str, "%s", smi_oid2string(&o, strbuf,
555 			    sizeof(strbuf), 0)) == -1)
556 				goto fail;
557 			break;
558 		case BER_TYPE_OCTETSTRING:
559 			if (ober_get_string(root, &buf) == -1)
560 				goto fail;
561 			p = reallocarray(NULL, 4, root->be_len + 1);
562 			if (p == NULL)
563 				goto fail;
564 			strvisx(p, buf, root->be_len, VIS_NL);
565 			if (asprintf(&str, "\"%s\"", p) == -1) {
566 				free(p);
567 				goto fail;
568 			}
569 			free(p);
570 			break;
571 		case BER_TYPE_NULL:
572 			if (asprintf(&str, "null") == -1)
573 				goto fail;
574 			break;
575 		default:
576 			/* Should not happen in a valid SNMP packet */
577 			if (asprintf(&str, "[U/%u]", root->be_type) == -1)
578 				goto fail;
579 			break;
580 		}
581 		break;
582 	case BER_CLASS_APPLICATION:
583 		switch (root->be_type) {
584 		case SNMP_T_IPADDR:
585 			if (ober_get_string(root, &buf) == -1)
586 				goto fail;
587 			if (asprintf(&str, "%s",
588 			    inet_ntoa(*(struct in_addr *)buf)) == -1)
589 					goto fail;
590 			break;
591 		case SNMP_T_COUNTER32:
592 			if (ober_get_integer(root, &v) == -1)
593 				goto fail;
594 			if (asprintf(&str, "%lld(c32)", v) == -1)
595 				goto fail;
596 			break;
597 		case SNMP_T_GAUGE32:
598 			if (ober_get_integer(root, &v) == -1)
599 				goto fail;
600 			if (asprintf(&str, "%lld(g32)", v) == -1)
601 				goto fail;
602 			break;
603 		case SNMP_T_TIMETICKS:
604 			if (ober_get_integer(root, &v) == -1)
605 				goto fail;
606 			if (asprintf(&str, "%lld.%llds", v/100, v%100) == -1)
607 				goto fail;
608 			break;
609 		case SNMP_T_OPAQUE:
610 			if (ober_get_string(root, &buf) == -1)
611 				goto fail;
612 			p = reallocarray(NULL, 4, root->be_len + 1);
613 			if (p == NULL)
614 				goto fail;
615 			strvisx(p, buf, root->be_len, VIS_NL);
616 			if (asprintf(&str, "\"%s\"(opaque)", p) == -1) {
617 				free(p);
618 				goto fail;
619 			}
620 			free(p);
621 			break;
622 		case SNMP_T_COUNTER64:
623 			if (ober_get_integer(root, &v) == -1)
624 				goto fail;
625 			if (asprintf(&str, "%lld(c64)", v) == -1)
626 				goto fail;
627 			break;
628 		default:
629 			/* Should not happen in a valid SNMP packet */
630 			if (asprintf(&str, "[A/%u]", root->be_type) == -1)
631 				goto fail;
632 			break;
633 		}
634 		break;
635 	case BER_CLASS_CONTEXT:
636 		switch (root->be_type) {
637 		case SNMP_V_NOSUCHOBJECT:
638 			str = strdup("noSuchObject");
639 			break;
640 		case SNMP_V_NOSUCHINSTANCE:
641 			str = strdup("noSuchInstance");
642 			break;
643 		case SNMP_V_ENDOFMIBVIEW:
644 			str = strdup("endOfMibView");
645 			break;
646 		default:
647 			/* Should not happen in a valid SNMP packet */
648 			if (asprintf(&str, "[C/%u]", root->be_type) == -1)
649 				goto fail;
650 			break;
651 		}
652 		break;
653 	default:
654 		/* Should not happen in a valid SNMP packet */
655 		if (asprintf(&str, "[%hhu/%u]", root->be_class,
656 		    root->be_type) == -1)
657 			goto fail;
658 		break;
659 	}
660 
661 	return (str);
662 
663  fail:
664 	free(str);
665 	return (NULL);
666 }
667 
668 unsigned int
669 smi_application(struct ber_element *elm)
670 {
671 	if (elm->be_class != BER_CLASS_APPLICATION)
672 		return (BER_TYPE_OCTETSTRING);
673 
674 	switch (elm->be_type) {
675 	case SNMP_T_IPADDR:
676 		return (BER_TYPE_OCTETSTRING);
677 	case SNMP_T_COUNTER32:
678 	case SNMP_T_GAUGE32:
679 	case SNMP_T_TIMETICKS:
680 	case SNMP_T_OPAQUE:
681 	case SNMP_T_COUNTER64:
682 		return (BER_TYPE_INTEGER);
683 	default:
684 		break;
685 	}
686 	return (BER_TYPE_OCTETSTRING);
687 }
688 
689 int
690 smi_oid_cmp(struct oid *a, struct oid *b)
691 {
692 	size_t	 i;
693 
694 	for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++)
695 		if (a->o_oid[i] != b->o_oid[i])
696 			return (a->o_oid[i] - b->o_oid[i]);
697 
698 	/*
699 	 * Return success if the matched object is a table
700 	 * or a MIB registered by a subagent
701 	 * (it will match any sub-elements)
702 	 */
703 	if (b->o_flags & OID_TABLE &&
704 	    (a->o_flags & OID_KEY) == 0 &&
705 	    (a->o_oidlen > b->o_oidlen))
706 		return (0);
707 
708 	return (a->o_oidlen - b->o_oidlen);
709 }
710 
711 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp);
712 
713 int
714 smi_key_cmp(struct oid *a, struct oid *b)
715 {
716 	if (a->o_name == NULL || b->o_name == NULL)
717 		return (-1);
718 	return (strcasecmp(a->o_name, b->o_name));
719 }
720 
721 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp);
722