On 10/3/19 9:21 AM, Sebastien Marie wrote:
> On Thu, Sep 26, 2019 at 02:33:11PM +0200, 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@
>
> few comments.
>
> thanks.
>
>> 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
>> @@ -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]);
>
> is "errorindex - 1" the right index ?
Yes: RFC 3416 section 4.1:
A variable binding is identified by its index value. The first variable
binding in a variable-binding list is index one, the second is index
two, etc.
I choose to use argv, because it's easier for the end user to see his
own input for where he mistyped instead of a double parsed oid to -O
based output.
>
> $ ./obj/snmp set 192.168.1.5 sysContact.0 s "test"
> snmp: Can't parse oid 192.168.1.5: Not writable
>
> Note that the same pattern is used in others commands.
Somehow the machines I test against don't return a not writable message.
Tested against snmpd, net-snmpd, HP Laserjet.
Could you show me a tcpdump -v output on this output?
>
>> +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();
>
> if I don't mess myself, callers already checks that 'argc % 3 != 0'. So I
> think
> the condition is more a defense for programmer error that user error, and I
> would use an assert(argc % 3 == 0) or abort().
I removed these checks from snmpc_{set,trap}. Even less code. :-)
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 3 Oct 2019 07:59:07 -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 3 Oct 2019 07:59:07 -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 3 Oct 2019 07:59:07 -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 3 Oct 2019 07:59:07 -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,22 +669,52 @@ 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 (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();
if (version == SNMP_V1)
errx(1, "trap is not supported for snmp v1");
@@ -706,164 +739,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 +993,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