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