diff options
author | 2014-04-14 12:58:04 +0000 | |
---|---|---|
committer | 2014-04-14 12:58:04 +0000 | |
commit | a3fcfb5964000876aa74632d554e80ff17d34583 (patch) | |
tree | 2c56ca01c38a31a2f7492a8b0bc31ce70d84b831 | |
parent | Adapt snmpctl to use AgentX protocol to send traps (diff) | |
download | wireguard-openbsd-a3fcfb5964000876aa74632d554e80ff17d34583.tar.xz wireguard-openbsd-a3fcfb5964000876aa74632d554e80ff17d34583.zip |
Adapt relayd to use AgentX protocol to send traps
ok reyk@ benno@
-rw-r--r-- | usr.sbin/relayd/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/relayd/agentx.c | 969 | ||||
-rw-r--r-- | usr.sbin/relayd/parse.y | 28 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.conf.5 | 19 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 10 | ||||
-rw-r--r-- | usr.sbin/relayd/snmp.c | 304 |
6 files changed, 1231 insertions, 104 deletions
diff --git a/usr.sbin/relayd/Makefile b/usr.sbin/relayd/Makefile index 76fd248686c..8e92b9012cf 100644 --- a/usr.sbin/relayd/Makefile +++ b/usr.sbin/relayd/Makefile @@ -1,10 +1,11 @@ -# $OpenBSD: Makefile,v 1.24 2014/01/18 05:54:51 martynas Exp $ +# $OpenBSD: Makefile,v 1.25 2014/04/14 12:58:04 blambert Exp $ PROG= relayd SRCS= parse.y log.c control.c ssl.c ssl_privsep.c \ relayd.c pfe.c pfe_filter.c pfe_route.c hce.c relay.c \ relay_http.c relay_udp.c carp.c check_icmp.c check_tcp.c \ - check_script.c name2id.c snmp.c shuffle.c proc.c config.c + check_script.c name2id.c snmp.c shuffle.c proc.c config.c \ + agentx.c MAN= relayd.8 relayd.conf.5 LDADD= -levent -lssl -lcrypto -lutil diff --git a/usr.sbin/relayd/agentx.c b/usr.sbin/relayd/agentx.c new file mode 100644 index 00000000000..fd815b620ab --- /dev/null +++ b/usr.sbin/relayd/agentx.c @@ -0,0 +1,969 @@ +/* $OpenBSD: agentx.c,v 1.1 2014/04/14 12:58:04 blambert Exp $ */ +/* + * Copyright (c) 2013,2014 Bret Stephen Lambert <blambert@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/uio.h> +#include <sys/un.h> + +#include <arpa/inet.h> + +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "snmp.h" +#include "ber.h" + +int snmp_agentx_octetstring(struct agentx_pdu *, char *, int); +int snmp_agentx_buffercheck(struct agentx_pdu *, size_t); +int snmp_agentx_oid(struct agentx_pdu *, struct snmp_oid *); +int snmp_agentx_buffer_consume(struct agentx_pdu *, u_int); +int snmp_agentx_int(struct agentx_pdu *, uint32_t *); +int snmp_agentx_int64(struct agentx_pdu *, uint64_t *); +int snmp_agentx_do_read_raw(struct agentx_pdu *, void *, int, int); +void snmp_agentx_update_ids(struct agentx_handle *, struct agentx_pdu *); +struct agentx_pdu * + agentx_find_inflight(struct agentx_handle *, uint32_t, uint32_t); + +#ifdef DEBUG +static void snmp_agentx_dump_hdr(struct agentx_hdr *); +#endif + +#define PDU_BUFLEN 256 + +/* snmpTrapOid.0 */ +struct snmp_oid trapoid_0 = { + .o_id = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }, + .o_n = 11 +}; + +/* + * AgentX handle allocation and management routines. + */ + +struct agentx_handle * +snmp_agentx_alloc(int s) +{ + struct agentx_handle *h; + + if ((h = calloc(1, sizeof(*h))) == NULL) + return (NULL); + + h->fd = s; + h->timeout = AGENTX_DEFAULT_TIMEOUT; + + TAILQ_INIT(&h->w); + TAILQ_INIT(&h->inflight); + + return (h); +} + +/* + * Synchronous open of unix socket path. + */ +struct agentx_handle * +snmp_agentx_open(const char *path, char *descr, struct snmp_oid *oid) +{ + struct sockaddr_un sun; + struct agentx_handle *h; + int s; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + return (NULL); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + + if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) + goto fail; + + if ((h = snmp_agentx_fdopen(s, descr, oid)) == NULL) + goto fail; + + return (h); + fail: + close(s); + return (NULL); +} + +/* + * Synchronous AgentX open operation over previously-opened socket. + */ +struct agentx_handle * +snmp_agentx_fdopen(int s, char *descr, struct snmp_oid *oid) +{ + struct agentx_handle *h; + struct agentx_pdu *pdu = NULL; + + if ((h = snmp_agentx_alloc(s)) == NULL) + return (NULL); + + if ((pdu = snmp_agentx_open_pdu(h, descr, oid)) == NULL || + (pdu = snmp_agentx_request(h, pdu)) == NULL || + snmp_agentx_open_response(h, pdu) == -1) { + if (pdu) + snmp_agentx_pdu_free(pdu); + snmp_agentx_free(h); + return (NULL); + } + + return (h); +} + +/* + * Synchronous close of agentx handle. + */ +int +snmp_agentx_close(struct agentx_handle *h, uint8_t reason) +{ + struct agentx_pdu *pdu; + int error = 0; + + if ((pdu = snmp_agentx_close_pdu(h, reason)) == NULL) + return (-1); + if ((pdu = snmp_agentx_request(h, pdu)) == NULL) + return (-1); + if (snmp_agentx_response(h, pdu) == -1) + error = -1; + + snmp_agentx_pdu_free(pdu); + + return (error); +} + +void +snmp_agentx_free(struct agentx_handle *h) +{ + struct agentx_pdu *pdu; + + if (h->fd != -1) + close(h->fd); + + while ((pdu = TAILQ_FIRST(&h->w))) { + TAILQ_REMOVE(&h->w, pdu, entry); + snmp_agentx_pdu_free(pdu); + } + while ((pdu = TAILQ_FIRST(&h->inflight))) { + TAILQ_REMOVE(&h->w, pdu, entry); + snmp_agentx_pdu_free(pdu); + } + if (h->r) + snmp_agentx_pdu_free(h->r); + + free(h); +} + +/* + * AgentX pdu allocation routines. + */ + +/* + * Allocate an AgentX PDU. + */ +struct agentx_pdu * +snmp_agentx_pdu_alloc(void) +{ + struct agentx_pdu *pdu; + + if ((pdu = calloc(1, sizeof(*pdu))) == NULL) + return (NULL); + if ((pdu->buffer = calloc(PDU_BUFLEN, sizeof(uint8_t))) == NULL) { + free(pdu); + return (NULL); + } + + pdu->buflen = PDU_BUFLEN; + + bzero(pdu->buffer, pdu->buflen); + pdu->ptr = pdu->buffer + sizeof(struct agentx_hdr); + pdu->ioptr = pdu->buffer; + pdu->hdr = (struct agentx_hdr *)pdu->buffer; + pdu->hdr->version = AGENTX_VERSION; + pdu->hdr->flags = AGENTX_LOCAL_BYTE_ORDER_FLAG; + pdu->hdr->reserved = 0; + pdu->hdr->length = 0; + pdu->datalen = sizeof(struct agentx_hdr); + + return (pdu); +} + +/* + * Read the response PDU for a generic operation. + */ +int +snmp_agentx_response(struct agentx_handle *h, struct agentx_pdu *pdu) +{ + struct agentx_response_data resp; + + if (snmp_agentx_read_raw(pdu, &resp, sizeof(resp)) == -1) + return (-1); + + if (!snmp_agentx_byteorder_native(pdu->hdr)) { + resp.error = snmp_agentx_int16_byteswap(resp.error); + resp.index = snmp_agentx_int16_byteswap(resp.index); + } + + h->error = resp.error; + if (resp.error != AGENTX_ERR_NONE) + return (-1); + + return (0); +} + +/* + * Read the response PDU for an open operation. + */ +int +snmp_agentx_open_response(struct agentx_handle *h, struct agentx_pdu *pdu) +{ + + if (snmp_agentx_response(h, pdu) == -1) + return (-1); + + h->sessionid = pdu->hdr->sessionid; + return (0); +} + +void +snmp_agentx_pdu_free(struct agentx_pdu *pdu) +{ + free(pdu->buffer); + if (pdu->request) + free(pdu->request); + free(pdu); +} + +/* + * Set the callback function to be called when a complete PDU is received. + */ +void +snmp_agentx_set_callback(struct agentx_handle *h, + void (*cb)(struct agentx_handle *, struct agentx_pdu *, void *), void *arg) +{ + h->cb = cb; + h->cb_arg = arg; +} + +int +snmp_agentx_buffer_consume(struct agentx_pdu *b, u_int len) +{ + int padding; + + padding = ((len + 3) & ~0x03) - len; + + if (b->datalen < (len + padding)) + return (-1); + + b->datalen -= len + padding; + b->ptr += len + padding; + + return (0); +} + +/* + * Send an AgentX PDU. Flushes any already-enqueued PDUs. + */ +int +snmp_agentx_send(struct agentx_handle *h, struct agentx_pdu *pdu) +{ + ssize_t n; + + /* set the appropriate IDs in the protocol header */ + if (pdu != NULL && + (pdu->datalen == pdu->hdr->length + sizeof(struct agentx_hdr))) { + pdu->hdr->sessionid = h->sessionid; + + if (pdu->hdr->type != AGENTX_RESPONSE) { + ++h->transactid; + ++h->packetid; + } + + pdu->hdr->transactid = h->transactid; + pdu->hdr->packetid = h->packetid; + TAILQ_INSERT_TAIL(&h->w, pdu, entry); + } + + again: + if ((pdu = TAILQ_FIRST(&h->w)) == NULL) + return (0); + + if ((n = send(h->fd, pdu->ioptr, pdu->datalen, 0)) == -1) + return (-1); + + pdu->ioptr += n; + pdu->datalen -= n; + + if (pdu->datalen > 0) { + errno = EAGAIN; + return (-1); + } + +#ifdef DEBUG + snmp_agentx_dump_hdr(pdu->hdr); +#endif + + TAILQ_REMOVE(&h->w, pdu, entry); + TAILQ_INSERT_TAIL(&h->inflight, pdu, entry); + + goto again; +} + +/* + * Attempt to read a single AgentX PDU. + */ +struct agentx_pdu * +snmp_agentx_recv(struct agentx_handle *h) +{ + struct agentx_pdu *pdu, *match; + ssize_t n; + + h->error = AGENTX_ERR_NONE; + + if (h->r == NULL) { + if ((h->r = snmp_agentx_pdu_alloc()) == NULL) + return (NULL); + h->r->datalen = 0; /* XXX -- force this for receive buffers */ + } + pdu = h->r; + + if (snmp_agentx_buffercheck(pdu, sizeof(struct agentx_hdr)) == -1) + return (NULL); + + /* read header */ + if (pdu->datalen < sizeof(struct agentx_hdr)) { + n = recv(h->fd, pdu->ioptr, sizeof(struct agentx_hdr), 0); + + if (n == 0 || n == -1) + return (NULL); + + pdu->datalen += n; + pdu->ioptr += n; + + if (pdu->datalen < sizeof(struct agentx_hdr)) { + errno = EAGAIN; + return (NULL); + } + + if (snmp_agentx_buffercheck(pdu, pdu->hdr->length) == -1) + return (NULL); + } + + /* read body */ + n = recv(h->fd, pdu->ioptr, pdu->hdr->length, 0); + + if (n == 0 || n == -1) + return (NULL); + + pdu->datalen += n; + pdu->ioptr += n; + + if (pdu->datalen < pdu->hdr->length + sizeof(struct agentx_hdr)) { + errno = EAGAIN; + return (NULL); + } +#ifdef DEBUG + snmp_agentx_dump_hdr(pdu->hdr); +#endif + /* If this is an open on a new connection, fix it up */ + if (pdu->hdr->type == AGENTX_OPEN && h->sessionid == 0) { + pdu->hdr->sessionid = 0; /* ignored, per RFC */ + h->transactid = pdu->hdr->transactid; + h->packetid = pdu->hdr->packetid; + } + + if (pdu->hdr->version != AGENTX_VERSION) { + h->error = AGENTX_ERR_PARSE_ERROR; + goto fail; + } + + if (pdu->hdr->type == AGENTX_RESPONSE) { + + match = agentx_find_inflight(h, pdu->hdr->transactid, + pdu->hdr->packetid); + if (match == NULL) { + errno = ESRCH; /* XXX */ + goto fail; + } + + TAILQ_REMOVE(&h->inflight, match, entry); + pdu->request = match; + h->r = NULL; + + if (h->cb) + h->cb(h, pdu, h->cb_arg); + + } else { + if (pdu->hdr->sessionid != h->sessionid) { + h->error = AGENTX_ERR_NOT_OPEN; + goto fail; + } + + if (pdu->hdr->flags & AGENTX_NON_DEFAULT_CONTEXT) { + h->error = AGENTX_ERR_UNSUPPORTED_CONTEXT; + goto fail; + } + + snmp_agentx_update_ids(h, pdu); /* XXX */ + + if (pdu->datalen != pdu->hdr->length + sizeof(*pdu->hdr)) { + h->error = AGENTX_ERR_PARSE_ERROR; + goto fail; + } + } + + h->r = NULL; + return (pdu); + fail: + snmp_agentx_pdu_free(pdu); + h->r = NULL; + return (NULL); +} + +/* + * Synchonous request and receipt of response. + */ +struct agentx_pdu * +snmp_agentx_request(struct agentx_handle *h, struct agentx_pdu *pdu) +{ + + if (snmp_agentx_send(h, pdu) == -1) { + if (errno != EAGAIN) + return (NULL); + } + while (snmp_agentx_send(h, NULL) == -1) { + if (errno != EAGAIN) + return (NULL); + } + while ((pdu = snmp_agentx_recv(h)) == NULL) { + if (errno != EAGAIN) + return (NULL); + } + h->error = AGENTX_ERR_NONE; + + return (pdu); +} + +struct agentx_pdu * +agentx_find_inflight(struct agentx_handle *h, uint32_t tid, uint32_t pid) +{ + struct agentx_pdu *pdu; + + TAILQ_FOREACH(pdu, &h->inflight, entry) + if (pdu->hdr->transactid == tid && pdu->hdr->packetid == pid) + break; + return (pdu); +} + +int +snmp_agentx_buffercheck(struct agentx_pdu *pdu, size_t len) +{ + uint8_t *newptr; + int newlen; + + if (pdu->buflen - pdu->datalen >= len) + return (0); + + newlen = pdu->buflen; + while (newlen - pdu->datalen < len) + newlen *= 2; + + if ((newptr = realloc(pdu->buffer, newlen)) == NULL) + return (-1); + + pdu->buflen = newlen; + pdu->ioptr = &newptr[pdu->ioptr - pdu->buffer]; + pdu->buffer = newptr; + pdu->hdr = (struct agentx_hdr *)pdu->buffer; + pdu->ptr = &pdu->buffer[pdu->datalen]; + + return (0); +} + +/* + * Utility routines for initializing common AgentX PDUs. + */ + +struct agentx_pdu * +snmp_agentx_open_pdu(struct agentx_handle *h, char *descr, + struct snmp_oid *oid) +{ + struct agentx_open_timeout to; + struct snmp_oid nulloid; + struct agentx_pdu *pdu; + + if ((pdu = snmp_agentx_pdu_alloc()) == NULL) + return (NULL); + + pdu->hdr->type = AGENTX_OPEN; + + if (oid == NULL) { + bzero(&nulloid, sizeof(nulloid)); + oid = &nulloid; + } + + bzero(&to, sizeof(to)); + to.timeout = AGENTX_DEFAULT_TIMEOUT; + + if (snmp_agentx_raw(pdu, &to, sizeof(to)) == -1 || + snmp_agentx_oid(pdu, oid) == -1 || + snmp_agentx_octetstring(pdu, descr, strlen(descr)) == -1) + goto fail; + + return (pdu); + fail: + snmp_agentx_pdu_free(pdu); + return (NULL); +} + +struct agentx_pdu * +snmp_agentx_close_pdu(struct agentx_handle *h, uint8_t reason) +{ + struct agentx_close_request_data req; + struct agentx_pdu *pdu; + + if ((pdu = snmp_agentx_pdu_alloc()) == NULL) + return (NULL); + pdu->hdr->type = AGENTX_CLOSE; + + bzero(&req, sizeof(req)); + req.reason = reason; + + if (snmp_agentx_raw(pdu, &req, sizeof(req)) == -1) { + snmp_agentx_pdu_free(pdu); + return (NULL); + } + + return (pdu); +} + +struct agentx_pdu * +snmp_agentx_notify_pdu(struct snmp_oid *oid) +{ + struct agentx_pdu *pdu; + + if ((pdu = snmp_agentx_pdu_alloc()) == NULL) + return (NULL); + pdu->hdr->type = AGENTX_NOTIFY; + + if (snmp_agentx_varbind(pdu, &trapoid_0, + AGENTX_OBJECT_IDENTIFIER, oid, sizeof(*oid)) == -1) { + snmp_agentx_pdu_free(pdu); + return (NULL); + } + + return (pdu); +} + +struct agentx_pdu * +snmp_agentx_response_pdu(int uptime, int error, int idx) +{ + struct agentx_response_data resp; + struct agentx_pdu *pdu; + + if ((pdu = snmp_agentx_pdu_alloc()) == NULL) + return (NULL); + pdu->hdr->type = AGENTX_RESPONSE; + + resp.sysuptime = uptime; + resp.error = error; + resp.index = idx; + + if (snmp_agentx_raw(pdu, &resp, sizeof(resp)) == -1) { + snmp_agentx_pdu_free(pdu); + return (NULL); + } + + return (pdu); +} + +struct agentx_pdu * +snmp_agentx_ping_pdu(void) +{ + struct agentx_pdu *pdu; + + if ((pdu = snmp_agentx_pdu_alloc()) == NULL) + return (NULL); + pdu->hdr->version = AGENTX_VERSION; + pdu->hdr->type = AGENTX_PING; + + return (pdu); +} + +struct agentx_pdu * +snmp_agentx_register_pdu(struct snmp_oid *oid, int timeout, int range_index, + int range_bound) +{ + struct agentx_register_hdr rhdr; + struct agentx_pdu *pdu; + + if ((pdu = snmp_agentx_pdu_alloc()) == NULL) + return (NULL); + + pdu->hdr->version = AGENTX_VERSION; + pdu->hdr->type = AGENTX_REGISTER; + + rhdr.timeout = timeout; + rhdr.priority = AGENTX_REGISTER_PRIO_DEFAULT; + rhdr.subrange = range_index; + rhdr.reserved = 0; + + if (snmp_agentx_raw(pdu, &rhdr, sizeof(rhdr)) == -1 || + snmp_agentx_oid(pdu, oid) == -1 || + (range_index && snmp_agentx_int(pdu, &range_bound) == -1)) { + snmp_agentx_pdu_free(pdu); + return (NULL); + } + + return (pdu); +} + +/* + * AgentX PDU write routines. + */ + +int +snmp_agentx_raw(struct agentx_pdu *pdu, void *data, int len) +{ + + if (snmp_agentx_buffercheck(pdu, len) == -1) + return (-1); + + memcpy(pdu->ptr, data, len); + + pdu->hdr->length += len; + pdu->ptr += len; + pdu->datalen += len; + + return (0); +} + +int +snmp_agentx_int(struct agentx_pdu *pdu, uint32_t *i) +{ + return (snmp_agentx_raw(pdu, i, sizeof(*i))); +} + +int +snmp_agentx_int64(struct agentx_pdu *pdu, uint64_t *i) +{ + return (snmp_agentx_raw(pdu, i, sizeof(*i))); +} + +int +snmp_agentx_octetstring(struct agentx_pdu *pdu, char *str, int len) +{ + static uint8_t pad[4] = { 0, 0, 0, 0 }; + int padding; + uint32_t l; + + padding = ((len + 3) & ~0x03) - len; + + l = len; + if (snmp_agentx_int(pdu, &len) == -1 || + snmp_agentx_raw(pdu, str, len) == -1 || + snmp_agentx_raw(pdu, pad, padding) == -1) + return (-1); + + return (0); +} + +int +snmp_agentx_oid(struct agentx_pdu *pdu, struct snmp_oid *oid) +{ + struct agentx_oid_hdr ohdr; + u_int i, prefix; + + i = prefix = 0; + + if (oid->o_id[0] == 1 && oid->o_id[1] == 3 && + oid->o_id[2] == 6 && oid->o_id[3] == 1 && + oid->o_id[4] < 256) { + prefix = oid->o_id[4]; + i = 5; + } + + if (prefix) + ohdr.n_subid = oid->o_n - 5; + else + ohdr.n_subid = oid->o_n; + ohdr.prefix = prefix; + ohdr.include = 0; + ohdr.reserved = 0; + + if (snmp_agentx_raw(pdu, &ohdr, sizeof(ohdr)) == -1) + return (-1); + + for (; i < oid->o_n; i++) + if (snmp_agentx_int(pdu, &oid->o_id[i]) == -1) + return (-1); + + return (0); +} + +int +snmp_agentx_varbind(struct agentx_pdu *pdu, struct snmp_oid *oid, int type, + void *data, int len) +{ + struct agentx_varbind_hdr vbhdr; + + vbhdr.type = type; + vbhdr.reserved = 0; + if (snmp_agentx_raw(pdu, &vbhdr, sizeof(vbhdr)) == -1) + return (-1); + + if (snmp_agentx_oid(pdu, oid) == -1) + return (-1); + + switch (type) { + + case AGENTX_NO_SUCH_OBJECT: + case AGENTX_NO_SUCH_INSTANCE: + case AGENTX_END_OF_MIB_VIEW: + case AGENTX_NULL: + /* no data follows the OID */ + return (0); + + case AGENTX_IP_ADDRESS: + case AGENTX_OPAQUE: + case AGENTX_OCTET_STRING: + return (snmp_agentx_octetstring(pdu, data, len)); + + case AGENTX_OBJECT_IDENTIFIER: + return (snmp_agentx_oid(pdu, (struct snmp_oid *)data)); + + case AGENTX_INTEGER: + case AGENTX_COUNTER32: + case AGENTX_GAUGE32: + case AGENTX_TIME_TICKS: + return (snmp_agentx_int(pdu, (uint32_t *)data)); + + case AGENTX_COUNTER64: + return (snmp_agentx_int64(pdu, (uint64_t *)data)); + + default: + return (-1); + } + /* NOTREACHED */ +} + +/* + * AgentX PDU read routines. + */ + +int +snmp_agentx_read_vbhdr(struct agentx_pdu *pdu, + struct agentx_varbind_hdr *vbhdr) +{ + if (snmp_agentx_read_raw(pdu, vbhdr, sizeof(*vbhdr)) == -1) + return (-1); + if (!snmp_agentx_byteorder_native(pdu->hdr)) + vbhdr->type = snmp_agentx_int16_byteswap(vbhdr->type); + return (0); +} + +int +snmp_agentx_copy_raw(struct agentx_pdu *pdu, void *v, int len) +{ + return (snmp_agentx_do_read_raw(pdu, v, len, 0)); +} + +int +snmp_agentx_read_raw(struct agentx_pdu *pdu, void *v, int len) +{ + return (snmp_agentx_do_read_raw(pdu, v, len, 1)); +} + +int +snmp_agentx_do_read_raw(struct agentx_pdu *pdu, void *v, int len, int consume) +{ + void *ptr = pdu->ptr; + + if (consume) + if (snmp_agentx_buffer_consume(pdu, len) == -1) + return (-1); + + memcpy(v, ptr, len); + + return (0); +} + +int +snmp_agentx_read_int(struct agentx_pdu *pdu, uint32_t *i) +{ + if (snmp_agentx_read_raw(pdu, i, sizeof(*i)) == -1) + return (-1); + if (!snmp_agentx_byteorder_native(pdu->hdr)) + *i = snmp_agentx_int_byteswap(*i); + return (0); +} + +int +snmp_agentx_read_int64(struct agentx_pdu *pdu, uint64_t *i) +{ + if (snmp_agentx_read_raw(pdu, i, sizeof(*i)) == -1) + return (-1); + if (!snmp_agentx_byteorder_native(pdu->hdr)) + *i = snmp_agentx_int64_byteswap(*i); + return (0); +} + +int +snmp_agentx_read_oid(struct agentx_pdu *pdu, struct snmp_oid *oid) +{ + struct agentx_oid_hdr ohdr; + int i = 0; + + if (snmp_agentx_read_raw(pdu, &ohdr, sizeof(ohdr)) == -1) + return (-1); + + bzero(oid, sizeof(*oid)); + + if (ohdr.prefix != 0) { + oid->o_id[0] = 1; + oid->o_id[1] = 3; + oid->o_id[2] = 6; + oid->o_id[3] = 1; + oid->o_id[4] = ohdr.prefix; + i = 5; + } + + while (ohdr.n_subid--) + if (snmp_agentx_read_int(pdu, &oid->o_id[i++]) == -1) + return (-1); + + oid->o_n = i; + + return (0); +} + +char * +snmp_agentx_read_octetstr(struct agentx_pdu *pdu, int *len) +{ + char *str; + uint32_t l; + + if (snmp_agentx_read_int(pdu, &l) == -1) + return (NULL); + + if ((str = malloc(l)) == NULL) + return (NULL); + + if (snmp_agentx_read_raw(pdu, str, l) == -1) { + free(str); + return (NULL); + } + *len = l; + + return (str); +} + +/* + * Synchronous AgentX calls. + */ + +int +snmp_agentx_ping(struct agentx_handle *h) +{ + struct agentx_pdu *pdu; + int error = 0; + + if ((pdu = snmp_agentx_ping_pdu()) == NULL || + (pdu = snmp_agentx_request(h, pdu)) == NULL) + return (-1); + + if (snmp_agentx_response(h, pdu) == -1) + error = -1; + snmp_agentx_pdu_free(pdu); + + return (error); +} + +/* + * Internal utility functions. + */ + +void +snmp_agentx_update_ids(struct agentx_handle *h, struct agentx_pdu *pdu) +{ + /* XXX -- update to reflect the new queueing semantics */ + h->transactid = pdu->hdr->transactid; + h->packetid = pdu->hdr->packetid; +} + +char * +snmp_agentx_type2name(int type) +{ + static char *names[] = { + "AGENTX_OPEN", + "AGENTX_CLOSE", + "AGENTX_REGISTER", + "AGENTX_UNREGISTER", + "AGENTX_GET", + "AGENTX_GET_NEXT", + "AGENTX_GET_BULK", + "AGENTX_TEST_SET", + "AGENTX_COMMIT_SET", + "AGENTX_UNDO_SET", + "AGENTX_CLEANUP_SET", + "AGENTX_NOTIFY", + "AGENTX_PING", + "AGENTX_INDEX_ALLOCATE", + "AGENTX_INDEX_DEALLOCATE", + "AGENTX_ADD_AGENT_CAPS", + "AGENTX_REMOVE_AGENT_CAPS", + "AGENTX_RESPONSE" + }; + + if (type > 18) + return ("unknown"); + + return (names[type - 1]); +} + +#ifdef DEBUG +static void +snmp_agentx_dump_hdr(struct agentx_hdr *hdr) +{ + if (hdr == NULL) { + printf("NULL\n"); + return; + } + + printf("Version: %i\n", hdr->version); + printf("Type: %s\n", snmp_agentx_type2name(hdr->type)); + printf("Flags: %i\n", hdr->flags); + printf("Reserved: %i\n", hdr->reserved); + printf("Session ID: %i\n", hdr->sessionid); + printf("Transaction ID: %i\n", hdr->transactid); + printf("Packet ID: %i\n", hdr->packetid); + printf("Data Length: %i\n", hdr->length); + + if (hdr->type == AGENTX_RESPONSE) { + struct agentx_response *r = (struct agentx_response *)hdr; + + printf("SysUptime: %i\n", r->data.sysuptime); + printf("Error: %i\n", r->data.error); + printf("Index: %i\n", r->data.index); + } +} +#endif diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index a5e759ad015..9ad8418213a 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.175 2014/01/22 00:21:16 henning Exp $ */ +/* $OpenBSD: parse.y,v 1.176 2014/04/14 12:58:04 blambert Exp $ */ /* * Copyright (c) 2007-2011 Reyk Floeter <reyk@openbsd.org> @@ -56,6 +56,7 @@ #include <openssl/ssl.h> #include "relayd.h" +#include "snmp.h" TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); static struct file { @@ -156,14 +157,14 @@ typedef struct { %token LOADBALANCE LOG LOOKUP MARK MARKED MODE NAT NO DESTINATION %token NODELAY NOTHING ON PARENT PATH PORT PREFORK PRIORITY PROTO %token QUERYSTR REAL REDIRECT RELAY REMOVE REQUEST RESPONSE RETRY -%token RETURN ROUNDROBIN ROUTE SACK SCRIPT SEND SESSION SOCKET SPLICE +%token RETURN ROUNDROBIN ROUTE SACK SCRIPT SEND SESSION SNMP SOCKET SPLICE %token SSL STICKYADDR STYLE TABLE TAG TCP TIMEOUT TO ROUTER RTLABEL %token TRANSPARENT TRAP UPDATES URL VIRTUAL WITH TTL RTABLE MATCH %token RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDH CURVE %token <v.string> STRING %token <v.number> NUMBER -%type <v.string> hostname interface table -%type <v.number> http_type loglevel mark +%type <v.string> hostname interface table optstring +%type <v.number> http_type loglevel mark restricted trap %type <v.number> direction dstmode flag forwardmode retry %type <v.number> optssl optsslclient sslcache %type <v.number> redirect_proto relay_proto match @@ -369,13 +370,24 @@ main : INTERVAL NUMBER { } conf->sc_prefork_relay = $2; } - | SEND TRAP { + | SNMP trap optstring { if (loadcfg) break; - conf->sc_flags |= F_TRAP; + conf->sc_flags |= F_SNMP; + if ($2) + conf->sc_snmp_flags |= FSNMP_TRAPONLY; + if ($3) + conf->sc_snmp_path = $3; + else + conf->sc_snmp_path = strdup(AGENTX_SOCKET); + if (conf->sc_snmp_path == NULL) + fatal("out of memory"); } ; +trap : /* nothing */ { $$ = 0; } + | TRAP { $$ = 1; } + loglevel : UPDATES { $$ = RELAYD_OPT_LOGUPDATE; } | ALL { $$ = RELAYD_OPT_LOGALL; } ; @@ -1797,6 +1809,9 @@ optnl : '\n' optnl nl : '\n' optnl ; +optstring : STRING { $$ = $1; } + | /* nothing */ { $$ = NULL; } + ; %% struct keywords { @@ -1909,6 +1924,7 @@ lookup(char *s) { "script", SCRIPT }, { "send", SEND }, { "session", SESSION }, + { "snmp", SNMP }, { "socket", SOCKET }, { "source-hash", SRCHASH }, { "splice", SPLICE }, diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index eafdccfe18f..3cba6718ea0 100644 --- a/usr.sbin/relayd/relayd.conf.5 +++ b/usr.sbin/relayd/relayd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: relayd.conf.5,v 1.139 2013/09/09 17:57:44 reyk Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.140 2014/04/14 12:58:04 blambert Exp $ .\" .\" Copyright (c) 2006, 2007 Reyk Floeter <reyk@openbsd.org> .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 9 2013 $ +.Dd $Mdocdate: April 14 2014 $ .Dt RELAYD.CONF 5 .Os .Sh NAME @@ -147,15 +147,22 @@ to a relay. .Xr relayd 8 runs 5 relay processes by default and every process will handle all configured relays. -.It Ic send trap +.It Xo Ic snmp Oo Ic trap Oc Ic Oo Qo Ar path Qc Oc Send an SNMP trap when the state of a host changes. .Xr relayd 8 will try to connect to .Xr snmpd 8 -and request it send a trap to the registered trap receivers; -see +over the AgentX SNMP socket specified by +.Ar path +and request it send a trap to the registered trap receivers. +If +.Ar path +is not specified, a default path of +.Ar /var/run/agentx.sock +will be used. +See .Xr snmpd.conf 5 -for more information about the configuration. +for more information about SNMP configuration. .It Ic timeout Ar number Set the global timeout in milliseconds for checks. This can be overridden by the timeout value in the table definitions. diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index 675930b325a..e95cf3af22d 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.172 2014/02/14 10:21:00 benno Exp $ */ +/* $OpenBSD: relayd.h,v 1.173 2014/04/14 12:58:04 blambert Exp $ */ /* * Copyright (c) 2006 - 2012 Reyk Floeter <reyk@openbsd.org> @@ -277,7 +277,7 @@ TAILQ_HEAD(addresslist, address); #define F_DEMOTED 0x00008000 #define F_UDP 0x00010000 #define F_RETURN 0x00020000 -#define F_TRAP 0x00040000 +#define F_SNMP 0x00040000 #define F_NEEDPF 0x00080000 #define F_PORT 0x00100000 #define F_SSLCLIENT 0x00200000 @@ -913,6 +913,8 @@ struct relayd { struct timeval sc_statinterval; int sc_snmp; + const char *sc_snmp_path; + int sc_snmp_flags; struct event sc_snmpto; struct event sc_snmpev; @@ -927,6 +929,8 @@ struct relayd { int sc_reload; }; +#define FSNMP_TRAPONLY 0x01 + #define RELAYD_OPT_VERBOSE 0x01 #define RELAYD_OPT_NOACTION 0x04 #define RELAYD_OPT_LOGUPDATE 0x08 @@ -1129,7 +1133,7 @@ void pn_ref(u_int16_t); /* snmp.c */ void snmp_init(struct relayd *, enum privsep_procid); -int snmp_setsock(struct relayd *, enum privsep_procid); +void snmp_setsock(struct relayd *, enum privsep_procid); int snmp_getsock(struct relayd *, struct imsg *); void snmp_hosttrap(struct relayd *, struct table *, struct host *); diff --git a/usr.sbin/relayd/snmp.c b/usr.sbin/relayd/snmp.c index c426e14b51e..e8099b5b648 100644 --- a/usr.sbin/relayd/snmp.c +++ b/usr.sbin/relayd/snmp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: snmp.c,v 1.13 2013/01/17 20:34:18 bluhm Exp $ */ +/* $OpenBSD: snmp.c,v 1.14 2014/04/14 12:58:04 blambert Exp $ */ /* * Copyright (c) 2008 Reyk Floeter <reyk@openbsd.org> @@ -45,11 +45,21 @@ goto done; \ } while (0) -static struct imsgev *iev_snmp = NULL; -enum privsep_procid snmp_procid; +static struct snmp_oid hosttrapoid = { + { 1, 3, 6, 1, 4, 1, 30155, 3, 1, 0 }, + 10 +}; + +static struct agentx_handle *snmp_agentx = NULL; +enum privsep_procid snmp_procid; void snmp_sock(int, short, void *); -int snmp_element(const char *, enum snmp_type, void *, int64_t); +int snmp_element(const char *, enum snmp_type, void *, int64_t, + struct agentx_pdu *); +int snmp_string2oid(const char *, struct snmp_oid *); +void snmp_event_add(struct relayd *, int); +void snmp_agentx_process(struct agentx_handle *, struct agentx_pdu *, + void *); void snmp_init(struct relayd *env, enum privsep_procid id) @@ -59,21 +69,26 @@ snmp_init(struct relayd *env, enum privsep_procid id) if (event_initialized(&env->sc_snmpto)) event_del(&env->sc_snmpto); if (env->sc_snmp != -1) { + if (snmp_agentx) { + snmp_agentx_close(snmp_agentx, AGENTX_CLOSE_OTHER); + snmp_agentx = NULL; + } close(env->sc_snmp); env->sc_snmp = -1; } - if ((env->sc_flags & F_TRAP) == 0) + if ((env->sc_flags & F_SNMP) == 0) return; snmp_procid = id; - snmp_sock(-1, -1, env); + + proc_compose_imsg(env->sc_ps, snmp_procid, -1, + IMSG_SNMPSOCK, -1, NULL, 0); } -int +void snmp_setsock(struct relayd *env, enum privsep_procid id) { - struct imsgev tmpiev; struct sockaddr_un sun; int s = -1; @@ -82,28 +97,23 @@ snmp_setsock(struct relayd *env, enum privsep_procid id) bzero(&sun, sizeof(sun)); sun.sun_family = AF_UNIX; - strlcpy(sun.sun_path, SNMP_SOCKET, sizeof(sun.sun_path)); + strlcpy(sun.sun_path, env->sc_snmp_path, sizeof(sun.sun_path)); + + socket_set_blockmode(s, BM_NONBLOCK); if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) { close(s); s = -1; - goto done; } - - /* enable restricted snmp socket mode */ - bzero(&tmpiev, sizeof(tmpiev)); - imsg_init(&tmpiev.ibuf, s); - imsg_compose_event(&tmpiev, IMSG_SNMP_LOCK, 0, 0, -1, NULL, 0); - done: proc_compose_imsg(env->sc_ps, id, -1, IMSG_SNMPSOCK, s, NULL, 0); - return (-1); } int snmp_getsock(struct relayd *env, struct imsg *imsg) { - struct timeval tv = SNMP_RECONNECT_TIMEOUT; + struct timeval tv = SNMP_RECONNECT_TIMEOUT; + struct agentx_pdu *pdu; if (imsg->fd == -1) goto retry; @@ -111,14 +121,16 @@ snmp_getsock(struct relayd *env, struct imsg *imsg) env->sc_snmp = imsg->fd; log_debug("%s: got new snmp socket %d", __func__, imsg->fd); - if (iev_snmp == NULL && - (iev_snmp = calloc(1, sizeof(*iev_snmp))) == NULL) - fatal("snmp_getsock: calloc"); - imsg_init(&iev_snmp->ibuf, env->sc_snmp); - event_set(&env->sc_snmpev, env->sc_snmp, - EV_READ|EV_TIMEOUT, snmp_sock, env); - event_add(&env->sc_snmpev, NULL); + if ((snmp_agentx = snmp_agentx_alloc(env->sc_snmp)) == NULL) + fatal("snmp_getsock: agentx alloc"); + if ((pdu = snmp_agentx_open_pdu(snmp_agentx, "relayd", NULL)) == NULL) + fatal("snmp_getsock: agentx pdu"); + (void)snmp_agentx_send(snmp_agentx, pdu); + + snmp_agentx_set_callback(snmp_agentx, snmp_agentx_process, env); + snmp_event_add(env, EV_WRITE); + return (0); retry: evtimer_set(&env->sc_snmpto, snmp_sock, env); @@ -127,88 +139,181 @@ snmp_getsock(struct relayd *env, struct imsg *imsg) } void +snmp_event_add(struct relayd *env, int wflag) +{ + event_del(&env->sc_snmpev); + event_set(&env->sc_snmpev, env->sc_snmp, EV_READ|wflag, snmp_sock, env); + event_add(&env->sc_snmpev, NULL); +} + +void snmp_sock(int fd, short event, void *arg) { struct relayd *env = arg; - struct timeval tv = SNMP_RECONNECT_TIMEOUT; + int evflags = 0; - switch (event) { - case -1: - bzero(&tv, sizeof(tv)); - goto retry; - case EV_READ: - log_debug("%s: snmp socket closed %d", __func__, env->sc_snmp); - (void)close(env->sc_snmp); - break; + if (event & EV_TIMEOUT) { + goto reopen; + } + if (event & EV_WRITE) { + if (snmp_agentx_send(snmp_agentx, NULL) == -1) { + if (errno != EAGAIN) + goto close; + + /* short write */ + evflags |= EV_WRITE; + } + } + if (event & EV_READ) { + if (snmp_agentx_recv(snmp_agentx) == NULL) { + if (snmp_agentx->error) { + log_warnx("agentx protocol error '%i'", + snmp_agentx->error); + goto close; + } + if (errno != EAGAIN) { + log_warn("agentx socket error"); + goto close; + } + + /* short read */ + } + + /* PDU handled in the registered callback */ } + snmp_event_add(env, evflags); + return; + + close: + log_debug("%s: snmp socket closed %d", __func__, env->sc_snmp); + snmp_agentx_free(snmp_agentx); + env->sc_snmp = -1; + snmp_agentx = NULL; + reopen: proc_compose_imsg(env->sc_ps, snmp_procid, -1, IMSG_SNMPSOCK, -1, NULL, 0); return; - retry: - evtimer_set(&env->sc_snmpto, snmp_sock, arg); - evtimer_add(&env->sc_snmpto, &tv); } +void +snmp_agentx_process(struct agentx_handle *h, struct agentx_pdu *pdu, void *arg) +{ + struct agentx_close_request_data close_hdr; + struct relayd *env = arg; + + switch (pdu->request->hdr->type) { + + case AGENTX_NOTIFY: + if (snmp_agentx_response(h, pdu) == -1) + break; + break; + + case AGENTX_OPEN: + if (snmp_agentx_open_response(h, pdu) == -1) + break; + break; + + case AGENTX_CLOSE: + snmp_agentx_read_raw(pdu, &close_hdr, sizeof(close_hdr)); + log_info("snmp: agentx master has closed connection (%i)", + close_hdr.reason); + + snmp_agentx_free(snmp_agentx); + env->sc_snmp = -1; + snmp_agentx = NULL; + proc_compose_imsg(env->sc_ps, snmp_procid, -1, + IMSG_SNMPSOCK, -1, NULL, 0); + break; + + default: + if (snmp_agentx_response(h, pdu) == -1) + break; + break; + } + + snmp_agentx_pdu_free(pdu); + return; +} + + int -snmp_element(const char *oid, enum snmp_type type, void *buf, int64_t val) +snmp_element(const char *oidstr, enum snmp_type type, void *buf, int64_t val, + struct agentx_pdu *pdu) { - struct iovec iov[2]; - int iovcnt = 2; u_int32_t d; u_int64_t l; - struct snmp_imsg sm; + struct snmp_oid oid; DPRINTF("%s: oid %s type %d buf %p val %lld", __func__, oid, type, buf, val); - bzero(&iov, sizeof(iov)); + if (snmp_string2oid(oidstr, &oid) == -1) + return (-1); switch (type) { - case SNMP_COUNTER32: case SNMP_GAUGE32: - case SNMP_TIMETICKS: - case SNMP_OPAQUE: - case SNMP_UINTEGER32: + case SNMP_NSAPADDR: case SNMP_INTEGER32: + case SNMP_UINTEGER32: + d = (u_int32_t)val; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_INTEGER, + &d, sizeof(d)) == -1) + return (-1); + break; + + case SNMP_COUNTER32: d = (u_int32_t)val; - iov[1].iov_base = &d; - iov[1].iov_len = sizeof(d); + if (snmp_agentx_varbind(pdu, &oid, AGENTX_COUNTER32, + &d, sizeof(d)) == -1) + return (-1); break; + + case SNMP_TIMETICKS: + d = (u_int32_t)val; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_TIME_TICKS, + &d, sizeof(d)) == -1) + return (-1); + break; + case SNMP_COUNTER64: l = (u_int64_t)val; - iov[1].iov_base = &l; - iov[1].iov_len = sizeof(l); + if (snmp_agentx_varbind(pdu, &oid, AGENTX_COUNTER64, + &l, sizeof(l)) == -1) + return (-1); break; - case SNMP_NSAPADDR: - case SNMP_BITSTRING: - case SNMP_OCTETSTRING: + case SNMP_IPADDR: - case SNMP_OBJECT: - iov[1].iov_base = buf; - if (val == 0) - iov[1].iov_len = strlen((char *)buf); - else - iov[1].iov_len = val; - break; - case SNMP_NULL: - iovcnt--; + case SNMP_OPAQUE: + d = (u_int32_t)val; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OPAQUE, + buf, strlen(buf)) == -1) + return (-1); break; + + case SNMP_OBJECT: { + struct snmp_oid oid1; + + if (snmp_string2oid(buf, &oid1) == -1) + return (-1); + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OBJECT_IDENTIFIER, + &oid1, sizeof(oid1)) == -1) + return (-1); } - bzero(&sm, sizeof(sm)); - if (strlcpy(sm.snmp_oid, oid, sizeof(sm.snmp_oid)) >= - sizeof(sm.snmp_oid)) - return (-1); - sm.snmp_type = type; - sm.snmp_len = iov[1].iov_len; - iov[0].iov_base = &sm; - iov[0].iov_len = sizeof(sm); + case SNMP_BITSTRING: + case SNMP_OCTETSTRING: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OCTET_STRING, + buf, strlen(buf)) == -1) + return (-1); + break; - if (imsg_composev(&iev_snmp->ibuf, IMSG_SNMP_ELEMENT, 0, 0, -1, - iov, iovcnt) == -1) - return (-1); - imsg_event_add(iev_snmp); + case SNMP_NULL: + /* no data beyond the OID itself */ + if (snmp_agentx_varbind(pdu, &oid, AGENTX_NULL, + NULL, 0) == -1) + return (-1); + } return (0); } @@ -220,7 +325,9 @@ snmp_element(const char *oid, enum snmp_type type, void *buf, int64_t val) void snmp_hosttrap(struct relayd *env, struct table *table, struct host *host) { - if (iev_snmp == NULL || env->sc_snmp == -1) + struct agentx_pdu *pdu; + + if (snmp_agentx == NULL || env->sc_snmp == -1) return; /* @@ -228,21 +335,44 @@ snmp_hosttrap(struct relayd *env, struct table *table, struct host *host) * XXX The trap format needs some tweaks and other OIDs */ - imsg_compose_event(iev_snmp, IMSG_SNMP_TRAP, 0, 0, -1, NULL, 0); + if ((pdu = snmp_agentx_notify_pdu(&hosttrapoid)) == NULL) + return; - SNMP_ELEMENT(".1.0", SNMP_NULL, NULL, 0); - SNMP_ELEMENT(".1.1.0", SNMP_OCTETSTRING, host->conf.name, 0); - SNMP_ELEMENT(".1.2.0", SNMP_INTEGER32, NULL, host->up); - SNMP_ELEMENT(".1.3.0", SNMP_INTEGER32, NULL, host->last_up); - SNMP_ELEMENT(".1.4.0", SNMP_INTEGER32, NULL, host->up_cnt); - SNMP_ELEMENT(".1.5.0", SNMP_INTEGER32, NULL, host->check_cnt); - SNMP_ELEMENT(".1.6.0", SNMP_OCTETSTRING, table->conf.name, 0); - SNMP_ELEMENT(".1.7.0", SNMP_INTEGER32, NULL, table->up); + SNMP_ELEMENT(".1.0", SNMP_NULL, NULL, 0, pdu); + SNMP_ELEMENT(".1.1.0", SNMP_OCTETSTRING, host->conf.name, 0, pdu); + SNMP_ELEMENT(".1.2.0", SNMP_INTEGER32, NULL, host->up, pdu); + SNMP_ELEMENT(".1.3.0", SNMP_INTEGER32, NULL, host->last_up, pdu); + SNMP_ELEMENT(".1.4.0", SNMP_INTEGER32, NULL, host->up_cnt, pdu); + SNMP_ELEMENT(".1.5.0", SNMP_INTEGER32, NULL, host->check_cnt, pdu); + SNMP_ELEMENT(".1.6.0", SNMP_OCTETSTRING, table->conf.name, 0, pdu); + SNMP_ELEMENT(".1.7.0", SNMP_INTEGER32, NULL, table->up, pdu); if (!host->conf.retry) goto done; - SNMP_ELEMENT(".1.8.0", SNMP_INTEGER32, NULL, host->conf.retry); - SNMP_ELEMENT(".1.9.0", SNMP_INTEGER32, NULL, host->retry_cnt); + SNMP_ELEMENT(".1.8.0", SNMP_INTEGER32, NULL, host->conf.retry, pdu); + SNMP_ELEMENT(".1.9.0", SNMP_INTEGER32, NULL, host->retry_cnt, pdu); done: - imsg_compose_event(iev_snmp, IMSG_SNMP_END, 0, 0, -1, NULL, 0); + snmp_agentx_send(snmp_agentx, pdu); + snmp_event_add(env, EV_WRITE); +} + +int +snmp_string2oid(const char *oidstr, struct snmp_oid *o) +{ + char *sp, *p, str[BUFSIZ]; + const char *errstr; + + if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) + return (-1); + bzero(o, sizeof(*o)); + + for (p = sp = str; p != NULL; sp = p) { + if ((p = strpbrk(p, ".-")) != NULL) + *p++ = '\0'; + o->o_id[o->o_n++] = strtonum(sp, 0, UINT_MAX, &errstr); + if (errstr || o->o_n > SNMP_MAX_OID_LEN) + return (-1); + } + + return (0); } |