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