diff options
-rw-r--r-- | usr.sbin/snmpctl/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/snmpctl/parser.c | 118 | ||||
-rw-r--r-- | usr.sbin/snmpctl/parser.h | 19 | ||||
-rw-r--r-- | usr.sbin/snmpctl/snmpctl.c | 151 |
4 files changed, 203 insertions, 89 deletions
diff --git a/usr.sbin/snmpctl/Makefile b/usr.sbin/snmpctl/Makefile index ea500477728..55cc688a3d0 100644 --- a/usr.sbin/snmpctl/Makefile +++ b/usr.sbin/snmpctl/Makefile @@ -1,9 +1,9 @@ -# $Id: Makefile,v 1.6 2014/01/18 05:54:52 martynas Exp $ +# $Id: Makefile,v 1.7 2014/04/14 12:56:21 blambert Exp $ .PATH: ${.CURDIR}/../snmpd PROG= snmpctl -SRCS= log.c ber.c smi.c snmpclient.c snmpctl.c parser.c +SRCS= log.c ber.c smi.c snmpclient.c snmpctl.c parser.c agentx.c MAN= snmpctl.8 diff --git a/usr.sbin/snmpctl/parser.c b/usr.sbin/snmpctl/parser.c index c0144308efc..1cd549f8deb 100644 --- a/usr.sbin/snmpctl/parser.c +++ b/usr.sbin/snmpctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.14 2013/10/07 11:40:09 reyk Exp $ */ +/* $OpenBSD: parser.c,v 1.15 2014/04/14 12:56:21 blambert Exp $ */ /* * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org> @@ -199,8 +199,6 @@ static const struct token t_string[] = { }; static struct parse_result res; -static struct imsgbuf *ibuf; -static struct snmp_imsg sm; const struct token *match_token(char *, const struct token []); void show_valid_args(const struct token []); @@ -214,6 +212,7 @@ parse(int argc, char *argv[]) bzero(&res, sizeof(res)); res.version = -1; TAILQ_INIT(&res.oids); + TAILQ_INIT(&res.varbinds); while (argc >= 0) { if ((match = match_token(argv[0], table)) == NULL) { @@ -245,16 +244,9 @@ match_token(char *word, const struct token table[]) u_int i, match = 0; const struct token *t = NULL; const char *errs = NULL; - u_int32_t u; - int32_t d; - int64_t l; - struct iovec iov[2]; - int iovcnt = 0; - struct in_addr in4; - struct in6_addr in6; + int terminal = 0; struct parse_val *val; - - bzero(&iov, sizeof(iov)); + struct parse_varbind *vb = NULL; for (i = 0; table[i].type != ENDTOKEN; i++) { switch (table[i].type) { @@ -319,109 +311,99 @@ match_token(char *word, const struct token table[]) strlen(word)) == 0) { match++; t = &table[i]; - sm.snmp_type = t->value; + vb = TAILQ_LAST(&res.varbinds, parse_varbinds); + if (vb == NULL) + errx(1, "inconsistent varbind list"); + vb->sm.snmp_type = t->value; if (t->value == SNMP_NULL) - iovcnt = 1; + terminal = 1; } break; case TRAPOID: if (word == NULL || strlen(word) == 0) break; - if (ibuf == NULL && - (ibuf = malloc(sizeof(struct imsgbuf))) == NULL) + if ((res.trapoid = strdup(word)) == NULL) err(1, "malloc"); - res.ibuf = ibuf; - imsg_init(ibuf, -1); - - /* Create a new trap */ - imsg_compose(ibuf, IMSG_SNMP_TRAP, - 0, 0, -1, NULL, 0); - - /* First element must be the trap OID. */ - bzero(&sm, sizeof(sm)); - sm.snmp_type = SNMP_NULL; - if (strlcpy(sm.snmp_oid, word, - sizeof(sm.snmp_oid)) >= sizeof(sm.snmp_oid)) - errx(1, "trap oid too long"); - if (imsg_compose(ibuf, IMSG_SNMP_ELEMENT, 0, 0, -1, - &sm, sizeof(sm)) == -1) - errx(1, "imsg"); - match++; t = &table[i]; break; case ELEMENTOBJECT: if (word == NULL || strlen(word) == 0) break; - bzero(&sm, sizeof(sm)); - if (strlcpy(sm.snmp_oid, word, - sizeof(sm.snmp_oid)) >= sizeof(sm.snmp_oid)) + if ((vb = calloc(1, sizeof(*vb))) == NULL) + errx(1, "calloc"); + if (strlcpy(vb->sm.snmp_oid, word, + sizeof(vb->sm.snmp_oid)) >= sizeof(vb->sm.snmp_oid)) errx(1, "oid too long"); + + TAILQ_INSERT_TAIL(&res.varbinds, vb, vb_entry); match++; t = &table[i]; break; case IPADDRVAL: if (word == NULL || strlen(word) == 0) break; - if (inet_pton(AF_INET, word, &in4) == -1) { + vb = TAILQ_LAST(&res.varbinds, parse_varbinds); + if (vb == NULL) + errx(1, "inconsistent varbind list"); + if (inet_pton(AF_INET, word, &vb->u.in4) == -1) { /* XXX the SNMP_IPADDR type is IPv4-only? */ - if (inet_pton(AF_INET6, word, &in6) == -1) + if (inet_pton(AF_INET6, word, &vb->u.in6) == -1) errx(1, "invalid IP address"); - iov[1].iov_len = sizeof(in6); - iov[1].iov_base = &in6; + vb->sm.snmp_len = sizeof(vb->u.in6); } else { - iov[1].iov_len = sizeof(in4); - iov[1].iov_base = &in4; + vb->sm.snmp_len = sizeof(vb->u.in4); } - iovcnt = 2; + terminal = 1; break; case INT32VAL: if (word == NULL || strlen(word) == 0) break; - d = strtonum(word, INT_MIN, INT_MAX, &errs); - iov[1].iov_len = sizeof(d); - iov[1].iov_base = &d; - iovcnt = 2; + vb = TAILQ_LAST(&res.varbinds, parse_varbinds); + if (vb == NULL) + errx(1, "inconsistent varbind list"); + vb->u.d = strtonum(word, INT_MIN, INT_MAX, &errs); + vb->sm.snmp_len = sizeof(vb->u.d); + terminal = 1; break; case UINT32VAL: if (word == NULL || strlen(word) == 0) break; - u = strtonum(word, 0, UINT_MAX, &errs); - iov[1].iov_len = sizeof(u); - iov[1].iov_base = &u; - iovcnt = 2; + vb = TAILQ_LAST(&res.varbinds, parse_varbinds); + if (vb == NULL) + errx(1, "inconsistent varbind list"); + vb->u.u = strtonum(word, 0, UINT_MAX, &errs); + vb->sm.snmp_len = sizeof(vb->u.u); + terminal = 1; break; case INT64VAL: if (word == NULL || strlen(word) == 0) break; - l = strtonum(word, INT64_MIN, INT64_MAX, &errs); - iov[1].iov_len = sizeof(l); - iov[1].iov_base = &l; - iovcnt = 2; + vb = TAILQ_LAST(&res.varbinds, parse_varbinds); + if (vb == NULL) + errx(1, "inconsistent varbind list"); + vb->u.l = strtonum(word, INT64_MIN, INT64_MAX, &errs); + vb->sm.snmp_len = sizeof(vb->u.l); + terminal = 1; break; case STRINGVAL: if (word == NULL || strlen(word) == 0) break; - iov[1].iov_len = strlen(word); - iov[1].iov_base = word; - iovcnt = 2; + vb = TAILQ_LAST(&res.varbinds, parse_varbinds); + if (vb == NULL) + errx(1, "inconsistent varbind list"); + vb->u.str = word; + vb->sm.snmp_len = strlen(word); + terminal = 1; break; case ENDTOKEN: break; } - if (iovcnt) + if (terminal) break; } - if (iovcnt) { - /* Write trap varbind element */ - sm.snmp_len = iov[1].iov_len; - iov[0].iov_len = sizeof(sm); - iov[0].iov_base = &sm; - if (imsg_composev(ibuf, IMSG_SNMP_ELEMENT, 0, 0, -1, - iov, iovcnt) == -1) - err(1, "imsg"); - + if (terminal) { t = &table[i]; } else if (match != 1) { if (word == NULL) diff --git a/usr.sbin/snmpctl/parser.h b/usr.sbin/snmpctl/parser.h index 3b41a4b6d99..d50a7ed96ca 100644 --- a/usr.sbin/snmpctl/parser.h +++ b/usr.sbin/snmpctl/parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.h,v 1.8 2013/10/07 11:40:09 reyk Exp $ */ +/* $OpenBSD: parser.h,v 1.9 2014/04/14 12:56:21 blambert Exp $ */ /* * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> @@ -32,11 +32,28 @@ struct parse_val { }; TAILQ_HEAD(parse_vals, parse_val); +struct parse_varbind { + struct snmp_imsg sm; + union { + uint32_t u; + int32_t d; + uint64_t l; + struct in_addr in4; + struct in6_addr in6; + char *str; + } u; + + TAILQ_ENTRY(parse_varbind) vb_entry; +}; +TAILQ_HEAD(parse_varbinds, parse_varbind); + struct parse_result { enum actions action; struct imsgbuf *ibuf; char *host; + char *trapoid; struct parse_vals oids; + struct parse_varbinds varbinds; char *community; int version; }; diff --git a/usr.sbin/snmpctl/snmpctl.c b/usr.sbin/snmpctl/snmpctl.c index ad24c2f2163..7a277e1fb60 100644 --- a/usr.sbin/snmpctl/snmpctl.c +++ b/usr.sbin/snmpctl/snmpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: snmpctl.c,v 1.19 2013/11/14 20:48:52 deraadt Exp $ */ +/* $OpenBSD: snmpctl.c,v 1.20 2014/04/14 12:56:21 blambert Exp $ */ /* * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> @@ -24,6 +24,7 @@ #include <sys/queue.h> #include <sys/un.h> #include <sys/tree.h> +#include <sys/uio.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -64,7 +65,9 @@ struct imsgname imsgunknown = { -1, "<unknown>", NULL }; -struct imsgbuf *ibuf; +void snmpctl_trap(int, struct parse_result *); + +struct imsgbuf ibuf; struct snmpd *env; struct oid mib_tree[] = MIB_TREE; @@ -153,20 +156,13 @@ main(int argc, char *argv[]) err(1, "connect: %s", sock); } - if (res->ibuf != NULL) { - ibuf = res->ibuf; - ibuf->fd = ibuf->w.fd = ctl_sock; - } else { - if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) - err(1, "malloc"); - imsg_init(ibuf, ctl_sock); - } + imsg_init(&ibuf, ctl_sock); done = 0; /* process user request */ switch (res->action) { case MONITOR: - imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0); + imsg_compose(&ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0); break; case NONE: case SHOW_MIB: @@ -175,23 +171,23 @@ main(int argc, char *argv[]) case BULKWALK: break; case TRAP: - imsg_compose(ibuf, IMSG_SNMP_END, 0, 0, -1, NULL, 0); - done = 1; + /* explicitly downgrade the socket */ + imsg_compose(&ibuf, IMSG_SNMP_AGENTX, 0, 0, -1, NULL, 0); break; } - while (ibuf->w.queued) - if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN) + while (ibuf.w.queued) + if (msgbuf_write(&ibuf.w) <= 0 && errno != EAGAIN) err(1, "write error"); while (!done) { - if ((n = imsg_read(ibuf)) == -1) + if ((n = imsg_read(&ibuf)) == -1) errx(1, "imsg_read error"); if (n == 0) errx(1, "pipe closed"); while (!done) { - if ((n = imsg_get(ibuf, &imsg)) == -1) + if ((n = imsg_get(&ibuf, &imsg)) == -1) errx(1, "imsg_get error"); if (n == 0) break; @@ -200,6 +196,11 @@ main(int argc, char *argv[]) done = monitor(&imsg); break; case TRAP: + if (imsg.hdr.type == IMSG_CTL_OK) { + snmpctl_trap(ctl_sock, res); + done = 1; + } else + errx(1, "snmpd refused connection"); break; case NONE: case SHOW_MIB: @@ -212,7 +213,6 @@ main(int argc, char *argv[]) } } close(ctl_sock); - free(ibuf); return (0); } @@ -269,3 +269,118 @@ monitor(struct imsg *imsg) return (done); } + +void +snmpctl_trap(int ctl_sock, struct parse_result *res) +{ + struct snmp_oid oid, oid1; + struct parse_varbind *vb; + struct agentx_handle *h; + struct agentx_pdu *pdu; + void *ptr; + + if ((h = snmp_agentx_fdopen(ctl_sock, "snmpctl", NULL)) == NULL) + err(1, "agentx socket"); + + if (ber_string2oid(res->trapoid, (struct ber_oid *)&oid) == -1) + errx(1, "bad trap oid %s", res->trapoid); + + if ((pdu = snmp_agentx_notify_pdu(&oid)) == NULL) + errx(1, "notify start"); + + while ((vb = TAILQ_FIRST(&res->varbinds)) != NULL) { + + if (ber_string2oid(vb->sm.snmp_oid, + (struct ber_oid *)&oid) == -1) + errx(1, "bad oid %s", vb->sm.snmp_oid); + + switch (vb->sm.snmp_type) { + + /* SNMP_GAUGE32 is handled here */ + case SNMP_INTEGER32: + case SNMP_NSAPADDR: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_INTEGER, + &vb->u.d, vb->sm.snmp_len) == -1) + errx(1, "varbind"); + break; + + case SNMP_OPAQUE: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OPAQUE, + &vb->u.d, vb->sm.snmp_len) == -1) + errx(1, "varbind"); + break; + + case SNMP_COUNTER32: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_COUNTER32, + &vb->u.d, vb->sm.snmp_len) == -1) + errx(1, "varbind"); + break; + + case SNMP_TIMETICKS: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_TIME_TICKS, + &vb->u.d, vb->sm.snmp_len) == -1) + errx(1, "varbind"); + break; + + case SNMP_UINTEGER32: + case SNMP_UNSIGNED32: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_INTEGER, + &vb->u.u, vb->sm.snmp_len) == -1) + errx(1, "varbind"); + break; + + case SNMP_COUNTER64: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_COUNTER64, + &vb->u.l, vb->sm.snmp_len) == -1) + errx(1, "varbind"); + break; + + case SNMP_IPADDR: + if (vb->sm.snmp_len == sizeof(struct in6_addr)) + ptr = &vb->u.in6; + else + ptr = &vb->u.in4; + + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OPAQUE, + ptr, vb->sm.snmp_len) == -1) + errx(1, "varbind"); + break; + + case SNMP_OBJECT: + if (ber_string2oid(vb->u.str, + (struct ber_oid *)&oid1) == -1) + errx(1, "invalid OID %s", vb->u.str); + + if (snmp_agentx_varbind(pdu, &oid, + AGENTX_OBJECT_IDENTIFIER, &oid1, + sizeof(oid1)) == -1) + errx(1, "varbind"); + break; + + case SNMP_BITSTRING: + case SNMP_OCTETSTRING: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OCTET_STRING, + vb->u.str, vb->sm.snmp_len) == -1) + errx(1, "varbind"); + break; + + case SNMP_NULL: + /* no data beyond the OID itself */ + if (snmp_agentx_varbind(pdu, &oid, AGENTX_NULL, + NULL, 0) == -1) + errx(1, "varbind"); + break; + } + + TAILQ_REMOVE(&res->varbinds, vb, vb_entry); + free(vb); + } + + if ((pdu = snmp_agentx_request(h, pdu)) == NULL) + err(1, "request: %i", h->error); + if (snmp_agentx_response(h, pdu) == -1) + errx(1, "response: %i", h->error); + snmp_agentx_pdu_free(pdu); + if (snmp_agentx_close(h, AGENTX_CLOSE_SHUTDOWN) == -1) + err(1, "close"); +} |