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
>