print-snmp.c 43 KB

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