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