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