I'm implementing snmpv3 in our erlang snmp client at the moment, so I thought
it'd be nice if tcpdump was able to understand it too.  I've roughly copied the
output formatting from tcpdump.org tcpdump, but the code is all my own work.
Unlike the other tcpdump, this just says '[PDU encrypted]' for encrypted
packets rather than complaining it got the wrong object type for the PDU.

ok?


Index: print-snmp.c
===================================================================
RCS file: /cvs/src/usr.sbin/tcpdump/print-snmp.c,v
retrieving revision 1.23
diff -u -p -r1.23 print-snmp.c
--- print-snmp.c        20 Sep 2018 12:23:13 -0000      1.23
+++ print-snmp.c        26 Nov 2018 01:36:38 -0000
@@ -321,7 +321,17 @@ struct be {
  * Defaults for SNMP PDU components
  */
 #define DEF_COMMUNITY "public"
-#define DEF_VERSION 1
+#define SNMPV1_VERSION 0
+#define SNMPV2_VERSION 1
+#define SNMPV3_VERSION 3
+
+/*
+ * SNMPv3 message flags
+ */
+#define SNMPV3_FLAG_AUTH 1
+#define SNMPV3_FLAG_PRIV 2
+
+#define SNMPV3_SEC_USM 3
 
 /*
  * constants for ASN.1 decoding
@@ -762,6 +772,32 @@ asn1_decode(u_char *p, u_int length)
  *             community OCTET STRING,
  *             data ANY        -- PDUs
  *     }
+ *
+ * SNMPv3 header
+ *     SEQUENCE {
+ *             version INTEGER {version-3(3)},
+ *             header SEQUENCE {
+ *                     msgID INTEGER,
+ *                     msgMaxSize INTEGER,
+ *                     msgFlags OCTET STRING,
+ *                     msgSecurityModel INTEGER {sec-usm(3)}
+ *             },
+ *             msgSecurityParameters OCTET STRING,
+ *             scopedPDU SEQUENCE {
+ *                     contextEngineID OCTET STRING,
+ *                     contextName OCTET STRING,
+ *                     data ANY        -- PDUs
+ *             }
+ *     }
+ * SNMPv3 USM parameters (msgSecurityParameters)
+ *     SEQUENCE {
+ *             engineID OCTET STRING,
+ *             engineBoots INTEGER,
+ *             engineTime INTEGER,
+ *             username OCTET STRING,
+ *             auth OCTET STRING,
+ *             privacy OCTET STRING
+ *     }
  * PDUs for all but Trap: (see rfc1157 from page 15 on)
  *     SEQUENCE {
  *             request-id INTEGER,
@@ -1032,6 +1068,254 @@ trap_print(const u_char *np, u_int lengt
        return;
 }
 
+static int
+snmpv3_print_usm(const u_char *np, u_int length)
+{
+       struct be elem;
+       int count;
+
+       /* usm Sequence */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if (elem.type != BE_SEQ) {
+               fputs("[!usm SEQ]", stdout);
+               asn1_print(&elem);
+               return (1);
+       }
+
+       /* descend */
+       length = elem.asnlen;
+       np = (u_char *)elem.data.raw;
+
+       /* engineID */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if (elem.type != BE_STR) {
+               fputs("[!engineID STR]", stdout);
+               asn1_print(&elem);
+               return (1);
+       }
+       np += count;
+       length -= count;
+
+       /* engineBoots */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if (elem.type != BE_INT) {
+               fputs("[!engineBoots INT]", stdout);
+               asn1_print(&elem);
+               return (1);
+       }
+       np += count;
+       length -= count;
+
+       /* engineTime */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if (elem.type != BE_INT) {
+               fputs("[!engineTime INT]", stdout);
+               asn1_print(&elem);
+               return (1);
+       }
+       np += count;
+       length -= count;
+
+       /* username */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if (elem.type != BE_STR) {
+               fputs("[!username STR]", stdout);
+               asn1_print(&elem);
+               return (1);
+       }
+       printf("U=%.*s ", (int)elem.asnlen, elem.data.str);
+       np += count;
+       length -= count;
+
+       /* auth and privacy follow, but we don't need to look */
+       return (0);
+}
+
+static int
+snmpv3_print_header(const u_char *np, u_int length, int *encrypted)
+{
+       struct be elem;
+       int count;
+
+       /* msgID */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if (elem.type != BE_INT) {
+               fputs("[!msgID INT]", stdout);
+               asn1_print(&elem);
+               return (1);
+       }
+       np += count;
+       length -= count;
+
+       /* msgMaxSize */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if (elem.type != BE_INT) {
+               fputs("[!msgMaxSize INT]", stdout);
+               asn1_print(&elem);
+               return (1);
+       }
+       np += count;
+       length -= count;
+
+       /* msgFlags */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if ((elem.type != BE_STR) || (elem.asnlen != 1)) {
+               printf("[!msgFlags STR/%d]", elem.asnlen);
+               asn1_print(&elem);
+               return (1);
+       }
+       fputs("F=", stdout);
+       if (elem.data.str[0] & SNMPV3_FLAG_AUTH)
+               fputs("a", stdout);
+       if (elem.data.str[0] & SNMPV3_FLAG_PRIV) {
+               fputs("p", stdout);
+               *encrypted = 1;
+       }
+       fputs(" ", stdout);
+       np += count;
+       length -= count;
+
+       /* msgSecurityModel */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return (1);
+       if (elem.type != BE_INT) {
+               fputs("[!msgSecurityModel INT]", stdout);
+               asn1_print(&elem);
+               return (1);
+       }
+       if (elem.data.integer != SNMPV3_SEC_USM) {
+               printf("[msgSecurityModel(%d)!=USM]", elem.data.integer);
+               return (1);
+       }
+
+       return (0);
+}
+
+/*
+ * Decode SNMPv3 header and pass on to PDU printing routines
+ *
+ * This starts after the initial sequence and version fields
+ * have been decoded.
+ */
+static void
+snmpv3_print(const u_char *np, u_int length)
+{
+       struct be elem, pdu;
+       int count, encrypted;
+
+       encrypted = 0;
+
+       /* header Sequence */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return;
+       if (elem.type != BE_SEQ) {
+               fputs("[!header SEQ]", stdout);
+               asn1_print(&elem);
+               return;
+       }
+       length -= count;
+       np += count;
+
+       if (snmpv3_print_header((u_char *)elem.data.raw, elem.asnlen,
+           &encrypted))
+               return;
+
+       /* msgSecurityParameters */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return;
+       if (elem.type != BE_STR) {
+               fputs("[!msgSecurityParameters STR]", stdout);
+               asn1_print(&elem);
+               return;
+       }
+       np += count;
+       length -= count;
+
+       if (snmpv3_print_usm((u_char *)elem.data.raw, elem.asnlen))
+               return;
+
+       if (encrypted) {
+               printf("[PDU encrypted]");
+               return;
+       }
+
+       /* scopedPDU Sequence */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return;
+       if (elem.type != BE_SEQ) {
+               fputs("[!scopedPDU SEQ]", stdout);
+               asn1_print(&elem);
+               return;
+       }
+
+       /* descend */
+       length = elem.asnlen;
+       np = (u_char *)elem.data.raw;
+
+       /* contextEngineID */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return;
+       if (elem.type != BE_STR) {
+               fputs("[!contextEngineID STR]", stdout);
+               asn1_print(&elem);
+               return;
+       }
+       fputs("E=", stdout);
+       asn1_print(&elem);
+       np += count;
+       length -= count;
+       fputs(" ", stdout);
+       
+       /* contextName */
+       if ((count = asn1_parse(np, length, &elem)) < 0)
+               return;
+       if (elem.type != BE_STR) {
+               fputs("[!contextName STR]", stdout);
+               asn1_print(&elem);
+               return;
+       }
+       printf("C=%.*s ", (int)elem.asnlen, elem.data.str);
+       np += count;
+       length -= count;
+
+       /* PDU (Context) */
+       if ((count = asn1_parse(np, length, &pdu)) < 0)
+               return;
+       if (pdu.type != BE_PDU) {
+               fputs("[no PDU]", stdout);
+               return;
+       }
+       if (count < length)
+               printf("[%d extra after PDU]", length - count);
+       asn1_print(&pdu);
+       /* descend into PDU */
+       length = pdu.asnlen;
+       np = (u_char *)pdu.data.raw;
+
+       switch (pdu.id) {
+       case TRAP:
+               trap_print(np, length);
+               break;
+       case GETREQ:
+       case GETNEXTREQ:
+       case GETRESP:
+       case SETREQ:
+       case GETBULKREQ:
+       case INFORMREQ:
+       case TRAPV2:
+       case REPORT:
+               snmppdu_print(pdu.id, np, length);
+               break;
+       }
+}
 /*
  * Decode SNMP header and pass on to PDU printing routines
  */
@@ -1070,13 +1354,19 @@ snmp_print(const u_char *np, u_int lengt
                asn1_print(&elem);
                return;
        }
-       /* only handle version 1 and 2 */
-       if (elem.data.integer > DEF_VERSION) {
-               printf("[version(%d)>%d]", elem.data.integer, DEF_VERSION);
-               return;
-       }
        length -= count;
        np += count;
+       switch (elem.data.integer) {
+       case SNMPV1_VERSION:
+       case SNMPV2_VERSION:
+               break;
+       case SNMPV3_VERSION:
+               snmpv3_print(np, length);
+               return;
+       default:
+               printf("[version(%d)]", elem.data.integer);
+               return;
+       }
 
        /* Community (String) */
        if ((count = asn1_parse(np, length, &elem)) < 0)

Reply via email to