1 /* $OpenBSD: smi.c,v 1.30 2021/10/21 15:08:15 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 50 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 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, &snmpd_env->sc_starttime, <=)) 68 return (0); 69 timersub(&now, &snmpd_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 (snmpd_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 ober_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 && ober_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 free(oid->o_data); 177 if (oid->o_flags & OID_DYNAMIC) { 178 free(oid->o_name); 179 free(oid); 180 } 181 } 182 183 int 184 smi_insert(struct oid *oid) 185 { 186 struct oid key, *value; 187 188 if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL) 189 fatalx("smi_insert: invalid MIB table"); 190 191 bzero(&key, sizeof(key)); 192 bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid)); 193 value = RB_FIND(oidtree, &smi_oidtree, &key); 194 if (value != NULL) 195 return (-1); 196 197 RB_INSERT(oidtree, &smi_oidtree, oid); 198 return (0); 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_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 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 struct ber_oid o; 471 char strbuf[BUFSIZ]; 472 473 switch (root->be_encoding) { 474 case BER_TYPE_INTEGER: 475 case BER_TYPE_ENUMERATED: 476 if (ober_get_integer(root, &v) == -1) 477 goto fail; 478 if (asprintf(&str, "%lld", v) == -1) 479 goto fail; 480 break; 481 case BER_TYPE_BITSTRING: 482 if (ober_get_bitstring(root, (void *)&buf, &len) == -1) 483 goto fail; 484 if ((str = calloc(1, len * 2 + 1)) == NULL) 485 goto fail; 486 for (p = str, i = 0; i < len; i++) { 487 snprintf(p, 3, "%02x", buf[i]); 488 p += 2; 489 } 490 break; 491 case BER_TYPE_OBJECT: 492 if (ober_get_oid(root, &o) == -1) 493 goto fail; 494 if (asprintf(&str, "%s", 495 smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1) 496 goto fail; 497 break; 498 case BER_TYPE_OCTETSTRING: 499 if (ober_get_string(root, &buf) == -1) 500 goto fail; 501 if (root->be_class == BER_CLASS_APPLICATION && 502 root->be_type == SNMP_T_IPADDR) { 503 if (asprintf(&str, "%s", 504 inet_ntoa(*(struct in_addr *)buf)) == -1) 505 goto fail; 506 } else { 507 if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL) 508 goto fail; 509 strvisx(p, buf, root->be_len, VIS_NL); 510 if (asprintf(&str, "\"%s\"", p) == -1) { 511 free(p); 512 goto fail; 513 } 514 free(p); 515 } 516 break; 517 case BER_TYPE_NULL: /* no payload */ 518 case BER_TYPE_EOC: 519 case BER_TYPE_SEQUENCE: 520 case BER_TYPE_SET: 521 default: 522 str = strdup(""); 523 break; 524 } 525 526 return (str); 527 528 fail: 529 free(str); 530 return (NULL); 531 } 532 533 unsigned int 534 smi_application(struct ber_element *elm) 535 { 536 if (elm->be_class != BER_CLASS_APPLICATION) 537 return (BER_TYPE_OCTETSTRING); 538 539 switch (elm->be_type) { 540 case SNMP_T_IPADDR: 541 return (BER_TYPE_OCTETSTRING); 542 case SNMP_T_COUNTER32: 543 case SNMP_T_GAUGE32: 544 case SNMP_T_TIMETICKS: 545 case SNMP_T_OPAQUE: 546 case SNMP_T_COUNTER64: 547 return (BER_TYPE_INTEGER); 548 default: 549 break; 550 } 551 return (BER_TYPE_OCTETSTRING); 552 } 553 554 int 555 smi_oid_cmp(struct oid *a, struct oid *b) 556 { 557 size_t i; 558 559 for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) 560 if (a->o_oid[i] != b->o_oid[i]) 561 return (a->o_oid[i] - b->o_oid[i]); 562 563 /* 564 * Return success if the matched object is a table 565 * or a MIB registered by a subagent 566 * (it will match any sub-elements) 567 */ 568 if ((b->o_flags & OID_TABLE || 569 b->o_flags & OID_REGISTERED) && 570 (a->o_flags & OID_KEY) == 0 && 571 (a->o_oidlen > b->o_oidlen)) 572 return (0); 573 574 return (a->o_oidlen - b->o_oidlen); 575 } 576 577 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp); 578 579 int 580 smi_key_cmp(struct oid *a, struct oid *b) 581 { 582 if (a->o_name == NULL || b->o_name == NULL) 583 return (-1); 584 return (strcasecmp(a->o_name, b->o_name)); 585 } 586 587 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp); 588