Anyone willing to look into this one? Or someone objecting to me committing this?
On 9/26/19 2:33 PM, Martijn van Duren wrote: > On 9/26/19 9:54 AM, Martijn van Duren wrote: >> Hello, >> >> I reckon this will be on of the last major additions. >> Adding "snmp set" allows us to run snmpd's regress without installing >> netsnmp. :-) >> >> Tested with snmpd's regress test. >> >> Majority of diff is moving oid/value parsing from snmp trap to a >> separate function. >> >> OK? >> >> martijn@ > > Some manpage feedback from jmc. > > Index: snmp.1 > =================================================================== > RCS file: /cvs/src/usr.bin/snmp/snmp.1,v > retrieving revision 1.6 > diff -u -p -r1.6 snmp.1 > --- snmp.1 18 Sep 2019 09:54:36 -0000 1.6 > +++ snmp.1 26 Sep 2019 12:32:38 -0000 > @@ -110,6 +110,28 @@ > .Ar agent > .Op Ar oid > .Nm > +.Cm set > +.Op Fl A Ar authpass > +.Op Fl a Ar digest > +.Op Fl c Ar community > +.Op Fl E Ar ctxengineid > +.Op Fl e Ar secengineid > +.Op Fl K Ar localpriv > +.Op Fl k Ar localauth > +.Op Fl l Ar seclevel > +.Op Fl n Ar ctxname > +.Op Fl O Cm afnQqSvx > +.Op Fl r Ar retries > +.Op Fl t Ar timeout > +.Op Fl u Ar user > +.Op Fl v Ar version > +.Op Fl X Ar privpass > +.Op Fl x Ar cipher > +.Op Fl Z Ar boots , Ns Ar time > +.Ar agent > +.Ar varoid type value > +.Oo Ar varoid type value Oc ... > +.Nm > .Cm trap > .Op Fl A Ar authpass > .Op Fl a Ar digest > @@ -182,6 +204,12 @@ This uses the > subcommand internally to retrieve multiple MIBs at a time. > This command is not available for > .Fl v Cm 1 . > +.It Cm set > +Set one or more OID to a new value. > +The triple > +.Ar varoid , type , value > +is described in > +.Sx Data types . > .It Cm trap > Send a trap message to the > .Ar agent . > @@ -194,8 +222,8 @@ The > .Ar trapoid > is the identification OID used by the trap handler to determine its action. > The triple > -.Op Ar varoid , type, value > -is described below > +.Op Ar varoid , type , value > +is described in > .Sx Data types . > This command is not available for > .Fl v Cm 1 . > Index: snmp.c > =================================================================== > RCS file: /cvs/src/usr.bin/snmp/snmp.c,v > retrieving revision 1.6 > diff -u -p -r1.6 snmp.c > --- snmp.c 18 Sep 2019 09:54:36 -0000 1.6 > +++ snmp.c 26 Sep 2019 12:32:38 -0000 > @@ -249,6 +249,22 @@ fail: > return NULL; > } > > +struct ber_element * > +snmp_set(struct snmp_agent *agent, struct ber_element *vblist) > +{ > + struct ber_element *pdu; > + > + if ((pdu = ber_add_sequence(NULL)) == NULL) > + return NULL; > + if (ber_printf_elements(pdu, "tddd{e", BER_CLASS_CONTEXT, > + SNMP_C_SETREQ, arc4random() & 0x7fffffff, 0, 0, vblist) == NULL) { > + ber_free_elements(pdu); > + return NULL; > + } > + > + return snmp_resolve(agent, pdu, 1); > +} > + > static struct ber_element * > snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply) > { > Index: snmp.h > =================================================================== > RCS file: /cvs/src/usr.bin/snmp/snmp.h,v > retrieving revision 1.5 > diff -u -p -r1.5 snmp.h > --- snmp.h 18 Sep 2019 09:54:36 -0000 1.5 > +++ snmp.h 26 Sep 2019 12:32:38 -0000 > @@ -164,6 +164,7 @@ struct ber_element * > struct ber_element *snmp_getnext(struct snmp_agent *, struct ber_oid *, > size_t); > struct ber_element * > snmp_getbulk(struct snmp_agent *, struct ber_oid *, size_t, int, int); > +struct ber_element *snmp_set(struct snmp_agent *, struct ber_element *); > int snmp_trap(struct snmp_agent *, struct timespec *, struct ber_oid *, > struct ber_element *); > > Index: snmpc.c > =================================================================== > RCS file: /cvs/src/usr.bin/snmp/snmpc.c,v > retrieving revision 1.11 > diff -u -p -r1.11 snmpc.c > --- snmpc.c 18 Sep 2019 09:54:36 -0000 1.11 > +++ snmpc.c 26 Sep 2019 12:32:38 -0000 > @@ -46,6 +46,7 @@ > > int snmpc_get(int, char *[]); > int snmpc_walk(int, char *[]); > +int snmpc_set(int, char *[]); > int snmpc_trap(int, char *[]); > int snmpc_mibtree(int, char *[]); > struct snmp_agent *snmpc_connect(char *, char *); > @@ -53,6 +54,7 @@ int snmpc_parseagent(char *, char *); > int snmpc_print(struct ber_element *); > __dead void snmpc_printerror(enum snmp_error, char *); > char *snmpc_hex2bin(char *, size_t *); > +struct ber_element *snmpc_varbindparse(int, char *[]); > void usage(void); > > struct snmp_app { > @@ -69,6 +71,7 @@ struct snmp_app snmp_apps[] = { > { "walk", 1, "C:", "[-C cIipt] [-C E endoid] agent [oid]", snmpc_walk }, > { "bulkget", 1, "C:", "[-C n<nonrep>r<maxrep>] agent oid ...", > snmpc_get }, > { "bulkwalk", 1, "C:", "[-C cipn<nonrep>r<maxrep>] agent [oid]", > snmpc_walk }, > + { "set", 1, NULL, "agent oid type value [oid type value] ...", > snmpc_set }, > { "trap", 1, NULL, "agent uptime oid [oid type value] ...", snmpc_trap > }, > { "mibtree", 0, "O:", "[-O fnS]", snmpc_mibtree } > }; > @@ -666,19 +669,54 @@ snmpc_walk(int argc, char *argv[]) > } > > int > +snmpc_set(int argc, char *argv[]) > +{ > + struct snmp_agent *agent; > + struct ber_element *pdu, *varbind; > + int errorstatus, errorindex; > + int class; > + unsigned type; > + > + if (argc < 4) > + usage(); > + if ((agent = snmpc_connect(argv[0], "161")) == NULL) > + err(1, "%s", snmp_app->name); > + argc--; > + argv++; > + > + if (argc < 3 || argc % 3 != 0) > + usage(); > + > + if (pledge("stdio", NULL) == -1) > + err(1, "pledge"); > + > + pdu = snmp_set(agent, snmpc_varbindparse(argc, argv)); > + > + (void) ber_scanf_elements(pdu, "t{Sdd{e", &class, &type, &errorstatus, > + &errorindex, &varbind); > + if (errorstatus != 0) > + snmpc_printerror((enum snmp_error) errorstatus, > + argv[errorindex - 1]); > + > + if (class == BER_CLASS_CONTEXT && type == SNMP_C_REPORT) > + printf("Received report:\n"); > + for (; varbind != NULL; varbind = varbind->be_next) { > + if (!snmpc_print(varbind)) > + err(1, "Can't print response"); > + } > + ber_free_elements(pdu); > + snmp_free_agent(agent); > + return 0; > +} > + > +int > snmpc_trap(int argc, char *argv[]) > { > struct snmp_agent *agent; > struct timespec ts; > - struct ber_oid trapoid, oid, oidval; > - struct in_addr addr4; > - char *addr = (char *)&addr4; > - char *str = NULL, *tmpstr, *endstr; > + struct ber_oid trapoid; > const char *errstr = NULL; > - struct ber_element *varbind = NULL, *pdu = NULL; > long long lval; > - int i, ret; > - size_t strl, byte; > > if (argc < 3 || argc % 3 != 0) > usage(); > @@ -706,164 +744,8 @@ snmpc_trap(int argc, char *argv[]) > > argc -= 3; > argv += 3; > - for (i = 0; i < argc; i += 3) { > - if (smi_string2oid(argv[i], &oid) == -1) > - errx(1, "Invalid oid: %s\n", argv[i]); > - switch (argv[i + 1][0]) { > - case 'a': > - ret = inet_pton(AF_INET, argv[i + 2], &addr4); > - if (ret == -1) > - err(1, "inet_pton"); > - if (ret == 0) > - errx(1, "%s: Bad value notation (%s)", argv[i], > - argv[i + 2]); > - if ((varbind = ber_printf_elements(varbind, "{Oxt}", > - &oid, addr, sizeof(addr4), BER_CLASS_APPLICATION, > - SNMP_T_IPADDR)) == NULL) > - err(1, "ber_printf_elements"); > - break; > - case 'b': > - tmpstr = argv[i + 2]; > - strl = 0; > - do { > - lval = strtoll(tmpstr, &endstr, 10); > - if (endstr[0] != ' ' && endstr[0] != '\t' && > - endstr[0] != ',' && endstr[0] != '\0') > - errx(1, "%s: Bad value notation (%s)", > - argv[i], argv[i + 2]); > - if (tmpstr == endstr) { > - tmpstr++; > - continue; > - } > - if (lval < 0) > - errx(1, "%s: Bad value notation (%s)", > - argv[i], argv[i + 2]); > - byte = lval / 8; > - if (byte >= strl) { > - if ((str = recallocarray(str, strl, > - byte + 1, 1)) == NULL) > - err(1, "malloc"); > - strl = byte + 1; > - } > - str[byte] |= 0x80 >> (lval % 8); > - tmpstr = endstr + 1; > - } while (endstr[0] != '\0'); > - /* > - * RFC3416 Section 2.5 > - * A BITS value is encoded as an OCTET STRING > - */ > - goto pastestring; > - case 'c': > - lval = strtonum(argv[i + 2], INT32_MIN, INT32_MAX, > - &errstr); > - if (errstr != NULL) > - errx(1, "%s: Bad value notation (%s)", argv[i], > - argv[i + 2]); > - if ((varbind = ber_printf_elements(varbind, "{Oit}", > - &oid, lval, BER_CLASS_APPLICATION, > - SNMP_T_COUNTER32)) == NULL) > - err(1, "ber_printf_elements"); > - break; > - case 'd': > - /* String always shrinks */ > - if ((str = malloc(strlen(argv[i + 2]))) == NULL) > - err(1, "malloc"); > - tmpstr = argv[i + 2]; > - strl = 0; > - do { > - lval = strtoll(tmpstr, &endstr, 10); > - if (endstr[0] != ' ' && endstr[0] != '\t' && > - endstr[0] != '\0') > - errx(1, "%s: Bad value notation (%s)", > - argv[i], argv[i + 2]); > - if (tmpstr == endstr) { > - tmpstr++; > - continue; > - } > - if (lval < 0 || lval > 0xff) > - errx(1, "%s: Bad value notation (%s)", > - argv[i], argv[i + 2]); > - str[strl++] = (unsigned char) lval; > - tmpstr = endstr + 1; > - } while (endstr[0] != '\0'); > - goto pastestring; > - case 'u': > - case 'i': > - lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX, > - &errstr); > - if (errstr != NULL) > - errx(1, "%s: Bad value notation (%s)", argv[i], > - argv[i + 2]); > - if ((varbind = ber_printf_elements(varbind, "{Oi}", > - &oid, lval)) == NULL) > - err(1, "ber_printf_elements"); > - break; > - case 'n': > - if ((varbind = ber_printf_elements(varbind, "{O0}", > - &oid)) == NULL) > - err(1, "ber_printf_elements"); > - break; > - case 'o': > - if (smi_string2oid(argv[i + 2], &oidval) == -1) > - errx(1, "%s: Unknown Object Identifier (Sub-id " > - "not found: (top) -> %s)", argv[i], > - argv[i + 2]); > - if ((varbind = ber_printf_elements(varbind, "{OO}", > - &oid, &oidval)) == NULL) > - err(1, "ber_printf_elements"); > - break; > - case 's': > - if ((str = strdup(argv[i + 2])) == NULL) > - err(1, NULL); > - strl = strlen(argv[i + 2]); > -pastestring: > - if ((varbind = ber_printf_elements(varbind, "{Ox}", > - &oid, str, strl)) == NULL) > - err(1, "ber_printf_elements"); > - free(str); > - break; > - case 't': > - lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX, > - &errstr); > - if (errstr != NULL) > - errx(1, "%s: Bad value notation (%s)", argv[i], > - argv[i + 2]); > - if ((varbind = ber_printf_elements(varbind, "{Oit}", > - &oid, lval, BER_CLASS_APPLICATION, > - SNMP_T_TIMETICKS)) == NULL) > - err(1, "ber_printf_elements"); > - break; > - case 'x': > - /* String always shrinks */ > - if ((str = malloc(strlen(argv[i + 2]))) == NULL) > - err(1, "malloc"); > - tmpstr = argv[i + 2]; > - strl = 0; > - do { > - lval = strtoll(tmpstr, &endstr, 16); > - if (endstr[0] != ' ' && endstr[0] != '\t' && > - endstr[0] != '\0') > - errx(1, "%s: Bad value notation (%s)", > - argv[i], argv[i + 2]); > - if (tmpstr == endstr) { > - tmpstr++; > - continue; > - } > - if (lval < 0 || lval > 0xff) > - errx(1, "%s: Bad value notation (%s)", > - argv[i], argv[i + 2]); > - str[strl++] = (unsigned char) lval; > - tmpstr = endstr + 1; > - } while (endstr[0] != '\0'); > - goto pastestring; > - default: > - usage(); > - } > - if (pdu == NULL) > - pdu = varbind; > - } > > - snmp_trap(agent, &ts, &trapoid, pdu); > + snmp_trap(agent, &ts, &trapoid, snmpc_varbindparse(argc, argv)); > > return 0; > } > @@ -1116,6 +998,181 @@ fail: > errno = EINVAL; > free(decstr); > return NULL; > +} > + > +struct ber_element * > +snmpc_varbindparse(int argc, char *argv[]) > +{ > + struct ber_oid oid, oidval; > + struct in_addr addr4; > + char *addr = (char *)&addr4; > + char *str = NULL, *tmpstr, *endstr; > + const char *errstr = NULL; > + struct ber_element *varbind = NULL, *vblist = NULL; > + int i, ret; > + size_t strl, byte; > + long long lval; > + > + if (argc % 3 != 0) > + usage(); > + for (i = 0; i < argc; i += 3) { > + if (smi_string2oid(argv[i], &oid) == -1) > + errx(1, "Invalid oid: %s\n", argv[i]); > + switch (argv[i + 1][0]) { > + case 'a': > + ret = inet_pton(AF_INET, argv[i + 2], &addr4); > + if (ret == -1) > + err(1, "inet_pton"); > + if (ret == 0) > + errx(1, "%s: Bad value notation (%s)", argv[i], > + argv[i + 2]); > + if ((varbind = ber_printf_elements(varbind, "{Oxt}", > + &oid, addr, sizeof(addr4), BER_CLASS_APPLICATION, > + SNMP_T_IPADDR)) == NULL) > + err(1, "ber_printf_elements"); > + break; > + case 'b': > + tmpstr = argv[i + 2]; > + strl = 0; > + do { > + lval = strtoll(tmpstr, &endstr, 10); > + if (endstr[0] != ' ' && endstr[0] != '\t' && > + endstr[0] != ',' && endstr[0] != '\0') > + errx(1, "%s: Bad value notation (%s)", > + argv[i], argv[i + 2]); > + if (tmpstr == endstr) { > + tmpstr++; > + continue; > + } > + if (lval < 0) > + errx(1, "%s: Bad value notation (%s)", > + argv[i], argv[i + 2]); > + byte = lval / 8; > + if (byte >= strl) { > + if ((str = recallocarray(str, strl, > + byte + 1, 1)) == NULL) > + err(1, "malloc"); > + strl = byte + 1; > + } > + str[byte] |= 0x80 >> (lval % 8); > + tmpstr = endstr + 1; > + } while (endstr[0] != '\0'); > + /* > + * RFC3416 Section 2.5 > + * A BITS value is encoded as an OCTET STRING > + */ > + goto pastestring; > + case 'c': > + lval = strtonum(argv[i + 2], INT32_MIN, INT32_MAX, > + &errstr); > + if (errstr != NULL) > + errx(1, "%s: Bad value notation (%s)", argv[i], > + argv[i + 2]); > + if ((varbind = ber_printf_elements(varbind, "{Oit}", > + &oid, lval, BER_CLASS_APPLICATION, > + SNMP_T_COUNTER32)) == NULL) > + err(1, "ber_printf_elements"); > + break; > + case 'd': > + /* String always shrinks */ > + if ((str = malloc(strlen(argv[i + 2]))) == NULL) > + err(1, "malloc"); > + tmpstr = argv[i + 2]; > + strl = 0; > + do { > + lval = strtoll(tmpstr, &endstr, 10); > + if (endstr[0] != ' ' && endstr[0] != '\t' && > + endstr[0] != '\0') > + errx(1, "%s: Bad value notation (%s)", > + argv[i], argv[i + 2]); > + if (tmpstr == endstr) { > + tmpstr++; > + continue; > + } > + if (lval < 0 || lval > 0xff) > + errx(1, "%s: Bad value notation (%s)", > + argv[i], argv[i + 2]); > + str[strl++] = (unsigned char) lval; > + tmpstr = endstr + 1; > + } while (endstr[0] != '\0'); > + goto pastestring; > + case 'u': > + case 'i': > + lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX, > + &errstr); > + if (errstr != NULL) > + errx(1, "%s: Bad value notation (%s)", argv[i], > + argv[i + 2]); > + if ((varbind = ber_printf_elements(varbind, "{Oi}", > + &oid, lval)) == NULL) > + err(1, "ber_printf_elements"); > + break; > + case 'n': > + if ((varbind = ber_printf_elements(varbind, "{O0}", > + &oid)) == NULL) > + err(1, "ber_printf_elements"); > + break; > + case 'o': > + if (smi_string2oid(argv[i + 2], &oidval) == -1) > + errx(1, "%s: Unknown Object Identifier (Sub-id " > + "not found: (top) -> %s)", argv[i], > + argv[i + 2]); > + if ((varbind = ber_printf_elements(varbind, "{OO}", > + &oid, &oidval)) == NULL) > + err(1, "ber_printf_elements"); > + break; > + case 's': > + if ((str = strdup(argv[i + 2])) == NULL) > + err(1, NULL); > + strl = strlen(argv[i + 2]); > +pastestring: > + if ((varbind = ber_printf_elements(varbind, "{Ox}", > + &oid, str, strl)) == NULL) > + err(1, "ber_printf_elements"); > + free(str); > + break; > + case 't': > + lval = strtonum(argv[i + 2], LLONG_MIN, LLONG_MAX, > + &errstr); > + if (errstr != NULL) > + errx(1, "%s: Bad value notation (%s)", argv[i], > + argv[i + 2]); > + if ((varbind = ber_printf_elements(varbind, "{Oit}", > + &oid, lval, BER_CLASS_APPLICATION, > + SNMP_T_TIMETICKS)) == NULL) > + err(1, "ber_printf_elements"); > + break; > + case 'x': > + /* String always shrinks */ > + if ((str = malloc(strlen(argv[i + 2]))) == NULL) > + err(1, "malloc"); > + tmpstr = argv[i + 2]; > + strl = 0; > + do { > + lval = strtoll(tmpstr, &endstr, 16); > + if (endstr[0] != ' ' && endstr[0] != '\t' && > + endstr[0] != '\0') > + errx(1, "%s: Bad value notation (%s)", > + argv[i], argv[i + 2]); > + if (tmpstr == endstr) { > + tmpstr++; > + continue; > + } > + if (lval < 0 || lval > 0xff) > + errx(1, "%s: Bad value notation (%s)", > + argv[i], argv[i + 2]); > + str[strl++] = (unsigned char) lval; > + tmpstr = endstr + 1; > + } while (endstr[0] != '\0'); > + goto pastestring; > + default: > + usage(); > + } > + if (vblist == NULL) > + vblist = varbind; > + } > + > + return vblist; > } > > __dead void >