Annotation of src/usr.bin/snmp/smi.c, Revision 1.1
1.1 ! martijn 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
! 5: * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
! 6: *
! 7: * Permission to use, copy, modify, and distribute this software for any
! 8: * purpose with or without fee is hereby granted, provided that the above
! 9: * copyright notice and this permission notice appear in all copies.
! 10: *
! 11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 18: */
! 19:
! 20: #include <sys/limits.h>
! 21: #include <sys/tree.h>
! 22: #include <sys/queue.h>
! 23:
! 24: #include <arpa/inet.h>
! 25:
! 26: #include <ctype.h>
! 27: #include <stdlib.h>
! 28: #include <stdio.h>
! 29: #include <string.h>
! 30: #include <strings.h>
! 31:
! 32: #include "ber.h"
! 33: #include "mib.h"
! 34: #include "snmp.h"
! 35: #include "smi.h"
! 36:
! 37: #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
! 38:
! 39: int smi_oid_cmp(struct oid *, struct oid *);
! 40: int smi_key_cmp(struct oid *, struct oid *);
! 41: struct oid * smi_findkey(char *);
! 42:
! 43: RB_HEAD(oidtree, oid);
! 44: RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp)
! 45: struct oidtree smi_oidtree;
! 46:
! 47: RB_HEAD(keytree, oid);
! 48: RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp)
! 49: struct keytree smi_keytree;
! 50:
! 51: int
! 52: smi_init(void)
! 53: {
! 54: /* Initialize the Structure of Managed Information (SMI) */
! 55: RB_INIT(&smi_oidtree);
! 56: mib_init();
! 57: return (0);
! 58: }
! 59:
! 60: void
! 61: smi_debug_elements(struct ber_element *root)
! 62: {
! 63: static int indent = 0;
! 64: char *value;
! 65: int constructed;
! 66:
! 67: /* calculate lengths */
! 68: ber_calc_len(root);
! 69:
! 70: switch (root->be_encoding) {
! 71: case BER_TYPE_SEQUENCE:
! 72: case BER_TYPE_SET:
! 73: constructed = root->be_encoding;
! 74: break;
! 75: default:
! 76: constructed = 0;
! 77: break;
! 78: }
! 79:
! 80: fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
! 81: switch (root->be_class) {
! 82: case BER_CLASS_UNIVERSAL:
! 83: fprintf(stderr, "class: universal(%u) type: ", root->be_class);
! 84: switch (root->be_type) {
! 85: case BER_TYPE_EOC:
! 86: fprintf(stderr, "end-of-content");
! 87: break;
! 88: case BER_TYPE_BOOLEAN:
! 89: fprintf(stderr, "boolean");
! 90: break;
! 91: case BER_TYPE_INTEGER:
! 92: fprintf(stderr, "integer");
! 93: break;
! 94: case BER_TYPE_BITSTRING:
! 95: fprintf(stderr, "bit-string");
! 96: break;
! 97: case BER_TYPE_OCTETSTRING:
! 98: fprintf(stderr, "octet-string");
! 99: break;
! 100: case BER_TYPE_NULL:
! 101: fprintf(stderr, "null");
! 102: break;
! 103: case BER_TYPE_OBJECT:
! 104: fprintf(stderr, "object");
! 105: break;
! 106: case BER_TYPE_ENUMERATED:
! 107: fprintf(stderr, "enumerated");
! 108: break;
! 109: case BER_TYPE_SEQUENCE:
! 110: fprintf(stderr, "sequence");
! 111: break;
! 112: case BER_TYPE_SET:
! 113: fprintf(stderr, "set");
! 114: break;
! 115: }
! 116: break;
! 117: case BER_CLASS_APPLICATION:
! 118: fprintf(stderr, "class: application(%u) type: ",
! 119: root->be_class);
! 120: switch (root->be_type) {
! 121: case SNMP_T_IPADDR:
! 122: fprintf(stderr, "ipaddr");
! 123: break;
! 124: case SNMP_T_COUNTER32:
! 125: fprintf(stderr, "counter32");
! 126: break;
! 127: case SNMP_T_GAUGE32:
! 128: fprintf(stderr, "gauge32");
! 129: break;
! 130: case SNMP_T_TIMETICKS:
! 131: fprintf(stderr, "timeticks");
! 132: break;
! 133: case SNMP_T_OPAQUE:
! 134: fprintf(stderr, "opaque");
! 135: break;
! 136: case SNMP_T_COUNTER64:
! 137: fprintf(stderr, "counter64");
! 138: break;
! 139: }
! 140: break;
! 141: case BER_CLASS_CONTEXT:
! 142: fprintf(stderr, "class: context(%u) type: ",
! 143: root->be_class);
! 144: switch (root->be_type) {
! 145: case SNMP_C_GETREQ:
! 146: fprintf(stderr, "getreq");
! 147: break;
! 148: case SNMP_C_GETNEXTREQ:
! 149: fprintf(stderr, "nextreq");
! 150: break;
! 151: case SNMP_C_GETRESP:
! 152: fprintf(stderr, "getresp");
! 153: break;
! 154: case SNMP_C_SETREQ:
! 155: fprintf(stderr, "setreq");
! 156: break;
! 157: case SNMP_C_TRAP:
! 158: fprintf(stderr, "trap");
! 159: break;
! 160: case SNMP_C_GETBULKREQ:
! 161: fprintf(stderr, "getbulkreq");
! 162: break;
! 163: case SNMP_C_INFORMREQ:
! 164: fprintf(stderr, "informreq");
! 165: break;
! 166: case SNMP_C_TRAPV2:
! 167: fprintf(stderr, "trapv2");
! 168: break;
! 169: case SNMP_C_REPORT:
! 170: fprintf(stderr, "report");
! 171: break;
! 172: }
! 173: break;
! 174: case BER_CLASS_PRIVATE:
! 175: fprintf(stderr, "class: private(%u) type: ", root->be_class);
! 176: break;
! 177: default:
! 178: fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
! 179: break;
! 180: }
! 181: fprintf(stderr, "(%u) encoding %u ",
! 182: root->be_type, root->be_encoding);
! 183:
! 184: if ((value = smi_print_element(root, 1, smi_os_default, smi_oidl_numeric)) == NULL)
! 185: goto invalid;
! 186:
! 187: switch (root->be_encoding) {
! 188: case BER_TYPE_BOOLEAN:
! 189: fprintf(stderr, "%s", value);
! 190: break;
! 191: case BER_TYPE_INTEGER:
! 192: case BER_TYPE_ENUMERATED:
! 193: fprintf(stderr, "value %s", value);
! 194: break;
! 195: case BER_TYPE_BITSTRING:
! 196: fprintf(stderr, "hexdump %s", value);
! 197: break;
! 198: case BER_TYPE_OBJECT:
! 199: fprintf(stderr, "oid %s", value);
! 200: break;
! 201: case BER_TYPE_OCTETSTRING:
! 202: if (root->be_class == BER_CLASS_APPLICATION &&
! 203: root->be_type == SNMP_T_IPADDR) {
! 204: fprintf(stderr, "addr %s", value);
! 205: } else {
! 206: fprintf(stderr, "string %s", value);
! 207: }
! 208: break;
! 209: case BER_TYPE_NULL: /* no payload */
! 210: case BER_TYPE_EOC:
! 211: case BER_TYPE_SEQUENCE:
! 212: case BER_TYPE_SET:
! 213: default:
! 214: fprintf(stderr, "%s", value);
! 215: break;
! 216: }
! 217:
! 218: invalid:
! 219: if (value == NULL)
! 220: fprintf(stderr, "<INVALID>");
! 221: else
! 222: free(value);
! 223: fprintf(stderr, "\n");
! 224:
! 225: if (constructed)
! 226: root->be_encoding = constructed;
! 227:
! 228: if (constructed && root->be_sub) {
! 229: indent += 2;
! 230: smi_debug_elements(root->be_sub);
! 231: indent -= 2;
! 232: }
! 233: if (root->be_next)
! 234: smi_debug_elements(root->be_next);
! 235: }
! 236:
! 237: char *
! 238: smi_print_element(struct ber_element *root, int print_hint,
! 239: enum smi_output_string output_string, enum smi_oid_lookup lookup)
! 240: {
! 241: char *str = NULL, *buf, *p;
! 242: size_t len, i;
! 243: long long v, ticks;
! 244: int d;
! 245: int is_hex = 0;
! 246: struct ber_oid o;
! 247: char strbuf[BUFSIZ];
! 248: char *hint;
! 249: int days, hours, min, sec, csec;
! 250:
! 251: switch (root->be_encoding) {
! 252: case BER_TYPE_BOOLEAN:
! 253: if (ber_get_boolean(root, &d) == -1)
! 254: goto fail;
! 255: if (print_hint) {
! 256: if (asprintf(&str, "INTEGER: %s(%d)",
! 257: d ? "true" : "false", d) == -1)
! 258: goto fail;
! 259: }
! 260: else
! 261: if (asprintf(&str, "%s", d ? "true" : "false") == -1)
! 262: goto fail;
! 263: break;
! 264: case BER_TYPE_INTEGER:
! 265: case BER_TYPE_ENUMERATED:
! 266: if (ber_get_integer(root, &v) == -1)
! 267: goto fail;
! 268: if (root->be_class == BER_CLASS_APPLICATION &&
! 269: root->be_type == SNMP_T_TIMETICKS) {
! 270: ticks = v;
! 271: days = ticks / (60 * 60 * 24 * 100);
! 272: ticks %= (60 * 60 * 24 * 100);
! 273: hours = ticks / (60 * 60 * 100);
! 274: ticks %= (60 * 60 * 100);
! 275: min = ticks / (60 * 100);
! 276: ticks %= (60 * 100);
! 277: sec = ticks / 100;
! 278: ticks %= 100;
! 279: csec = ticks;
! 280:
! 281: if (print_hint) {
! 282: if (days == 0) {
! 283: if (asprintf(&str,
! 284: "Timeticks: (%lld) "
! 285: "%d:%02d:%02d.%02d",
! 286: v, hours, min, sec, csec) == -1)
! 287: goto fail;
! 288: } else if (days == 1) {
! 289: if (asprintf(&str,
! 290: "Timeticks: (%lld) "
! 291: "1 day %d:%02d:%02d.%02d",
! 292: v, hours, min, sec, csec) == -1)
! 293: goto fail;
! 294: } else {
! 295: if (asprintf(&str,
! 296: "Timeticks: (%lld) "
! 297: "%d day %d:%02d:%02d.%02d",
! 298: v, days, hours, min, sec, csec) ==
! 299: -1)
! 300: goto fail;
! 301: }
! 302: } else {
! 303: if (days == 0) {
! 304: if (asprintf(&str, "%d:%02d:%02d.%02d",
! 305: hours, min, sec, csec) == -1)
! 306: goto fail;
! 307: } else if (days == 1) {
! 308: if (asprintf(&str,
! 309: "1 day %d:%02d:%02d.%02d",
! 310: hours, min, sec, csec) == -1)
! 311: goto fail;
! 312: } else {
! 313: if (asprintf(&str,
! 314: "%d day %d:%02d:%02d.%02d",
! 315: days, hours, min, sec, csec) == -1)
! 316: goto fail;
! 317: }
! 318: }
! 319: break;
! 320: }
! 321: hint = "INTEGER: ";
! 322: if (root->be_class == BER_CLASS_APPLICATION) {
! 323: if (root->be_type == SNMP_T_COUNTER32)
! 324: hint = "Counter32: ";
! 325: else if (root->be_type == SNMP_T_GAUGE32)
! 326: hint = "Gauge32: ";
! 327: else if (root->be_type == SNMP_T_OPAQUE)
! 328: hint = "Opaque: ";
! 329: else if (root->be_type == SNMP_T_COUNTER64)
! 330: hint = "Counter64: ";
! 331: }
! 332: if (asprintf(&str, "%s%lld", print_hint ? hint : "", v)
! 333: == -1)
! 334: goto fail;
! 335: break;
! 336: case BER_TYPE_BITSTRING:
! 337: if (ber_get_bitstring(root, (void *)&buf, &len) == -1)
! 338: goto fail;
! 339: if ((str = calloc(1, len * 2 + 1 + sizeof("BITS: "))) == NULL)
! 340: goto fail;
! 341: p = str;
! 342: if (print_hint) {
! 343: strlcpy(str, "BITS: ", sizeof(str));
! 344: p += sizeof("BITS: ");
! 345: }
! 346: for (i = 0; i < len; i++) {
! 347: snprintf(p, 3, "%02x", buf[i]);
! 348: p += 2;
! 349: }
! 350: break;
! 351: case BER_TYPE_OBJECT:
! 352: if (ber_get_oid(root, &o) == -1)
! 353: goto fail;
! 354: if (asprintf(&str, "%s%s",
! 355: print_hint ? "OID: " : "",
! 356: smi_oid2string(&o, strbuf, sizeof(strbuf), lookup)) == -1)
! 357: goto fail;
! 358: break;
! 359: case BER_TYPE_OCTETSTRING:
! 360: if (ber_get_string(root, &buf) == -1)
! 361: goto fail;
! 362: if (root->be_class == BER_CLASS_APPLICATION &&
! 363: root->be_type == SNMP_T_IPADDR) {
! 364: if (asprintf(&str, "%s%s",
! 365: print_hint ? "IpAddress: " : "",
! 366: inet_ntoa(*(struct in_addr *)buf)) == -1)
! 367: goto fail;
! 368: } else if (root->be_class == BER_CLASS_CONTEXT &&
! 369: root->be_type == BER_TYPE_EOC) {
! 370: str = strdup("No Such Object available on this agent at this OID");
! 371:
! 372: }else {
! 373: for (i = 0; i < root->be_len; i++) {
! 374: if (!isprint(buf[i])) {
! 375: if (output_string == smi_os_default)
! 376: output_string = smi_os_hex;
! 377: else if (output_string == smi_os_ascii)
! 378: is_hex = 1;
! 379: break;
! 380: }
! 381: }
! 382: /*
! 383: * hex is 3 * n (2 digits + n - 1 spaces + NUL-byte)
! 384: * ascii can be max (2 * n) + 2 quotes + NUL-byte
! 385: */
! 386: if ((p = str = reallocarray(NULL,
! 387: output_string == smi_os_hex ? 3 : 2,
! 388: root->be_len + 2)) == NULL)
! 389: goto fail;
! 390: if (is_hex)
! 391: *str++ = '"';
! 392: for (i = 0; i < root->be_len; i++) {
! 393: switch (output_string) {
! 394: case smi_os_default:
! 395: /* FALLTHROUGH */
! 396: case smi_os_ascii:
! 397: /*
! 398: * There's probably more edgecases here,
! 399: * not fully investigated
! 400: */
! 401: if (is_hex && buf[i] == '\\')
! 402: *str++ = '\\';
! 403: *str++ = isprint(buf[i]) ? buf[i] : '.';
! 404: break;
! 405: case smi_os_hex:
! 406: sprintf(str, "%s%02hhX",
! 407: i == 0 ? "" :
! 408: i % 16 == 0 ? "\n" : " ", buf[i]);
! 409: str += i == 0 ? 2 : 3;
! 410: break;
! 411: }
! 412: }
! 413: if (is_hex)
! 414: *str++ = '"';
! 415: *str = '\0';
! 416: str = NULL;
! 417: if (asprintf(&str, "%s%s",
! 418: print_hint ?
! 419: output_string == smi_os_hex ? "Hex-STRING: " :
! 420: "STRING: " :
! 421: "", p) == -1) {
! 422: free(p);
! 423: goto fail;
! 424: }
! 425: free(p);
! 426: }
! 427: break;
! 428: case BER_TYPE_NULL: /* no payload */
! 429: case BER_TYPE_EOC:
! 430: case BER_TYPE_SEQUENCE:
! 431: case BER_TYPE_SET:
! 432: default:
! 433: str = strdup("");
! 434: break;
! 435: }
! 436:
! 437: return (str);
! 438:
! 439: fail:
! 440: free(str);
! 441: return (NULL);
! 442: }
! 443:
! 444: int
! 445: smi_string2oid(const char *oidstr, struct ber_oid *o)
! 446: {
! 447: char *sp, *p, str[BUFSIZ];
! 448: const char *errstr;
! 449: struct oid *oid;
! 450: struct ber_oid ko;
! 451:
! 452: if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
! 453: return (-1);
! 454: bzero(o, sizeof(*o));
! 455:
! 456: /*
! 457: * Parse OID strings in the common form n.n.n or n-n-n.
! 458: * Based on ber_string2oid with additional support for symbolic names.
! 459: */
! 460: p = sp = str[0] == '.' ? str + 1 : str;
! 461: for (; p != NULL; sp = p) {
! 462: if ((p = strpbrk(p, ".-")) != NULL)
! 463: *p++ = '\0';
! 464: if ((oid = smi_findkey(sp)) != NULL) {
! 465: bcopy(&oid->o_id, &ko, sizeof(ko));
! 466: if (o->bo_n && ber_oid_cmp(o, &ko) != 2)
! 467: return (-1);
! 468: bcopy(&ko, o, sizeof(*o));
! 469: errstr = NULL;
! 470: } else {
! 471: o->bo_id[o->bo_n++] =
! 472: strtonum(sp, 0, UINT_MAX, &errstr);
! 473: }
! 474: if (errstr || o->bo_n > BER_MAX_OID_LEN)
! 475: return (-1);
! 476: }
! 477:
! 478: return (0);
! 479: }
! 480:
! 481: unsigned int
! 482: smi_application(struct ber_element *elm)
! 483: {
! 484: if (elm->be_class != BER_CLASS_APPLICATION)
! 485: return (BER_TYPE_OCTETSTRING);
! 486:
! 487: switch (elm->be_type) {
! 488: case SNMP_T_IPADDR:
! 489: return (BER_TYPE_OCTETSTRING);
! 490: case SNMP_T_COUNTER32:
! 491: case SNMP_T_GAUGE32:
! 492: case SNMP_T_TIMETICKS:
! 493: case SNMP_T_OPAQUE:
! 494: case SNMP_T_COUNTER64:
! 495: return (BER_TYPE_INTEGER);
! 496: default:
! 497: break;
! 498: }
! 499: return (BER_TYPE_OCTETSTRING);
! 500:
! 501: }
! 502:
! 503: char *
! 504: smi_oid2string(struct ber_oid *o, char *buf, size_t len,
! 505: enum smi_oid_lookup lookup)
! 506: {
! 507: char str[256];
! 508: struct oid *value, key;
! 509: size_t i;
! 510:
! 511: bzero(buf, len);
! 512: bzero(&key, sizeof(key));
! 513: bcopy(o, &key.o_id, sizeof(struct ber_oid));
! 514: key.o_flags |= OID_KEY; /* do not match wildcards */
! 515:
! 516: for (i = 0; i < o->bo_n; i++) {
! 517: key.o_oidlen = i + 1;
! 518: if (lookup != smi_oidl_numeric &&
! 519: (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) {
! 520: snprintf(str, sizeof(str), "%s", value->o_name);
! 521: if (lookup == smi_oidl_short && i + 1 < o->bo_n) {
! 522: key.o_oidlen = i + 2;
! 523: if (RB_FIND(oidtree, &smi_oidtree, &key) != NULL)
! 524: continue;
! 525: }
! 526: } else
! 527: snprintf(str, sizeof(str), "%d", key.o_oid[i]);
! 528: if (*buf != '\0' || i == 0)
! 529: strlcat(buf, ".", len);
! 530: strlcat(buf, str, len);
! 531: }
! 532:
! 533: return (buf);
! 534: }
! 535:
! 536: void
! 537: smi_mibtree(struct oid *oids)
! 538: {
! 539: struct oid *oid, *decl;
! 540: size_t i;
! 541:
! 542: for (i = 0; oids[i].o_oid[0] != 0; i++) {
! 543: oid = &oids[i];
! 544: if (oid->o_name != NULL) {
! 545: RB_INSERT(oidtree, &smi_oidtree, oid);
! 546: RB_INSERT(keytree, &smi_keytree, oid);
! 547: continue;
! 548: }
! 549: decl = RB_FIND(oidtree, &smi_oidtree, oid);
! 550: decl->o_flags = oid->o_flags;
! 551: decl->o_get = oid->o_get;
! 552: decl->o_set = oid->o_set;
! 553: decl->o_table = oid->o_table;
! 554: decl->o_val = oid->o_val;
! 555: decl->o_data = oid->o_data;
! 556: }
! 557: }
! 558:
! 559: struct oid *
! 560: smi_findkey(char *name)
! 561: {
! 562: struct oid oid;
! 563: if (name == NULL)
! 564: return (NULL);
! 565: oid.o_name = name;
! 566: return (RB_FIND(keytree, &smi_keytree, &oid));
! 567: }
! 568:
! 569: struct oid *
! 570: smi_foreach(struct oid *oid, u_int flags)
! 571: {
! 572: /*
! 573: * Traverse the tree of MIBs with the option to check
! 574: * for specific OID flags.
! 575: */
! 576: if (oid == NULL) {
! 577: oid = RB_MIN(oidtree, &smi_oidtree);
! 578: if (oid == NULL)
! 579: return (NULL);
! 580: if (flags == 0 || (oid->o_flags & flags))
! 581: return (oid);
! 582: }
! 583: for (;;) {
! 584: oid = RB_NEXT(oidtree, &smi_oidtree, oid);
! 585: if (oid == NULL)
! 586: break;
! 587: if (flags == 0 || (oid->o_flags & flags))
! 588: return (oid);
! 589: }
! 590:
! 591: return (oid);
! 592: }
! 593:
! 594: int
! 595: smi_oid_cmp(struct oid *a, struct oid *b)
! 596: {
! 597: size_t i;
! 598:
! 599: for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) {
! 600: if (a->o_oid[i] != b->o_oid[i])
! 601: return (a->o_oid[i] - b->o_oid[i]);
! 602: }
! 603:
! 604: /*
! 605: * Return success if the matched object is a table
! 606: * or a MIB registered by a subagent
! 607: * (it will match any sub-elements)
! 608: */
! 609: if ((b->o_flags & OID_TABLE ||
! 610: b->o_flags & OID_REGISTERED) &&
! 611: (a->o_flags & OID_KEY) == 0 &&
! 612: (a->o_oidlen > b->o_oidlen))
! 613: return (0);
! 614:
! 615: return (a->o_oidlen - b->o_oidlen);
! 616: }
! 617:
! 618: int
! 619: smi_key_cmp(struct oid *a, struct oid *b)
! 620: {
! 621: if (a->o_name == NULL || b->o_name == NULL)
! 622: return (-1);
! 623: return (strcasecmp(a->o_name, b->o_name));
! 624: }
! 625:
! 626: RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp)
! 627: RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp)