1/* 2 * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 3 * John Robert LoVerso. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * 28 * This implementation has been influenced by the CMU SNMP release, 29 * by Steve Waldbusser. However, this shares no code with that system. 30 * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_. 31 * Earlier forms of this implementation were derived and/or inspired by an 32 * awk script originally written by C. Philip Wood of LANL (but later 33 * heavily modified by John Robert LoVerso). The copyright notice for 34 * that work is preserved below, even though it may not rightly apply 35 * to this file. 36 * 37 * Support for SNMPv2c/SNMPv3 and the ability to link the module against 38 * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999. 39 * 40 * This started out as a very simple program, but the incremental decoding 41 * (into the BE structure) complicated things. 42 * 43 # Los Alamos National Laboratory 44 # 45 # Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997 46 # This software was produced under a U.S. Government contract 47 # (W-7405-ENG-36) by Los Alamos National Laboratory, which is 48 # operated by the University of California for the U.S. Department 49 # of Energy. The U.S. Government is licensed to use, reproduce, 50 # and distribute this software. Permission is granted to the 51 # public to copy and use this software without charge, provided 52 # that this Notice and any statement of authorship are reproduced 53 # on all copies. Neither the Government nor the University makes 54 # any warranty, express or implied, or assumes any liability or 55 # responsibility for the use of this software. 56 # @(#)snmp.awk.x 1.1 (LANL) 1/15/90 57 */ 58 59#include <sys/cdefs.h> 60#ifndef lint 61__RCSID("$NetBSD: print-snmp.c,v 1.7 2023/08/17 20:19:40 christos Exp $"); 62#endif 63 64/* \summary: Simple Network Management Protocol (SNMP) printer */ 65 66#ifdef HAVE_CONFIG_H 67#include <config.h> 68#endif 69 70#include "netdissect-stdinc.h" 71 72#include <stdio.h> 73#include <string.h> 74 75#ifdef USE_LIBSMI 76#include <smi.h> 77#endif 78 79#include "netdissect-ctype.h" 80 81#include "netdissect.h" 82#include "extract.h" 83 84#undef OPAQUE /* defined in <wingdi.h> */ 85 86 87/* 88 * Universal ASN.1 types 89 * (we only care about the tag values for those allowed in the Internet SMI) 90 */ 91static const char *Universal[] = { 92 "U-0", 93 "Boolean", 94 "Integer", 95#define INTEGER 2 96 "Bitstring", 97 "String", 98#define STRING 4 99 "Null", 100#define ASN_NULL 5 101 "ObjID", 102#define OBJECTID 6 103 "ObjectDes", 104 "U-8","U-9","U-10","U-11", /* 8-11 */ 105 "U-12","U-13","U-14","U-15", /* 12-15 */ 106 "Sequence", 107#define SEQUENCE 16 108 "Set" 109}; 110 111/* 112 * Application-wide ASN.1 types from the Internet SMI and their tags 113 */ 114static const char *Application[] = { 115 "IpAddress", 116#define IPADDR 0 117 "Counter", 118#define COUNTER 1 119 "Gauge", 120#define GAUGE 2 121 "TimeTicks", 122#define TIMETICKS 3 123 "Opaque", 124#define OPAQUE 4 125 "C-5", 126 "Counter64" 127#define COUNTER64 6 128}; 129 130/* 131 * Context-specific ASN.1 types for the SNMP PDUs and their tags 132 */ 133static const char *Context[] = { 134 "GetRequest", 135#define GETREQ 0 136 "GetNextRequest", 137#define GETNEXTREQ 1 138 "GetResponse", 139#define GETRESP 2 140 "SetRequest", 141#define SETREQ 3 142 "Trap", 143#define TRAP 4 144 "GetBulk", 145#define GETBULKREQ 5 146 "Inform", 147#define INFORMREQ 6 148 "V2Trap", 149#define V2TRAP 7 150 "Report" 151#define REPORT 8 152}; 153 154#define NOTIFY_CLASS(x) (x == TRAP || x == V2TRAP || x == INFORMREQ) 155#define READ_CLASS(x) (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ) 156#define WRITE_CLASS(x) (x == SETREQ) 157#define RESPONSE_CLASS(x) (x == GETRESP) 158#define INTERNAL_CLASS(x) (x == REPORT) 159 160/* 161 * Context-specific ASN.1 types for the SNMP Exceptions and their tags 162 */ 163static const char *Exceptions[] = { 164 "noSuchObject", 165#define NOSUCHOBJECT 0 166 "noSuchInstance", 167#define NOSUCHINSTANCE 1 168 "endOfMibView", 169#define ENDOFMIBVIEW 2 170}; 171 172/* 173 * Private ASN.1 types 174 * The Internet SMI does not specify any 175 */ 176static const char *Private[] = { 177 "P-0" 178}; 179 180/* 181 * error-status values for any SNMP PDU 182 */ 183static const char *ErrorStatus[] = { 184 "noError", 185 "tooBig", 186 "noSuchName", 187 "badValue", 188 "readOnly", 189 "genErr", 190 "noAccess", 191 "wrongType", 192 "wrongLength", 193 "wrongEncoding", 194 "wrongValue", 195 "noCreation", 196 "inconsistentValue", 197 "resourceUnavailable", 198 "commitFailed", 199 "undoFailed", 200 "authorizationError", 201 "notWritable", 202 "inconsistentName" 203}; 204#define DECODE_ErrorStatus(e) \ 205 ( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \ 206 ? ErrorStatus[e] \ 207 : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf)) 208 209/* 210 * generic-trap values in the SNMP Trap-PDU 211 */ 212static const char *GenericTrap[] = { 213 "coldStart", 214 "warmStart", 215 "linkDown", 216 "linkUp", 217 "authenticationFailure", 218 "egpNeighborLoss", 219 "enterpriseSpecific" 220#define GT_ENTERPRISE 6 221}; 222#define DECODE_GenericTrap(t) \ 223 ( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \ 224 ? GenericTrap[t] \ 225 : (snprintf(buf, sizeof(buf), "gt=%d", t), buf)) 226 227/* 228 * ASN.1 type class table 229 * Ties together the preceding Universal, Application, Context, and Private 230 * type definitions. 231 */ 232#define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */ 233static const struct { 234 const char *name; 235 const char **Id; 236 int numIDs; 237 } Class[] = { 238 defineCLASS(Universal), 239#define UNIVERSAL 0 240 defineCLASS(Application), 241#define APPLICATION 1 242 defineCLASS(Context), 243#define CONTEXT 2 244 defineCLASS(Private), 245#define PRIVATE 3 246 defineCLASS(Exceptions), 247#define EXCEPTIONS 4 248}; 249 250/* 251 * defined forms for ASN.1 types 252 */ 253static const char *Form[] = { 254 "Primitive", 255#define PRIMITIVE 0 256 "Constructed", 257#define CONSTRUCTED 1 258}; 259 260/* 261 * A structure for the OID tree for the compiled-in MIB. 262 * This is stored as a general-order tree. 263 */ 264static struct obj { 265 const char *desc; /* name of object */ 266 u_char oid; /* sub-id following parent */ 267 u_char type; /* object type (unused) */ 268 struct obj *child, *next; /* child and next sibling pointers */ 269} *objp = NULL; 270 271/* 272 * Include the compiled in SNMP MIB. "mib.h" is produced by feeding 273 * RFC-1156 format files into "makemib". "mib.h" MUST define at least 274 * a value for `mibroot'. 275 * 276 * In particular, this is gross, as this is including initialized structures, 277 * and by right shouldn't be an "include" file. 278 */ 279#include "mib.h" 280 281/* 282 * This defines a list of OIDs which will be abbreviated on output. 283 * Currently, this includes the prefixes for the Internet MIB, the 284 * private enterprises tree, and the experimental tree. 285 */ 286#define OID_FIRST_OCTET(x, y) (((x)*40) + (y)) /* X.690 8.19.4 */ 287 288#ifndef NO_ABREV_MIB 289static const uint8_t mib_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 2, 1 }; 290#endif 291#ifndef NO_ABREV_ENTER 292static const uint8_t enterprises_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 4, 1 }; 293#endif 294#ifndef NO_ABREV_EXPERI 295static const uint8_t experimental_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 3 }; 296#endif 297#ifndef NO_ABBREV_SNMPMODS 298static const uint8_t snmpModules_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 6, 3 }; 299#endif 300 301#define OBJ_ABBREV_ENTRY(prefix, obj) \ 302 { prefix, &_ ## obj ## _obj, obj ## _oid, sizeof (obj ## _oid) } 303static const struct obj_abrev { 304 const char *prefix; /* prefix for this abrev */ 305 struct obj *node; /* pointer into object table */ 306 const uint8_t *oid; /* ASN.1 encoded OID */ 307 size_t oid_len; /* length of OID */ 308} obj_abrev_list[] = { 309#ifndef NO_ABREV_MIB 310 /* .iso.org.dod.internet.mgmt.mib */ 311 OBJ_ABBREV_ENTRY("", mib), 312#endif 313#ifndef NO_ABREV_ENTER 314 /* .iso.org.dod.internet.private.enterprises */ 315 OBJ_ABBREV_ENTRY("E:", enterprises), 316#endif 317#ifndef NO_ABREV_EXPERI 318 /* .iso.org.dod.internet.experimental */ 319 OBJ_ABBREV_ENTRY("X:", experimental), 320#endif 321#ifndef NO_ABBREV_SNMPMODS 322 /* .iso.org.dod.internet.snmpV2.snmpModules */ 323 OBJ_ABBREV_ENTRY("S:", snmpModules), 324#endif 325 { 0,0,0,0 } 326}; 327 328/* 329 * This is used in the OID print routine to walk down the object tree 330 * rooted at `mibroot'. 331 */ 332#define OBJ_PRINT(o, suppressdot) \ 333{ \ 334 if (objp) { \ 335 do { \ 336 if ((o) == objp->oid) \ 337 break; \ 338 } while ((objp = objp->next) != NULL); \ 339 } \ 340 if (objp) { \ 341 ND_PRINT(suppressdot?"%s":".%s", objp->desc); \ 342 objp = objp->child; \ 343 } else \ 344 ND_PRINT(suppressdot?"%u":".%u", (o)); \ 345} 346 347/* 348 * This is the definition for the Any-Data-Type storage used purely for 349 * temporary internal representation while decoding an ASN.1 data stream. 350 */ 351struct be { 352 uint32_t asnlen; 353 union { 354 const uint8_t *raw; 355 int32_t integer; 356 uint32_t uns; 357 const u_char *str; 358 uint64_t uns64; 359 } data; 360 u_short id; 361 u_char form, class; /* tag info */ 362 u_char type; 363#define BE_ANY 255 364#define BE_NONE 0 365#define BE_NULL 1 366#define BE_OCTET 2 367#define BE_OID 3 368#define BE_INT 4 369#define BE_UNS 5 370#define BE_STR 6 371#define BE_SEQ 7 372#define BE_INETADDR 8 373#define BE_PDU 9 374#define BE_UNS64 10 375#define BE_NOSUCHOBJECT 128 376#define BE_NOSUCHINST 129 377#define BE_ENDOFMIBVIEW 130 378}; 379 380/* 381 * SNMP versions recognized by this module 382 */ 383static const char *SnmpVersion[] = { 384 "SNMPv1", 385#define SNMP_VERSION_1 0 386 "SNMPv2c", 387#define SNMP_VERSION_2 1 388 "SNMPv2u", 389#define SNMP_VERSION_2U 2 390 "SNMPv3" 391#define SNMP_VERSION_3 3 392}; 393 394/* 395 * Defaults for SNMP PDU components 396 */ 397#define DEF_COMMUNITY "public" 398 399/* 400 * constants for ASN.1 decoding 401 */ 402#define OIDMUX 40 403#define ASNLEN_INETADDR 4 404#define ASN_SHIFT7 7 405#define ASN_SHIFT8 8 406#define ASN_BIT8 0x80 407#define ASN_LONGLEN 0x80 408 409#define ASN_ID_BITS 0x1f 410#define ASN_FORM_BITS 0x20 411#define ASN_FORM_SHIFT 5 412#define ASN_CLASS_BITS 0xc0 413#define ASN_CLASS_SHIFT 6 414 415#define ASN_ID_EXT 0x1f /* extension ID in tag field */ 416 417/* 418 * This decodes the next ASN.1 object in the stream pointed to by "p" 419 * (and of real-length "len") and stores the intermediate data in the 420 * provided BE object. 421 * 422 * This returns -l if it fails (i.e., the ASN.1 stream is not valid). 423 * O/w, this returns the number of bytes parsed from "p". 424 */ 425static int 426asn1_parse(netdissect_options *ndo, 427 const u_char *p, u_int len, struct be *elem) 428{ 429 u_char form, class, id; 430 u_int i, hdr; 431 432 elem->asnlen = 0; 433 elem->type = BE_ANY; 434 if (len < 1) { 435 ND_PRINT("[nothing to parse]"); 436 return -1; 437 } 438 439 /* 440 * it would be nice to use a bit field, but you can't depend on them. 441 * +---+---+---+---+---+---+---+---+ 442 * + class |frm| id | 443 * +---+---+---+---+---+---+---+---+ 444 * 7 6 5 4 3 2 1 0 445 */ 446 id = GET_U_1(p) & ASN_ID_BITS; /* lower 5 bits, range 00-1f */ 447#ifdef notdef 448 form = (GET_U_1(p) & 0xe0) >> 5; /* move upper 3 bits to lower 3 */ 449 class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */ 450 form &= 0x1; /* bit 5 -> bit 0, range 0-1 */ 451#else 452 form = (u_char)(GET_U_1(p) & ASN_FORM_BITS) >> ASN_FORM_SHIFT; 453 class = (u_char)(GET_U_1(p) & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT; 454#endif 455 elem->form = form; 456 elem->class = class; 457 elem->id = id; 458 p++; len--; hdr = 1; 459 /* extended tag field */ 460 if (id == ASN_ID_EXT) { 461 /* 462 * The ID follows, as a sequence of octets with the 463 * 8th bit set and the remaining 7 bits being 464 * the next 7 bits of the value, terminated with 465 * an octet with the 8th bit not set. 466 * 467 * First, assemble all the octets with the 8th 468 * bit set. XXX - this doesn't handle a value 469 * that won't fit in 32 bits. 470 */ 471 id = 0; 472 while (GET_U_1(p) & ASN_BIT8) { 473 if (len < 1) { 474 ND_PRINT("[Xtagfield?]"); 475 return -1; 476 } 477 id = (id << 7) | (GET_U_1(p) & ~ASN_BIT8); 478 len--; 479 hdr++; 480 p++; 481 } 482 if (len < 1) { 483 ND_PRINT("[Xtagfield?]"); 484 return -1; 485 } 486 elem->id = id = (id << 7) | GET_U_1(p); 487 --len; 488 ++hdr; 489 ++p; 490 } 491 if (len < 1) { 492 ND_PRINT("[no asnlen]"); 493 return -1; 494 } 495 elem->asnlen = GET_U_1(p); 496 p++; len--; hdr++; 497 if (elem->asnlen & ASN_BIT8) { 498 uint32_t noct = elem->asnlen % ASN_BIT8; 499 elem->asnlen = 0; 500 if (len < noct) { 501 ND_PRINT("[asnlen? %d<%d]", len, noct); 502 return -1; 503 } 504 ND_TCHECK_LEN(p, noct); 505 for (; noct != 0; len--, hdr++, noct--) { 506 elem->asnlen = (elem->asnlen << ASN_SHIFT8) | GET_U_1(p); 507 p++; 508 } 509 } 510 if (len < elem->asnlen) { 511 ND_PRINT("[len%d<asnlen%u]", len, elem->asnlen); 512 return -1; 513 } 514 if (form >= sizeof(Form)/sizeof(Form[0])) { 515 ND_PRINT("[form?%d]", form); 516 return -1; 517 } 518 if (class >= sizeof(Class)/sizeof(Class[0])) { 519 ND_PRINT("[class?%c/%d]", *Form[form], class); 520 return -1; 521 } 522 if ((int)id >= Class[class].numIDs) { 523 ND_PRINT("[id?%c/%s/%d]", *Form[form], Class[class].name, id); 524 return -1; 525 } 526 ND_TCHECK_LEN(p, elem->asnlen); 527 528 switch (form) { 529 case PRIMITIVE: 530 switch (class) { 531 case UNIVERSAL: 532 switch (id) { 533 case STRING: 534 elem->type = BE_STR; 535 elem->data.str = p; 536 break; 537 538 case INTEGER: { 539 int32_t data; 540 elem->type = BE_INT; 541 data = 0; 542 543 if (elem->asnlen == 0) { 544 ND_PRINT("[asnlen=0]"); 545 return -1; 546 } 547 if (GET_U_1(p) & ASN_BIT8) /* negative */ 548 data = -1; 549 for (i = elem->asnlen; i != 0; p++, i--) 550 data = (data << ASN_SHIFT8) | GET_U_1(p); 551 elem->data.integer = data; 552 break; 553 } 554 555 case OBJECTID: 556 elem->type = BE_OID; 557 elem->data.raw = (const uint8_t *)p; 558 break; 559 560 case ASN_NULL: 561 elem->type = BE_NULL; 562 elem->data.raw = NULL; 563 break; 564 565 default: 566 elem->type = BE_OCTET; 567 elem->data.raw = (const uint8_t *)p; 568 ND_PRINT("[P/U/%s]", Class[class].Id[id]); 569 break; 570 } 571 break; 572 573 case APPLICATION: 574 switch (id) { 575 case IPADDR: 576 elem->type = BE_INETADDR; 577 elem->data.raw = (const uint8_t *)p; 578 break; 579 580 case COUNTER: 581 case GAUGE: 582 case TIMETICKS: { 583 uint32_t data; 584 elem->type = BE_UNS; 585 data = 0; 586 for (i = elem->asnlen; i != 0; p++, i--) 587 data = (data << 8) + GET_U_1(p); 588 elem->data.uns = data; 589 break; 590 } 591 592 case COUNTER64: { 593 uint64_t data64; 594 elem->type = BE_UNS64; 595 data64 = 0; 596 for (i = elem->asnlen; i != 0; p++, i--) 597 data64 = (data64 << 8) + GET_U_1(p); 598 elem->data.uns64 = data64; 599 break; 600 } 601 602 default: 603 elem->type = BE_OCTET; 604 elem->data.raw = (const uint8_t *)p; 605 ND_PRINT("[P/A/%s]", 606 Class[class].Id[id]); 607 break; 608 } 609 break; 610 611 case CONTEXT: 612 switch (id) { 613 case NOSUCHOBJECT: 614 elem->type = BE_NOSUCHOBJECT; 615 elem->data.raw = NULL; 616 break; 617 618 case NOSUCHINSTANCE: 619 elem->type = BE_NOSUCHINST; 620 elem->data.raw = NULL; 621 break; 622 623 case ENDOFMIBVIEW: 624 elem->type = BE_ENDOFMIBVIEW; 625 elem->data.raw = NULL; 626 break; 627 } 628 break; 629 630 default: 631 ND_PRINT("[P/%s/%s]", Class[class].name, Class[class].Id[id]); 632 elem->type = BE_OCTET; 633 elem->data.raw = (const uint8_t *)p; 634 break; 635 } 636 break; 637 638 case CONSTRUCTED: 639 switch (class) { 640 case UNIVERSAL: 641 switch (id) { 642 case SEQUENCE: 643 elem->type = BE_SEQ; 644 elem->data.raw = (const uint8_t *)p; 645 break; 646 647 default: 648 elem->type = BE_OCTET; 649 elem->data.raw = (const uint8_t *)p; 650 ND_PRINT("C/U/%s", Class[class].Id[id]); 651 break; 652 } 653 break; 654 655 case CONTEXT: 656 elem->type = BE_PDU; 657 elem->data.raw = (const uint8_t *)p; 658 break; 659 660 default: 661 elem->type = BE_OCTET; 662 elem->data.raw = (const uint8_t *)p; 663 ND_PRINT("C/%s/%s", Class[class].name, Class[class].Id[id]); 664 break; 665 } 666 break; 667 } 668 p += elem->asnlen; 669 len -= elem->asnlen; 670 return elem->asnlen + hdr; 671 672trunc: 673 nd_print_trunc(ndo); 674 return -1; 675} 676 677static int 678asn1_print_octets(netdissect_options *ndo, struct be *elem) 679{ 680 const u_char *p = (const u_char *)elem->data.raw; 681 uint32_t asnlen = elem->asnlen; 682 uint32_t i; 683 684 ND_TCHECK_LEN(p, asnlen); 685 for (i = asnlen; i != 0; p++, i--) 686 ND_PRINT("_%.2x", GET_U_1(p)); 687 return 0; 688 689trunc: 690 nd_print_trunc(ndo); 691 return -1; 692} 693 694static int 695asn1_print_string(netdissect_options *ndo, struct be *elem) 696{ 697 int printable = 1, first = 1; 698 const u_char *p; 699 uint32_t asnlen = elem->asnlen; 700 uint32_t i; 701 702 p = elem->data.str; 703 ND_TCHECK_LEN(p, asnlen); 704 for (i = asnlen; printable && i != 0; p++, i--) 705 printable = ND_ASCII_ISPRINT(GET_U_1(p)); 706 p = elem->data.str; 707 if (printable) { 708 ND_PRINT("\""); 709 if (nd_printn(ndo, p, asnlen, ndo->ndo_snapend)) { 710 ND_PRINT("\""); 711 goto trunc; 712 } 713 ND_PRINT("\""); 714 } else { 715 for (i = asnlen; i != 0; p++, i--) { 716 ND_PRINT(first ? "%.2x" : "_%.2x", GET_U_1(p)); 717 first = 0; 718 } 719 } 720 return 0; 721 722trunc: 723 nd_print_trunc(ndo); 724 return -1; 725} 726 727/* 728 * Display the ASN.1 object represented by the BE object. 729 * This used to be an integral part of asn1_parse() before the intermediate 730 * BE form was added. 731 */ 732static int 733asn1_print(netdissect_options *ndo, 734 struct be *elem) 735{ 736 const u_char *p; 737 uint32_t asnlen = elem->asnlen; 738 uint32_t i; 739 740 switch (elem->type) { 741 742 case BE_OCTET: 743 if (asn1_print_octets(ndo, elem) == -1) 744 return -1; 745 break; 746 747 case BE_NULL: 748 break; 749 750 case BE_OID: { 751 int o = 0, first = -1; 752 753 p = (const u_char *)elem->data.raw; 754 i = asnlen; 755 if (!ndo->ndo_nflag && asnlen > 2) { 756 const struct obj_abrev *a = &obj_abrev_list[0]; 757 for (; a->node; a++) { 758 if (i < a->oid_len) 759 continue; 760 if (!ND_TTEST_LEN(p, a->oid_len)) 761 continue; 762 if (memcmp(a->oid, p, a->oid_len) == 0) { 763 objp = a->node->child; 764 i -= a->oid_len; 765 p += a->oid_len; 766 ND_PRINT("%s", a->prefix); 767 first = 1; 768 break; 769 } 770 } 771 } 772 773 for (; i != 0; p++, i--) { 774 o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8); 775 if (GET_U_1(p) & ASN_LONGLEN) 776 continue; 777 778 /* 779 * first subitem encodes two items with 780 * 1st*OIDMUX+2nd 781 * (see X.690:1997 clause 8.19 for the details) 782 */ 783 if (first < 0) { 784 int s; 785 if (!ndo->ndo_nflag) 786 objp = mibroot; 787 first = 0; 788 s = o / OIDMUX; 789 if (s > 2) s = 2; 790 OBJ_PRINT(s, first); 791 o -= s * OIDMUX; 792 } 793 OBJ_PRINT(o, first); 794 if (--first < 0) 795 first = 0; 796 o = 0; 797 } 798 break; 799 } 800 801 case BE_INT: 802 ND_PRINT("%d", elem->data.integer); 803 break; 804 805 case BE_UNS: 806 ND_PRINT("%u", elem->data.uns); 807 break; 808 809 case BE_UNS64: 810 ND_PRINT("%" PRIu64, elem->data.uns64); 811 break; 812 813 case BE_STR: 814 if (asn1_print_string(ndo, elem) == -1) 815 return -1; 816 break; 817 818 case BE_SEQ: 819 ND_PRINT("Seq(%u)", elem->asnlen); 820 break; 821 822 case BE_INETADDR: 823 if (asnlen != ASNLEN_INETADDR) 824 ND_PRINT("[inetaddr len!=%d]", ASNLEN_INETADDR); 825 p = (const u_char *)elem->data.raw; 826 ND_TCHECK_LEN(p, asnlen); 827 for (i = asnlen; i != 0; p++, i--) { 828 ND_PRINT((i == asnlen) ? "%u" : ".%u", GET_U_1(p)); 829 } 830 break; 831 832 case BE_NOSUCHOBJECT: 833 case BE_NOSUCHINST: 834 case BE_ENDOFMIBVIEW: 835 ND_PRINT("[%s]", Class[EXCEPTIONS].Id[elem->id]); 836 break; 837 838 case BE_PDU: 839 ND_PRINT("%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen); 840 break; 841 842 case BE_ANY: 843 ND_PRINT("[BE_ANY!?]"); 844 break; 845 846 default: 847 ND_PRINT("[be!?]"); 848 break; 849 } 850 return 0; 851 852trunc: 853 nd_print_trunc(ndo); 854 return -1; 855} 856 857#ifdef notdef 858/* 859 * This is a brute force ASN.1 printer: recurses to dump an entire structure. 860 * This will work for any ASN.1 stream, not just an SNMP PDU. 861 * 862 * By adding newlines and spaces at the correct places, this would print in 863 * Rose-Normal-Form. 864 * 865 * This is not currently used. 866 */ 867static void 868asn1_decode(u_char *p, u_int length) 869{ 870 struct be elem; 871 int i = 0; 872 873 while (i >= 0 && length > 0) { 874 i = asn1_parse(ndo, p, length, &elem); 875 if (i >= 0) { 876 ND_PRINT(" "); 877 if (asn1_print(ndo, &elem) < 0) 878 return; 879 if (elem.type == BE_SEQ || elem.type == BE_PDU) { 880 ND_PRINT(" {"); 881 asn1_decode(elem.data.raw, elem.asnlen); 882 ND_PRINT(" }"); 883 } 884 length -= i; 885 p += i; 886 } 887 } 888} 889#endif 890 891#ifdef USE_LIBSMI 892 893struct smi2be { 894 SmiBasetype basetype; 895 int be; 896}; 897 898static const struct smi2be smi2betab[] = { 899 { SMI_BASETYPE_INTEGER32, BE_INT }, 900 { SMI_BASETYPE_OCTETSTRING, BE_STR }, 901 { SMI_BASETYPE_OCTETSTRING, BE_INETADDR }, 902 { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID }, 903 { SMI_BASETYPE_UNSIGNED32, BE_UNS }, 904 { SMI_BASETYPE_INTEGER64, BE_NONE }, 905 { SMI_BASETYPE_UNSIGNED64, BE_UNS64 }, 906 { SMI_BASETYPE_FLOAT32, BE_NONE }, 907 { SMI_BASETYPE_FLOAT64, BE_NONE }, 908 { SMI_BASETYPE_FLOAT128, BE_NONE }, 909 { SMI_BASETYPE_ENUM, BE_INT }, 910 { SMI_BASETYPE_BITS, BE_STR }, 911 { SMI_BASETYPE_UNKNOWN, BE_NONE } 912}; 913 914static int 915smi_decode_oid(netdissect_options *ndo, 916 struct be *elem, unsigned int *oid, 917 unsigned int oidsize, unsigned int *oidlen) 918{ 919 const u_char *p = (const u_char *)elem->data.raw; 920 uint32_t asnlen = elem->asnlen; 921 uint32_t i = asnlen; 922 int o = 0, first = -1; 923 unsigned int firstval; 924 925 for (*oidlen = 0; i != 0; p++, i--) { 926 o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8); 927 if (GET_U_1(p) & ASN_LONGLEN) 928 continue; 929 930 /* 931 * first subitem encodes two items with 1st*OIDMUX+2nd 932 * (see X.690:1997 clause 8.19 for the details) 933 */ 934 if (first < 0) { 935 first = 0; 936 firstval = o / OIDMUX; 937 if (firstval > 2) firstval = 2; 938 o -= firstval * OIDMUX; 939 if (*oidlen < oidsize) { 940 oid[(*oidlen)++] = firstval; 941 } 942 } 943 if (*oidlen < oidsize) { 944 oid[(*oidlen)++] = o; 945 } 946 o = 0; 947 } 948 return 0; 949} 950 951static int smi_check_type(SmiBasetype basetype, int be) 952{ 953 int i; 954 955 for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) { 956 if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) { 957 return 1; 958 } 959 } 960 961 return 0; 962} 963 964static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange, 965 struct be *elem) 966{ 967 int ok = 1; 968 969 switch (smiType->basetype) { 970 case SMI_BASETYPE_OBJECTIDENTIFIER: 971 case SMI_BASETYPE_OCTETSTRING: 972 if (smiRange->minValue.value.unsigned32 973 == smiRange->maxValue.value.unsigned32) { 974 ok = (elem->asnlen == smiRange->minValue.value.unsigned32); 975 } else { 976 ok = (elem->asnlen >= smiRange->minValue.value.unsigned32 977 && elem->asnlen <= smiRange->maxValue.value.unsigned32); 978 } 979 break; 980 981 case SMI_BASETYPE_INTEGER32: 982 ok = (elem->data.integer >= smiRange->minValue.value.integer32 983 && elem->data.integer <= smiRange->maxValue.value.integer32); 984 break; 985 986 case SMI_BASETYPE_UNSIGNED32: 987 ok = (elem->data.uns >= smiRange->minValue.value.unsigned32 988 && elem->data.uns <= smiRange->maxValue.value.unsigned32); 989 break; 990 991 case SMI_BASETYPE_UNSIGNED64: 992 /* XXX */ 993 break; 994 995 /* case SMI_BASETYPE_INTEGER64: SMIng */ 996 /* case SMI_BASETYPE_FLOAT32: SMIng */ 997 /* case SMI_BASETYPE_FLOAT64: SMIng */ 998 /* case SMI_BASETYPE_FLOAT128: SMIng */ 999 1000 case SMI_BASETYPE_ENUM: 1001 case SMI_BASETYPE_BITS: 1002 case SMI_BASETYPE_UNKNOWN: 1003 ok = 1; 1004 break; 1005 1006 default: 1007 ok = 0; 1008 break; 1009 } 1010 1011 return ok; 1012} 1013 1014static int smi_check_range(SmiType *smiType, struct be *elem) 1015{ 1016 SmiRange *smiRange; 1017 int ok = 1; 1018 1019 for (smiRange = smiGetFirstRange(smiType); 1020 smiRange; 1021 smiRange = smiGetNextRange(smiRange)) { 1022 1023 ok = smi_check_a_range(smiType, smiRange, elem); 1024 1025 if (ok) { 1026 break; 1027 } 1028 } 1029 1030 if (ok) { 1031 SmiType *parentType; 1032 parentType = smiGetParentType(smiType); 1033 if (parentType) { 1034 ok = smi_check_range(parentType, elem); 1035 } 1036 } 1037 1038 return ok; 1039} 1040 1041static SmiNode * 1042smi_print_variable(netdissect_options *ndo, 1043 struct be *elem, int *status) 1044{ 1045 unsigned int oid[128], oidlen; 1046 SmiNode *smiNode = NULL; 1047 unsigned int i; 1048 1049 if (!nd_smi_module_loaded) { 1050 *status = asn1_print(ndo, elem); 1051 return NULL; 1052 } 1053 *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int), 1054 &oidlen); 1055 if (*status < 0) 1056 return NULL; 1057 smiNode = smiGetNodeByOID(oidlen, oid); 1058 if (! smiNode) { 1059 *status = asn1_print(ndo, elem); 1060 return NULL; 1061 } 1062 if (ndo->ndo_vflag) { 1063 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name); 1064 } 1065 ND_PRINT("%s", smiNode->name); 1066 if (smiNode->oidlen < oidlen) { 1067 for (i = smiNode->oidlen; i < oidlen; i++) { 1068 ND_PRINT(".%u", oid[i]); 1069 } 1070 } 1071 *status = 0; 1072 return smiNode; 1073} 1074 1075static int 1076smi_print_value(netdissect_options *ndo, 1077 SmiNode *smiNode, u_short pduid, struct be *elem) 1078{ 1079 unsigned int i, oid[128], oidlen; 1080 SmiType *smiType; 1081 SmiNamedNumber *nn; 1082 int done = 0; 1083 1084 if (! smiNode || ! (smiNode->nodekind 1085 & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) { 1086 return asn1_print(ndo, elem); 1087 } 1088 1089 if (elem->type == BE_NOSUCHOBJECT 1090 || elem->type == BE_NOSUCHINST 1091 || elem->type == BE_ENDOFMIBVIEW) { 1092 return asn1_print(ndo, elem); 1093 } 1094 1095 if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) { 1096 ND_PRINT("[notNotifyable]"); 1097 } 1098 1099 if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) { 1100 ND_PRINT("[notReadable]"); 1101 } 1102 1103 if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) { 1104 ND_PRINT("[notWritable]"); 1105 } 1106 1107 if (RESPONSE_CLASS(pduid) 1108 && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) { 1109 ND_PRINT("[noAccess]"); 1110 } 1111 1112 smiType = smiGetNodeType(smiNode); 1113 if (! smiType) { 1114 return asn1_print(ndo, elem); 1115 } 1116 1117 if (! smi_check_type(smiType->basetype, elem->type)) { 1118 ND_PRINT("[wrongType]"); 1119 } 1120 1121 if (! smi_check_range(smiType, elem)) { 1122 ND_PRINT("[outOfRange]"); 1123 } 1124 1125 /* resolve bits to named bits */ 1126 1127 /* check whether instance identifier is valid */ 1128 1129 /* apply display hints (integer, octetstring) */ 1130 1131 /* convert instance identifier to index type values */ 1132 1133 switch (elem->type) { 1134 case BE_OID: 1135 if (smiType->basetype == SMI_BASETYPE_BITS) { 1136 /* print bit labels */ 1137 } else { 1138 if (nd_smi_module_loaded && 1139 smi_decode_oid(ndo, elem, oid, 1140 sizeof(oid)/sizeof(unsigned int), 1141 &oidlen) == 0) { 1142 smiNode = smiGetNodeByOID(oidlen, oid); 1143 if (smiNode) { 1144 if (ndo->ndo_vflag) { 1145 ND_PRINT("%s::", smiGetNodeModule(smiNode)->name); 1146 } 1147 ND_PRINT("%s", smiNode->name); 1148 if (smiNode->oidlen < oidlen) { 1149 for (i = smiNode->oidlen; 1150 i < oidlen; i++) { 1151 ND_PRINT(".%u", oid[i]); 1152 } 1153 } 1154 done++; 1155 } 1156 } 1157 } 1158 break; 1159 1160 case BE_INT: 1161 if (smiType->basetype == SMI_BASETYPE_ENUM) { 1162 for (nn = smiGetFirstNamedNumber(smiType); 1163 nn; 1164 nn = smiGetNextNamedNumber(nn)) { 1165 if (nn->value.value.integer32 1166 == elem->data.integer) { 1167 ND_PRINT("%s", nn->name); 1168 ND_PRINT("(%d)", elem->data.integer); 1169 done++; 1170 break; 1171 } 1172 } 1173 } 1174 break; 1175 } 1176 1177 if (! done) { 1178 return asn1_print(ndo, elem); 1179 } 1180 return 0; 1181} 1182#endif 1183 1184/* 1185 * General SNMP header 1186 * SEQUENCE { 1187 * version INTEGER {version-1(0)}, 1188 * community OCTET STRING, 1189 * data ANY -- PDUs 1190 * } 1191 * PDUs for all but Trap: (see rfc1157 from page 15 on) 1192 * SEQUENCE { 1193 * request-id INTEGER, 1194 * error-status INTEGER, 1195 * error-index INTEGER, 1196 * varbindlist SEQUENCE OF 1197 * SEQUENCE { 1198 * name ObjectName, 1199 * value ObjectValue 1200 * } 1201 * } 1202 * PDU for Trap: 1203 * SEQUENCE { 1204 * enterprise OBJECT IDENTIFIER, 1205 * agent-addr NetworkAddress, 1206 * generic-trap INTEGER, 1207 * specific-trap INTEGER, 1208 * time-stamp TimeTicks, 1209 * varbindlist SEQUENCE OF 1210 * SEQUENCE { 1211 * name ObjectName, 1212 * value ObjectValue 1213 * } 1214 * } 1215 */ 1216 1217/* 1218 * Decode SNMP varBind 1219 */ 1220static void 1221varbind_print(netdissect_options *ndo, 1222 u_short pduid, const u_char *np, u_int length) 1223{ 1224 struct be elem; 1225 int count = 0; 1226#ifdef USE_LIBSMI 1227 SmiNode *smiNode = NULL; 1228#endif 1229 int status; 1230 1231 /* Sequence of varBind */ 1232 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1233 return; 1234 if (elem.type != BE_SEQ) { 1235 ND_PRINT("[!SEQ of varbind]"); 1236 asn1_print(ndo, &elem); 1237 return; 1238 } 1239 if ((u_int)count < length) 1240 ND_PRINT("[%d extra after SEQ of varbind]", length - count); 1241 /* descend */ 1242 length = elem.asnlen; 1243 np = (const u_char *)elem.data.raw; 1244 1245 while (length) { 1246 const u_char *vbend; 1247 u_int vblength; 1248 1249 ND_PRINT(" "); 1250 1251 /* Sequence */ 1252 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1253 return; 1254 if (elem.type != BE_SEQ) { 1255 ND_PRINT("[!varbind]"); 1256 asn1_print(ndo, &elem); 1257 return; 1258 } 1259 vbend = np + count; 1260 vblength = length - count; 1261 /* descend */ 1262 length = elem.asnlen; 1263 np = (const u_char *)elem.data.raw; 1264 1265 /* objName (OID) */ 1266 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1267 return; 1268 if (elem.type != BE_OID) { 1269 ND_PRINT("[objName!=OID]"); 1270 asn1_print(ndo, &elem); 1271 return; 1272 } 1273#ifdef USE_LIBSMI 1274 smiNode = smi_print_variable(ndo, &elem, &status); 1275#else 1276 status = asn1_print(ndo, &elem); 1277#endif 1278 if (status < 0) 1279 return; 1280 length -= count; 1281 np += count; 1282 1283 if (pduid != GETREQ && pduid != GETNEXTREQ 1284 && pduid != GETBULKREQ) 1285 ND_PRINT("="); 1286 1287 /* objVal (ANY) */ 1288 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1289 return; 1290 if (pduid == GETREQ || pduid == GETNEXTREQ 1291 || pduid == GETBULKREQ) { 1292 if (elem.type != BE_NULL) { 1293 ND_PRINT("[objVal!=NULL]"); 1294 if (asn1_print(ndo, &elem) < 0) 1295 return; 1296 } 1297 } else { 1298 if (elem.type != BE_NULL) { 1299#ifdef USE_LIBSMI 1300 status = smi_print_value(ndo, smiNode, pduid, &elem); 1301#else 1302 status = asn1_print(ndo, &elem); 1303#endif 1304 } 1305 if (status < 0) 1306 return; 1307 } 1308 length = vblength; 1309 np = vbend; 1310 } 1311} 1312 1313/* 1314 * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest, 1315 * GetBulk, Inform, V2Trap, and Report 1316 */ 1317static void 1318snmppdu_print(netdissect_options *ndo, 1319 u_short pduid, const u_char *np, u_int length) 1320{ 1321 struct be elem; 1322 int count = 0, error_status; 1323 1324 /* reqId (Integer) */ 1325 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1326 return; 1327 if (elem.type != BE_INT) { 1328 ND_PRINT("[reqId!=INT]"); 1329 asn1_print(ndo, &elem); 1330 return; 1331 } 1332 if (ndo->ndo_vflag) 1333 ND_PRINT("R=%d ", elem.data.integer); 1334 length -= count; 1335 np += count; 1336 1337 /* errorStatus (Integer) */ 1338 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1339 return; 1340 if (elem.type != BE_INT) { 1341 ND_PRINT("[errorStatus!=INT]"); 1342 asn1_print(ndo, &elem); 1343 return; 1344 } 1345 error_status = 0; 1346 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ 1347 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT) 1348 && elem.data.integer != 0) { 1349 char errbuf[20]; 1350 ND_PRINT("[errorStatus(%s)!=0]", 1351 DECODE_ErrorStatus(elem.data.integer)); 1352 } else if (pduid == GETBULKREQ) { 1353 ND_PRINT(" N=%d", elem.data.integer); 1354 } else if (elem.data.integer != 0) { 1355 char errbuf[20]; 1356 ND_PRINT(" %s", DECODE_ErrorStatus(elem.data.integer)); 1357 error_status = elem.data.integer; 1358 } 1359 length -= count; 1360 np += count; 1361 1362 /* errorIndex (Integer) */ 1363 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1364 return; 1365 if (elem.type != BE_INT) { 1366 ND_PRINT("[errorIndex!=INT]"); 1367 asn1_print(ndo, &elem); 1368 return; 1369 } 1370 if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ 1371 || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT) 1372 && elem.data.integer != 0) 1373 ND_PRINT("[errorIndex(%d)!=0]", elem.data.integer); 1374 else if (pduid == GETBULKREQ) 1375 ND_PRINT(" M=%d", elem.data.integer); 1376 else if (elem.data.integer != 0) { 1377 if (!error_status) 1378 ND_PRINT("[errorIndex(%d) w/o errorStatus]", elem.data.integer); 1379 else 1380 ND_PRINT("@%d", elem.data.integer); 1381 } else if (error_status) { 1382 ND_PRINT("[errorIndex==0]"); 1383 } 1384 length -= count; 1385 np += count; 1386 1387 varbind_print(ndo, pduid, np, length); 1388} 1389 1390/* 1391 * Decode SNMP Trap PDU 1392 */ 1393static void 1394trappdu_print(netdissect_options *ndo, 1395 const u_char *np, u_int length) 1396{ 1397 struct be elem; 1398 int count = 0, generic; 1399 1400 ND_PRINT(" "); 1401 1402 /* enterprise (oid) */ 1403 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1404 return; 1405 if (elem.type != BE_OID) { 1406 ND_PRINT("[enterprise!=OID]"); 1407 asn1_print(ndo, &elem); 1408 return; 1409 } 1410 if (asn1_print(ndo, &elem) < 0) 1411 return; 1412 length -= count; 1413 np += count; 1414 1415 ND_PRINT(" "); 1416 1417 /* agent-addr (inetaddr) */ 1418 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1419 return; 1420 if (elem.type != BE_INETADDR) { 1421 ND_PRINT("[agent-addr!=INETADDR]"); 1422 asn1_print(ndo, &elem); 1423 return; 1424 } 1425 if (asn1_print(ndo, &elem) < 0) 1426 return; 1427 length -= count; 1428 np += count; 1429 1430 /* generic-trap (Integer) */ 1431 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1432 return; 1433 if (elem.type != BE_INT) { 1434 ND_PRINT("[generic-trap!=INT]"); 1435 asn1_print(ndo, &elem); 1436 return; 1437 } 1438 generic = elem.data.integer; 1439 { 1440 char buf[20]; 1441 ND_PRINT(" %s", DECODE_GenericTrap(generic)); 1442 } 1443 length -= count; 1444 np += count; 1445 1446 /* specific-trap (Integer) */ 1447 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1448 return; 1449 if (elem.type != BE_INT) { 1450 ND_PRINT("[specific-trap!=INT]"); 1451 asn1_print(ndo, &elem); 1452 return; 1453 } 1454 if (generic != GT_ENTERPRISE) { 1455 if (elem.data.integer != 0) 1456 ND_PRINT("[specific-trap(%d)!=0]", elem.data.integer); 1457 } else 1458 ND_PRINT(" s=%d", elem.data.integer); 1459 length -= count; 1460 np += count; 1461 1462 ND_PRINT(" "); 1463 1464 /* time-stamp (TimeTicks) */ 1465 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1466 return; 1467 if (elem.type != BE_UNS) { /* XXX */ 1468 ND_PRINT("[time-stamp!=TIMETICKS]"); 1469 asn1_print(ndo, &elem); 1470 return; 1471 } 1472 if (asn1_print(ndo, &elem) < 0) 1473 return; 1474 length -= count; 1475 np += count; 1476 1477 varbind_print(ndo, TRAP, np, length); 1478} 1479 1480/* 1481 * Decode arbitrary SNMP PDUs. 1482 */ 1483static void 1484pdu_print(netdissect_options *ndo, 1485 const u_char *np, u_int length, int version) 1486{ 1487 struct be pdu; 1488 int count = 0; 1489 1490 /* PDU (Context) */ 1491 if ((count = asn1_parse(ndo, np, length, &pdu)) < 0) 1492 return; 1493 if (pdu.type != BE_PDU) { 1494 ND_PRINT("[no PDU]"); 1495 return; 1496 } 1497 if ((u_int)count < length) 1498 ND_PRINT("[%d extra after PDU]", length - count); 1499 if (ndo->ndo_vflag) { 1500 ND_PRINT("{ "); 1501 } 1502 if (asn1_print(ndo, &pdu) < 0) 1503 return; 1504 ND_PRINT(" "); 1505 /* descend into PDU */ 1506 length = pdu.asnlen; 1507 np = (const u_char *)pdu.data.raw; 1508 1509 if (version == SNMP_VERSION_1 && 1510 (pdu.id == GETBULKREQ || pdu.id == INFORMREQ || 1511 pdu.id == V2TRAP || pdu.id == REPORT)) { 1512 ND_PRINT("[v2 PDU in v1 message]"); 1513 return; 1514 } 1515 1516 if (version == SNMP_VERSION_2 && pdu.id == TRAP) { 1517 ND_PRINT("[v1 PDU in v2 message]"); 1518 return; 1519 } 1520 1521 switch (pdu.id) { 1522 case TRAP: 1523 trappdu_print(ndo, np, length); 1524 break; 1525 case GETREQ: 1526 case GETNEXTREQ: 1527 case GETRESP: 1528 case SETREQ: 1529 case GETBULKREQ: 1530 case INFORMREQ: 1531 case V2TRAP: 1532 case REPORT: 1533 snmppdu_print(ndo, pdu.id, np, length); 1534 break; 1535 } 1536 1537 if (ndo->ndo_vflag) { 1538 ND_PRINT(" } "); 1539 } 1540} 1541 1542/* 1543 * Decode a scoped SNMP PDU. 1544 */ 1545static void 1546scopedpdu_print(netdissect_options *ndo, 1547 const u_char *np, u_int length, int version) 1548{ 1549 struct be elem; 1550 int count = 0; 1551 1552 /* Sequence */ 1553 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1554 return; 1555 if (elem.type != BE_SEQ) { 1556 ND_PRINT("[!scoped PDU]"); 1557 asn1_print(ndo, &elem); 1558 return; 1559 } 1560 length = elem.asnlen; 1561 np = (const u_char *)elem.data.raw; 1562 1563 /* contextEngineID (OCTET STRING) */ 1564 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1565 return; 1566 if (elem.type != BE_STR) { 1567 ND_PRINT("[contextEngineID!=STR]"); 1568 asn1_print(ndo, &elem); 1569 return; 1570 } 1571 length -= count; 1572 np += count; 1573 1574 ND_PRINT("E="); 1575 if (asn1_print_octets(ndo, &elem) == -1) 1576 return; 1577 ND_PRINT(" "); 1578 1579 /* contextName (OCTET STRING) */ 1580 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1581 return; 1582 if (elem.type != BE_STR) { 1583 ND_PRINT("[contextName!=STR]"); 1584 asn1_print(ndo, &elem); 1585 return; 1586 } 1587 length -= count; 1588 np += count; 1589 1590 ND_PRINT("C="); 1591 if (asn1_print_string(ndo, &elem) == -1) 1592 return; 1593 ND_PRINT(" "); 1594 1595 pdu_print(ndo, np, length, version); 1596} 1597 1598/* 1599 * Decode SNMP Community Header (SNMPv1 and SNMPv2c) 1600 */ 1601static void 1602community_print(netdissect_options *ndo, 1603 const u_char *np, u_int length, int version) 1604{ 1605 struct be elem; 1606 int count = 0; 1607 1608 /* Community (String) */ 1609 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1610 return; 1611 if (elem.type != BE_STR) { 1612 ND_PRINT("[comm!=STR]"); 1613 asn1_print(ndo, &elem); 1614 return; 1615 } 1616 /* default community */ 1617 if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 && 1618 strncmp((const char *)elem.data.str, DEF_COMMUNITY, 1619 sizeof(DEF_COMMUNITY) - 1) == 0)) { 1620 /* ! "public" */ 1621 ND_PRINT("C="); 1622 if (asn1_print_string(ndo, &elem) == -1) 1623 return; 1624 ND_PRINT(" "); 1625 } 1626 length -= count; 1627 np += count; 1628 1629 pdu_print(ndo, np, length, version); 1630} 1631 1632/* 1633 * Decode SNMPv3 User-based Security Message Header (SNMPv3) 1634 */ 1635static void 1636usm_print(netdissect_options *ndo, 1637 const u_char *np, u_int length) 1638{ 1639 struct be elem; 1640 int count = 0; 1641 1642 /* Sequence */ 1643 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1644 return; 1645 if (elem.type != BE_SEQ) { 1646 ND_PRINT("[!usm]"); 1647 asn1_print(ndo, &elem); 1648 return; 1649 } 1650 length = elem.asnlen; 1651 np = (const u_char *)elem.data.raw; 1652 1653 /* msgAuthoritativeEngineID (OCTET STRING) */ 1654 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1655 return; 1656 if (elem.type != BE_STR) { 1657 ND_PRINT("[msgAuthoritativeEngineID!=STR]"); 1658 asn1_print(ndo, &elem); 1659 return; 1660 } 1661 length -= count; 1662 np += count; 1663 1664 /* msgAuthoritativeEngineBoots (INTEGER) */ 1665 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1666 return; 1667 if (elem.type != BE_INT) { 1668 ND_PRINT("[msgAuthoritativeEngineBoots!=INT]"); 1669 asn1_print(ndo, &elem); 1670 return; 1671 } 1672 if (ndo->ndo_vflag) 1673 ND_PRINT("B=%d ", elem.data.integer); 1674 length -= count; 1675 np += count; 1676 1677 /* msgAuthoritativeEngineTime (INTEGER) */ 1678 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1679 return; 1680 if (elem.type != BE_INT) { 1681 ND_PRINT("[msgAuthoritativeEngineTime!=INT]"); 1682 asn1_print(ndo, &elem); 1683 return; 1684 } 1685 if (ndo->ndo_vflag) 1686 ND_PRINT("T=%d ", elem.data.integer); 1687 length -= count; 1688 np += count; 1689 1690 /* msgUserName (OCTET STRING) */ 1691 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1692 return; 1693 if (elem.type != BE_STR) { 1694 ND_PRINT("[msgUserName!=STR]"); 1695 asn1_print(ndo, &elem); 1696 return; 1697 } 1698 length -= count; 1699 np += count; 1700 1701 ND_PRINT("U="); 1702 if (asn1_print_string(ndo, &elem) == -1) 1703 return; 1704 ND_PRINT(" "); 1705 1706 /* msgAuthenticationParameters (OCTET STRING) */ 1707 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1708 return; 1709 if (elem.type != BE_STR) { 1710 ND_PRINT("[msgAuthenticationParameters!=STR]"); 1711 asn1_print(ndo, &elem); 1712 return; 1713 } 1714 length -= count; 1715 np += count; 1716 1717 /* msgPrivacyParameters (OCTET STRING) */ 1718 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1719 return; 1720 if (elem.type != BE_STR) { 1721 ND_PRINT("[msgPrivacyParameters!=STR]"); 1722 asn1_print(ndo, &elem); 1723 return; 1724 } 1725 length -= count; 1726 np += count; 1727 1728 if ((u_int)count < length) 1729 ND_PRINT("[%d extra after usm SEQ]", length - count); 1730} 1731 1732/* 1733 * Decode SNMPv3 Message Header (SNMPv3) 1734 */ 1735static void 1736v3msg_print(netdissect_options *ndo, 1737 const u_char *np, u_int length) 1738{ 1739 struct be elem; 1740 int count = 0; 1741 u_char flags; 1742 int model; 1743 const u_char *xnp = np; 1744 int xlength = length; 1745 1746 /* Sequence */ 1747 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1748 return; 1749 if (elem.type != BE_SEQ) { 1750 ND_PRINT("[!message]"); 1751 asn1_print(ndo, &elem); 1752 return; 1753 } 1754 length = elem.asnlen; 1755 np = (const u_char *)elem.data.raw; 1756 1757 if (ndo->ndo_vflag) { 1758 ND_PRINT("{ "); 1759 } 1760 1761 /* msgID (INTEGER) */ 1762 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1763 return; 1764 if (elem.type != BE_INT) { 1765 ND_PRINT("[msgID!=INT]"); 1766 asn1_print(ndo, &elem); 1767 return; 1768 } 1769 length -= count; 1770 np += count; 1771 1772 /* msgMaxSize (INTEGER) */ 1773 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1774 return; 1775 if (elem.type != BE_INT) { 1776 ND_PRINT("[msgMaxSize!=INT]"); 1777 asn1_print(ndo, &elem); 1778 return; 1779 } 1780 length -= count; 1781 np += count; 1782 1783 /* msgFlags (OCTET STRING) */ 1784 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1785 return; 1786 if (elem.type != BE_STR) { 1787 ND_PRINT("[msgFlags!=STR]"); 1788 asn1_print(ndo, &elem); 1789 return; 1790 } 1791 if (elem.asnlen != 1) { 1792 ND_PRINT("[msgFlags size %d]", elem.asnlen); 1793 return; 1794 } 1795 flags = GET_U_1(elem.data.str); 1796 if (flags != 0x00 && flags != 0x01 && flags != 0x03 1797 && flags != 0x04 && flags != 0x05 && flags != 0x07) { 1798 ND_PRINT("[msgFlags=0x%02X]", flags); 1799 return; 1800 } 1801 length -= count; 1802 np += count; 1803 1804 ND_PRINT("F=%s%s%s ", 1805 flags & 0x01 ? "a" : "", 1806 flags & 0x02 ? "p" : "", 1807 flags & 0x04 ? "r" : ""); 1808 1809 /* msgSecurityModel (INTEGER) */ 1810 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1811 return; 1812 if (elem.type != BE_INT) { 1813 ND_PRINT("[msgSecurityModel!=INT]"); 1814 asn1_print(ndo, &elem); 1815 return; 1816 } 1817 model = elem.data.integer; 1818 length -= count; 1819 np += count; 1820 1821 if ((u_int)count < length) 1822 ND_PRINT("[%d extra after message SEQ]", length - count); 1823 1824 if (ndo->ndo_vflag) { 1825 ND_PRINT("} "); 1826 } 1827 1828 if (model == 3) { 1829 if (ndo->ndo_vflag) { 1830 ND_PRINT("{ USM "); 1831 } 1832 } else { 1833 ND_PRINT("[security model %d]", model); 1834 return; 1835 } 1836 1837 np = xnp + (np - xnp); 1838 length = xlength - (np - xnp); 1839 1840 /* msgSecurityParameters (OCTET STRING) */ 1841 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1842 return; 1843 if (elem.type != BE_STR) { 1844 ND_PRINT("[msgSecurityParameters!=STR]"); 1845 asn1_print(ndo, &elem); 1846 return; 1847 } 1848 length -= count; 1849 np += count; 1850 1851 if (model == 3) { 1852 usm_print(ndo, elem.data.str, elem.asnlen); 1853 if (ndo->ndo_vflag) { 1854 ND_PRINT("} "); 1855 } 1856 } 1857 1858 if (ndo->ndo_vflag) { 1859 ND_PRINT("{ ScopedPDU "); 1860 } 1861 1862 scopedpdu_print(ndo, np, length, 3); 1863 1864 if (ndo->ndo_vflag) { 1865 ND_PRINT("} "); 1866 } 1867} 1868 1869/* 1870 * Decode SNMP header and pass on to PDU printing routines 1871 */ 1872void 1873snmp_print(netdissect_options *ndo, 1874 const u_char *np, u_int length) 1875{ 1876 struct be elem; 1877 int count = 0; 1878 int version = 0; 1879 1880 ndo->ndo_protocol = "snmp"; 1881 ND_PRINT(" "); 1882 1883 /* initial Sequence */ 1884 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1885 return; 1886 if (elem.type != BE_SEQ) { 1887 ND_PRINT("[!init SEQ]"); 1888 asn1_print(ndo, &elem); 1889 return; 1890 } 1891 if ((u_int)count < length) 1892 ND_PRINT("[%d extra after iSEQ]", length - count); 1893 /* descend */ 1894 length = elem.asnlen; 1895 np = (const u_char *)elem.data.raw; 1896 1897 /* Version (INTEGER) */ 1898 if ((count = asn1_parse(ndo, np, length, &elem)) < 0) 1899 return; 1900 if (elem.type != BE_INT) { 1901 ND_PRINT("[version!=INT]"); 1902 asn1_print(ndo, &elem); 1903 return; 1904 } 1905 1906 switch (elem.data.integer) { 1907 case SNMP_VERSION_1: 1908 case SNMP_VERSION_2: 1909 case SNMP_VERSION_3: 1910 if (ndo->ndo_vflag) 1911 ND_PRINT("{ %s ", SnmpVersion[elem.data.integer]); 1912 break; 1913 default: 1914 ND_PRINT("SNMP [version = %d]", elem.data.integer); 1915 return; 1916 } 1917 version = elem.data.integer; 1918 length -= count; 1919 np += count; 1920 1921 switch (version) { 1922 case SNMP_VERSION_1: 1923 case SNMP_VERSION_2: 1924 community_print(ndo, np, length, version); 1925 break; 1926 case SNMP_VERSION_3: 1927 v3msg_print(ndo, np, length); 1928 break; 1929 default: 1930 ND_PRINT("[version = %d]", elem.data.integer); 1931 break; 1932 } 1933 1934 if (ndo->ndo_vflag) { 1935 ND_PRINT("} "); 1936 } 1937} 1938