Since it was brought up on misc, there's been some off-list
interest in work I did during c2k15 that's timed out with
off-list testers.
The following diff implements a subset of RFC 4273, with
no export of individual route entries, which would be sheer
madness over snmp, and for which better tools exist.
For those asking about it: please test and let me know
success/failure so that we can all move forward on this.
- bert
diff --git usr.sbin/bgpd/Makefile usr.sbin/bgpd/Makefile
index c8b92dc..2098195 100644
--- usr.sbin/bgpd/Makefile
+++ usr.sbin/bgpd/Makefile
@@ -4,12 +4,14 @@ PROG= bgpd
SRCS= bgpd.c session.c log.c parse.y config.c \
rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c \
control.c pfkey.c rde_update.c rde_attr.c printconf.c \
- rde_filter.c pftable.c name2id.c util.c carp.c timer.c
+ rde_filter.c pftable.c name2id.c util.c carp.c timer.c snmp.c \
+ agentx.c
CFLAGS+= -Wall -I${.CURDIR}
CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
CFLAGS+= -Wmissing-declarations
CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual
CFLAGS+= -Wsign-compare
+CFLAGS+= -I${.CURDIR}/../snmpd
YFLAGS=
LDADD+= -lutil
DPADD+= ${LIBUTIL}
diff --git usr.sbin/bgpd/agentx.c usr.sbin/bgpd/agentx.c
new file mode 100644
index 0000000..90f3c7f
--- /dev/null
+++ usr.sbin/bgpd/agentx.c
@@ -0,0 +1,1135 @@
+/* $OpenBSD: agentx.c,v 1.8 2014/11/19 10:19:00 blambert Exp $ */
+/*
+ * Copyright (c) 2013,2014 Bret Stephen Lambert <[email protected]>
+ *
+ * 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"
+
+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);
+int snmp_agentx_do_read_oid(struct agentx_pdu *, struct snmp_oid *, int *);
+
+#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;
+ if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
+ sizeof(sun.sun_path))
+ goto fail;
+
+ 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_response(pdu, &resp) == -1)
+ return (-1);
+
+ h->error = resp.error;
+ if (resp.error != AGENTX_ERR_NONE)
+ return (-1);
+
+ return (0);
+}
+
+int
+snmp_agentx_read_response(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);
+ }
+
+ 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);
+}
+
+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 (pdu->hdr->version != AGENTX_VERSION) {
+ h->error = AGENTX_ERR_PARSE_ERROR;
+ 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 (pdu->hdr->version != AGENTX_VERSION) {
+ h->error = AGENTX_ERR_PARSE_ERROR;
+ goto fail;
+ }
+
+ /* 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->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;
+
+ } else {
+ if (pdu->hdr->sessionid != h->sessionid) {
+ h->error = AGENTX_ERR_NOT_OPEN;
+ 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;
+ }
+
+ if (pdu->hdr->flags & AGENTX_NON_DEFAULT_CONTEXT) {
+ pdu->context = snmp_agentx_read_octetstr(pdu,
+ &pdu->contextlen);
+ if (pdu->context == NULL) {
+ 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;
+ size_t newlen;
+
+ if (pdu->buflen - pdu->datalen >= len)
+ return (0);
+
+ newlen = pdu->buflen + len;
+ if (newlen < pdu->buflen || newlen < len)
+ return (-1);
+
+ 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);
+}
+
+struct agentx_pdu *
+snmp_agentx_unregister_pdu(struct snmp_oid *oid, int range_index,
+ int range_bound)
+{
+ struct agentx_unregister_hdr uhdr;
+ struct agentx_pdu *pdu;
+
+ if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
+ return (NULL);
+
+ pdu->hdr->version = AGENTX_VERSION;
+ pdu->hdr->type = AGENTX_UNREGISTER;
+
+ uhdr.reserved1 = 0;
+ uhdr.priority = AGENTX_REGISTER_PRIO_DEFAULT;
+ uhdr.subrange = range_index;
+ uhdr.reserved2 = 0;
+
+ if (snmp_agentx_raw(pdu, &uhdr, sizeof(uhdr)) == -1 ||
+ snmp_agentx_oid(pdu, oid) == -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);
+}
+
+struct agentx_pdu *
+snmp_agentx_get_pdu(struct snmp_oid oid[], int noid)
+{
+ struct snmp_oid nulloid;
+ struct agentx_pdu *pdu;
+ int i;
+
+ if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
+ return (NULL);
+
+ pdu->hdr->version = AGENTX_VERSION;
+ pdu->hdr->type = AGENTX_GET;
+
+ bzero(&nulloid, sizeof(nulloid));
+
+ for (i = 0; i < noid; i++) {
+ if (snmp_agentx_oid(pdu, &oid[i]) == -1 ||
+ snmp_agentx_oid(pdu, &nulloid) == -1) {
+ snmp_agentx_pdu_free(pdu);
+ return (NULL);
+ }
+ }
+
+ return (pdu);
+}
+
+struct agentx_pdu *
+snmp_agentx_getnext_pdu(struct snmp_oid oid[], int noid)
+{
+ struct snmp_oid nulloid;
+ struct agentx_pdu *pdu;
+ int i;
+
+ if ((pdu = snmp_agentx_pdu_alloc()) == NULL)
+ return (NULL);
+
+ pdu->hdr->version = AGENTX_VERSION;
+ pdu->hdr->type = AGENTX_GET_NEXT;
+
+ bzero(&nulloid, sizeof(nulloid));
+
+ for (i = 0; i < noid; i++) {
+ if (snmp_agentx_oid(pdu, &oid[i]) == -1 ||
+ snmp_agentx_oid(pdu, &nulloid) == -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, &l) == -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)
+{
+ int dummy;
+
+ return (snmp_agentx_do_read_oid(pdu, oid, &dummy));
+}
+
+int
+snmp_agentx_do_read_oid(struct agentx_pdu *pdu, struct snmp_oid *oid,
+ int *include)
+{
+ 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;
+ *include = ohdr.include;
+
+ return (0);
+}
+
+int
+snmp_agentx_read_searchrange(struct agentx_pdu *pdu, struct
agentx_search_range *sr)
+{
+ if (snmp_agentx_do_read_oid(pdu, &sr->start, &sr->include) == -1 ||
+ snmp_agentx_read_oid(pdu, &sr->end) == -1)
+ return (-1);
+
+ 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)
+ return (-1);
+
+ /* Attaches the pdu to the handle; will be released later */
+ 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);
+}
+
+/*
+ * 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_oid2string(struct snmp_oid *o, char *buf, size_t len)
+{
+ char str[256];
+ size_t i;
+
+ bzero(buf, len);
+
+ for (i = 0; i < o->o_n; i++) {
+ snprintf(str, sizeof(str), "%d", o->o_id[i]);
+ strlcat(buf, str, len);
+ if (i < (o->o_n - 1))
+ strlcat(buf, ".", len);
+ }
+
+ return (buf);
+}
+
+int
+snmp_oid_cmp(struct snmp_oid *a, struct snmp_oid *b)
+{
+ size_t i;
+
+ for (i = 0; i < SNMP_MAX_OID_LEN; i++) {
+ if (a->o_id[i] != 0) {
+ if (a->o_id[i] == b->o_id[i])
+ continue;
+ else if (a->o_id[i] < b->o_id[i]) {
+ /* b is a successor of a */
+ return (1);
+ } else {
+ /* b is a predecessor of a */
+ return (-1);
+ }
+ } else if (b->o_id[i] != 0) {
+ /* b is larger, but a child of a */
+ return (2);
+ } else
+ break;
+ }
+
+ /* b and a are identical */
+ return (0);
+}
+
+void
+snmp_oid_increment(struct snmp_oid *o)
+{
+ u_int i;
+
+ for (i = o->o_n; i > 0; i--) {
+ o->o_id[i - 1]++;
+ if (o->o_id[i - 1] != 0)
+ break;
+ }
+}
+
+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;
+ }
+
+ fprintf(stderr,
+ "agentx: version %d type %s flags %d reserved %d"
+ " sessionid %d transactid %d packetid %d length %d",
+ hdr->version, snmp_agentx_type2name(hdr->type), hdr->flags,
+ hdr->reserved, hdr->sessionid, hdr->transactid,
+ hdr->packetid, hdr->length);
+
+ if (hdr->type == AGENTX_RESPONSE) {
+ struct agentx_response *r = (struct agentx_response *)hdr;
+
+ fprintf(stderr, " sysuptime %d error %d index %d",
+ r->data.sysuptime, r->data.error, r->data.index);
+ }
+
+ fprintf(stderr, "\n");
+}
+#endif
diff --git usr.sbin/bgpd/bgpd.c usr.sbin/bgpd/bgpd.c
index ea5e83f..d9d534c 100644
--- usr.sbin/bgpd/bgpd.c
+++ usr.sbin/bgpd/bgpd.c
@@ -238,6 +238,11 @@ main(int argc, char *argv[])
if (pftable_clear_all() != 0)
quit = 1;
+ if (quit == 0 && conf->flags & BGPD_FLAG_SNMP_EXPORT) {
+ int s = snmp_setsock(conf);
+ imsg_compose(ibuf_se, IMSG_SNMP_OPEN, 0, 0, s, NULL, 0);
+ }
+
while (quit == 0) {
bzero(pfd, sizeof(pfd));
@@ -730,6 +735,18 @@ dispatch_imsg(struct imsgbuf *ibuf, int idx, struct
bgpd_config *conf)
}
reconfpending--;
break;
+ case IMSG_SNMP_OPEN:
+ if (idx != PFD_PIPE_SESSION)
+ log_warnx("SNMP_OPEN request not from SESSION");
+ else {
+ int s;
+
+ s = snmp_setsock(conf);
+ imsg_compose(ibuf_se, IMSG_SNMP_OPEN, 0, 0,
+ s, NULL, 0);
+ imsg_flush(ibuf_se);
+ }
+ break;
default:
break;
}
diff --git usr.sbin/bgpd/bgpd.h usr.sbin/bgpd/bgpd.h
index 297d1f9..44d688b 100644
--- usr.sbin/bgpd/bgpd.h
+++ usr.sbin/bgpd/bgpd.h
@@ -62,6 +62,7 @@
#define BGPD_FLAG_DECISION_ROUTEAGE 0x0100
#define BGPD_FLAG_DECISION_TRANS_AS 0x0200
#define BGPD_FLAG_DECISION_MED_ALWAYS 0x0400
+#define BGPD_FLAG_SNMP_EXPORT 0x2000
#define BGPD_LOG_UPDATES 0x0001
@@ -212,6 +213,7 @@ struct bgpd_config {
struct mrt_head *mrt;
char *csock;
char *rcsock;
+ char *snmpsock;
int flags;
int log;
u_int32_t bgpid;
@@ -419,7 +421,8 @@ enum imsg_type {
IMSG_PFTABLE_COMMIT,
IMSG_REFRESH,
IMSG_IFINFO,
- IMSG_DEMOTE
+ IMSG_DEMOTE,
+ IMSG_SNMP_OPEN
};
struct demote_msg {
diff --git usr.sbin/bgpd/config.c usr.sbin/bgpd/config.c
index a842f54..61af048 100644
--- usr.sbin/bgpd/config.c
+++ usr.sbin/bgpd/config.c
@@ -139,8 +139,6 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config
*conf,
if ((conf->flags & BGPD_FLAG_REFLECTOR) && conf->clusterid == 0)
conf->clusterid = conf->bgpid;
-
-
/* adjust FIB priority if changed */
/* if xconf is uninitalized we get RTP_NONE */
if (xconf->fib_priority != conf->fib_priority) {
@@ -160,6 +158,7 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config
*conf,
xconf->min_holdtime = conf->min_holdtime;
xconf->connectretry = conf->connectretry;
xconf->fib_priority = conf->fib_priority;
+ xconf->snmpsock = conf->snmpsock;
/* clear old control sockets and use new */
free(xconf->csock);
diff --git usr.sbin/bgpd/parse.y usr.sbin/bgpd/parse.y
index 14aff94..8eda9b2 100644
--- usr.sbin/bgpd/parse.y
+++ usr.sbin/bgpd/parse.y
@@ -178,7 +178,7 @@ typedef struct {
%token REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART
%token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY
%token DEMOTE ENFORCE NEIGHBORAS REFLECTOR DEPEND DOWN SOFTRECONFIG
-%token DUMP IN OUT SOCKET RESTRICTED
+%token DUMP IN OUT SOCKET RESTRICTED SNMP
%token LOG ROUTECOLL TRANSPARENT
%token TCP MD5SIG PASSWORD KEY TTLSECURITY
%token ALLOW DENY MATCH
@@ -589,6 +589,14 @@ conf_main : AS as4number {
conf->csock = $2;
}
}
+ | SNMP STRING {
+ if (conf->snmpsock) {
+ yyerror("too many snmp sockets");
+ YYERROR;
+ }
+ conf->flags |= BGPD_FLAG_SNMP_EXPORT;
+ conf->snmpsock = $2;
+ }
;
mrtdump : DUMP STRING inout STRING optnumber {
@@ -2232,6 +2240,7 @@ lookup(char *s)
{ "rtlabel", RTLABEL},
{ "self", SELF},
{ "set", SET},
+ { "snmp", SNMP},
{ "socket", SOCKET },
{ "softreconfig", SOFTRECONFIG},
{ "source-as", SOURCEAS},
diff --git usr.sbin/bgpd/rde.c usr.sbin/bgpd/rde.c
index 40dd5b6..e38e17f 100644
--- usr.sbin/bgpd/rde.c
+++ usr.sbin/bgpd/rde.c
@@ -38,8 +38,9 @@
#define PFD_PIPE_MAIN 0
#define PFD_PIPE_SESSION 1
-#define PFD_PIPE_SESSION_CTL 2
-#define PFD_PIPE_COUNT 3
+#define PFD_PIPE_SNMP 2
+#define PFD_PIPE_SESSION_CTL 3
+#define PFD_PIPE_COUNT 4
void rde_sighdlr(int);
void rde_dispatch_imsg_session(struct imsgbuf *);
@@ -2581,6 +2582,7 @@ rde_reload_done(void)
conf->listen_addrs = NULL;
conf->csock = NULL;
conf->rcsock = NULL;
+ conf->snmpsock = NULL;
free(nconf);
nconf = NULL;
diff --git usr.sbin/bgpd/session.c usr.sbin/bgpd/session.c
index 61d3356..d919169 100644
--- usr.sbin/bgpd/session.c
+++ usr.sbin/bgpd/session.c
@@ -44,6 +44,7 @@
#include "bgpd.h"
#include "mrt.h"
#include "session.h"
+#include "snmp.h"
#define PFD_PIPE_MAIN 0
#define PFD_PIPE_ROUTE 1
@@ -51,7 +52,8 @@
#define PFD_SOCK_CTL 3
#define PFD_SOCK_RCTL 4
#define PFD_SOCK_PFKEY 5
-#define PFD_LISTENERS_START 6
+#define PFD_SOCK_SNMP 6
+#define PFD_LISTENERS_START 7
void session_sighdlr(int);
int setup_listeners(u_int *);
@@ -109,6 +111,7 @@ int csock = -1, rcsock = -1;
u_int peer_cnt;
struct imsgbuf *ibuf_rde;
struct imsgbuf *ibuf_rde_ctl;
+struct imsgbuf *ibuf_snmp;
struct imsgbuf *ibuf_main;
struct mrt_head mrthead;
@@ -184,6 +187,9 @@ setup_listeners(u_int *la_cnt)
return (0);
}
+extern struct agentx_handle *snmp_agentx;
+extern int sc_snmp;
+
void
session_main(int debug, int verbose)
{
@@ -201,6 +207,7 @@ session_main(int debug, int verbose)
struct listen_addr *la;
void *newp;
short events;
+ time_t reconnect = 0;
if ((pw = getpwnam(BGPD_USER)) == NULL)
fatal(NULL);
@@ -346,6 +353,11 @@ session_main(int debug, int verbose)
set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main);
set_pollfd(&pfd[PFD_PIPE_ROUTE], ibuf_rde);
+ pfd[PFD_SOCK_SNMP].fd = sc_snmp;
+ pfd[PFD_SOCK_SNMP].events = POLLIN;
+ if (snmp_agentx && !TAILQ_EMPTY(&snmp_agentx->w))
+ pfd[PFD_SOCK_SNMP].events |= POLLOUT;
+
ctl_queued = 0;
TAILQ_FOREACH(ctl_conn, &ctl_conns, entry)
ctl_queued += ctl_conn->ibuf.w.queued;
@@ -522,6 +534,11 @@ session_main(int debug, int verbose)
}
}
+ if (pfd[PFD_SOCK_SNMP].revents & POLLIN)
+ snmp_sock(pfd[PFD_SOCK_SNMP].fd, conf);
+ if (pfd[PFD_SOCK_SNMP].revents & POLLOUT)
+ snmp_agentx_send(snmp_agentx, NULL);
+
for (j = PFD_LISTENERS_START; j < idx_listeners; j++)
if (pfd[j].revents & POLLIN)
session_accept(pfd[j].fd);
@@ -540,6 +557,17 @@ session_main(int debug, int verbose)
for (; j < i; j++)
control_dispatch_msg(&pfd[j], &ctl_cnt);
+
+ if (sc_snmp == -1 && (conf->flags & BGPD_FLAG_SNMP_EXPORT)) {
+ if (reconnect < time(NULL)) {
+ imsg_compose(ibuf_main, IMSG_SNMP_OPEN, 0, 0,
+ -1, NULL, 0);
+ reconnect = time(NULL);
+ } else {
+ if (timeout == 0 || timeout > 5)
+ timeout = 5;
+ }
+ }
}
while ((p = peers) != NULL) {
@@ -2497,7 +2525,7 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx,
u_int *listener_cnt)
struct mrt *mrt;
struct imsgbuf *i;
struct peer_config *pconf;
- struct peer *p, *next;
+ struct peer *p, *next, *pp, *nnext;
struct listen_addr *la, *nla;
struct kif *kif;
u_char *data;
@@ -2679,9 +2707,23 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx,
u_int *listener_cnt)
/* add new peers */
for (p = npeers; p != NULL; p = next) {
+
+ /* order lexigraphically for snmp export */
+ for (pp = peers; pp != NULL; pp = nnext) {
+ nnext = pp->next;
+ if (p->conf.id > pp->conf.id)
+ break;
+ }
+
next = p->next;
- p->next = peers;
- peers = p;
+
+ if (pp) {
+ p->next = pp->next;
+ pp->next = p;
+ } else {
+ p->next = peers;
+ peers = p;
+ }
}
/* find ones that need attention */
for (p = peers; p != NULL; p = p->next) {
@@ -2895,6 +2937,12 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx,
u_int *listener_cnt)
"IMSG_SESSION_RESTARTED");
}
break;
+ case IMSG_SNMP_OPEN:
+ if (idx != PFD_PIPE_MAIN)
+ fatalx("snmp open not from parent");
+ log_info("received snmp socket from parent");
+ sc_snmp = snmp_getsock(&imsg);
+ break;
default:
break;
}
diff --git usr.sbin/bgpd/session.h usr.sbin/bgpd/session.h
index 9aa7ca0..75226f2 100644
--- usr.sbin/bgpd/session.h
+++ usr.sbin/bgpd/session.h
@@ -303,3 +303,8 @@ void timer_set(struct peer *, enum
Timer, u_int);
void timer_stop(struct peer *, enum Timer);
void timer_remove(struct peer *, enum Timer);
void timer_remove_all(struct peer *);
+
+/* snmp.c */
+int snmp_setsock(struct bgpd_config *);
+void snmp_sock(int, struct bgpd_config *);
+int snmp_getsock(struct imsg *);
diff --git usr.sbin/bgpd/snmp.c usr.sbin/bgpd/snmp.c
new file mode 100644
index 0000000..fe9e4c9
--- /dev/null
+++ usr.sbin/bgpd/snmp.c
@@ -0,0 +1,754 @@
+/* $OpenBSD: snmp.c,v 1.23 2015/01/22 17:42:09 reyk Exp $ */
+
+/*
+ * Copyright (c) 2008 - 2014 Reyk Floeter <[email protected]>
+ *
+ * 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/queue.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <fcntl.h>
+
+#include <netinet/in.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <event.h>
+#include <imsg.h>
+#include <pwd.h>
+#include <signal.h>
+
+#include "bgpd.h"
+#include "snmp.h"
+#include "session.h"
+
+#if 0
+#define HOST_MAX_SUBIDX 10
+#define TABLE_MAX_SUBIDX 3
+#define ROUTER_MAX_SUBIDX 6
+#define NETROUTE_MAX_SUBIDX 5
+#define RELAY_MAX_SUBIDX 10
+#define SESSION_MAX_SUBIDX 12
+#define RDR_MAX_SUBIDX 10
+#endif
+
+#define OIDIDX_bgp4 8
+
+static struct snmp_oid bgpd4oid = {
+ { 1, 3, 6, 1, 1, 2, 1, 15 },
+ 8
+};
+
+struct agentx_handle *snmp_agentx = NULL;
+
+void snmp_dispatch_imsg_parent(struct imsgbuf *);
+void snmp_dispatch_imsg_rde(struct imsgbuf *);
+void snmp_dispatch_imsg_session(struct imsgbuf *);
+
+int snmp_getsock(struct imsg *);
+int snmp_string2oid(const char *, struct snmp_oid *);
+char *snmp_oid2string(struct snmp_oid *, char *, size_t);
+void snmp_agentx_process(struct agentx_handle *, struct agentx_pdu *,
+ void *);
+int snmp_register(void);
+int snmp_unregister(void);
+
+struct peer *snmp_peer_byidx(u_int *, u_int *, u_int, u_int);
+int snmp_peer_entry(struct snmp_oid *, struct agentx_pdu *,
+ int, uint32_t, uint32_t, u_int);
+
+int sc_snmp = -1;
+
+extern struct bgpd_config *conf;
+
+int
+snmp_setsock(struct bgpd_config *bgpd)
+{
+ struct sockaddr_un sun;
+ int flags, s = -1;
+
+ if (bgpd->snmpsock == NULL)
+ return (-1);
+
+ log_info("opening agentx socket at %s", bgpd->snmpsock);
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ return (-1);
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ if (strlcpy(sun.sun_path, bgpd->snmpsock,
+ sizeof(sun.sun_path)) >= sizeof(sun.sun_path))
+ fatalx("invalid socket path");
+
+ if ((flags = fcntl(s, F_GETFL, 0)) == -1)
+ fatal("fcntl F_GETFL");
+
+ flags |= O_NONBLOCK;
+
+ if ((flags = fcntl(s, F_SETFL, flags)) == -1)
+ fatal("fcntl F_SETFL");
+
+ if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("unable to open agentx socket");
+ close(s);
+ s = -1;
+ }
+ return (s);
+}
+
+int
+snmp_getsock(struct imsg *imsg)
+{
+ struct agentx_pdu *pdu;
+
+ if (imsg->fd == -1)
+ return (-1);
+
+ log_debug("%s: got new snmp socket %d", __func__, imsg->fd);
+
+ if ((snmp_agentx = snmp_agentx_alloc(imsg->fd)) == NULL)
+ fatal("snmp_getsock: agentx alloc");
+ if ((pdu = snmp_agentx_open_pdu(snmp_agentx, "bgpd", NULL)) == NULL)
+ fatal("snmp_getsock: agentx pdu");
+ (void)snmp_agentx_send(snmp_agentx, pdu);
+
+ return (imsg->fd);
+}
+
+void
+snmp_sock(int fd, struct bgpd_config *bgpd)
+{
+ struct agentx_pdu *pdu;
+
+ if ((pdu = snmp_agentx_recv(snmp_agentx)) == NULL) {
+ if (snmp_agentx->error) {
+ log_warnx("agentx protocol error '%i'",
+ snmp_agentx->error);
+ goto close;
+ }
+ if (errno != EAGAIN) {
+ if (errno != 0)
+ log_warn("agentx socket error");
+ goto close;
+ }
+
+ /* short read */
+ return;
+ }
+
+ snmp_agentx_process(snmp_agentx, pdu, bgpd);
+ return;
+
+ close:
+ log_debug("%s: snmp socket closed %d", __func__, sc_snmp);
+ snmp_agentx_free(snmp_agentx);
+ sc_snmp = -1;
+ snmp_agentx = NULL;
+}
+
+void
+snmp_agentx_process(struct agentx_handle *h, struct agentx_pdu *pdu, void *arg)
+{
+ struct bgpd_config *bgpd = arg;
+ struct agentx_getbulk_repeaters repeaters;
+ struct agentx_search_range sr;
+ struct snmp_oid oid;
+ struct agentx_close_request_data close_hdr;
+ struct agentx_pdu *resp;
+ u_int erridx = 0;
+ int getnext = 0, repetitions;
+ int nonrepeaters, maxrepetitions;
+ uint8_t version = (1 << 3);
+
+ nonrepeaters = maxrepetitions = -1;
+
+ switch (pdu->hdr->type) {
+ 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);
+ sc_snmp = -1;
+ snmp_agentx = NULL;
+ break;
+
+ case AGENTX_GET_BULK:
+ if (snmp_agentx_read_raw(pdu,
+ &repeaters, sizeof(repeaters)) == -1)
+ break;
+
+ nonrepeaters = repeaters.nonrepeaters;
+ maxrepetitions = repeaters.maxrepetitions;
+
+ /* FALLTHROUGH */
+ case AGENTX_GET:
+ case AGENTX_GET_NEXT:
+ if ((resp = snmp_agentx_response_pdu(0,
+ AGENTX_ERR_NONE, 0)) == NULL) {
+ log_warn("%s unable to allocate response pdu",
+ __func__);
+ break;
+ }
+ repetitions = 0;
+ repeat:
+ while (pdu->datalen > sizeof(struct agentx_hdr)) {
+ uint32_t infoendidx, infoentryendidx,
+ infoentryidxendidx;
+
+ erridx++;
+
+ /* AgentX GETs are the OID followed by the null OID */
+ if (snmp_agentx_read_searchrange(pdu, &sr) == -1) {
+ snmp_agentx_pdu_free(resp);
+ resp = NULL;
+ break;
+ }
+
+ if (sr.end.o_n >= OIDIDX_bgp4 + 1)
+ infoendidx = sr.end.o_id[OIDIDX_bgp4];
+ else
+ infoendidx = UINT32_MAX;
+ if (sr.end.o_n >= OIDIDX_bgp4 + 1 + 1)
+ infoentryendidx =
+ sr.end.o_id[OIDIDX_bgp4 + 1];
+ else
+ infoentryendidx = UINT32_MAX;
+ if (sr.end.o_n >= OIDIDX_bgp4 + 2 + 1)
+ infoentryidxendidx =
+ sr.end.o_id[OIDIDX_bgp4 + 2];
+ else
+ infoentryidxendidx = UINT32_MAX;
+
+ bcopy(&sr.start, &oid, sizeof(oid));
+
+ /*
+ * If the requested OID is not part of the registered
+ * MIB, return "no such object", per RFC
+ */
+ if (snmp_oid_cmp(&bgpd4oid, &oid) == -1) {
+ if (snmp_agentx_varbind(resp, &sr.start,
+ AGENTX_NO_SUCH_OBJECT, NULL, 0) == -1) {
+ log_warn("%s: unable to generate"
+ " response", __func__);
+ snmp_agentx_pdu_free(resp);
+ resp = NULL;
+ }
+ goto reply;
+ }
+
+ /*
+ * If not including the starting OID, increment
+ * here to go to the 'next' OID to allow the lookups
+ * to work correctly, as they do 'include' matching
+ */
+ if (pdu->hdr->type == AGENTX_GET_NEXT)
+ getnext = 1;
+
+ switch (oid.o_id[OIDIDX_bgp4]) {
+ case 1: /* bgpVersion */
+ if (infoendidx < 1)
+ break;
+
+ if (snmp_agentx_varbind(resp, &oid,
+ AGENTX_OCTET_STRING, &version,
+ sizeof(version)) == 0)
+ break;
+ if (!getnext)
+ goto nosuchinstance;
+
+ oid.o_id[OIDIDX_bgp4] = 2;
+ oid.o_id[OIDIDX_bgp4 + 1] = 0;
+ oid.o_id[OIDIDX_bgp4 + 2] = 0;
+ /* FALLTHROUGH */
+ case 2: /* bgpLocalAs */
+ if (infoendidx < 2)
+ break;
+
+ if (snmp_agentx_varbind(resp, &oid,
+ AGENTX_INTEGER, &bgpd->as,
+ sizeof(bgpd->as)) == 0)
+ break;
+ if (!getnext)
+ goto nosuchinstance;
+
+ oid.o_id[OIDIDX_bgp4] = 3;
+ oid.o_id[OIDIDX_bgp4 + 1] = 0;
+ oid.o_id[OIDIDX_bgp4 + 2] = 0;
+ /* FALLTHROUGH */
+ case 3: /* bgpPeerTable */
+ if (infoendidx < 3)
+ break;
+
+ if (snmp_peer_entry(&oid, resp, getnext, 0, 0,
0) == 0)
+ break;
+
+ /* XXX -- TODO */
+ if (!getnext)
+ goto nosuchinstance;
+
+ oid.o_id[OIDIDX_bgp4] = 4;
+ oid.o_id[OIDIDX_bgp4 + 1] = 0;
+ oid.o_id[OIDIDX_bgp4 + 2] = 0;
+ /* FALLTHROUGH */
+ case 4: /* bgpIdentifier */
+ if (infoendidx < 4)
+ break;
+
+ /* bgpd.bgpid is already in network order */
+ if (snmp_agentx_varbind(resp, &oid,
+ AGENTX_IP_ADDRESS, &bgpd->bgpid,
+ sizeof(bgpd->bgpid)) == 0)
+ break;
+ if (!getnext)
+ goto nosuchinstance;
+
+ oid.o_id[OIDIDX_bgp4] = 5;
+ oid.o_id[OIDIDX_bgp4 + 1] = 0;
+ oid.o_id[OIDIDX_bgp4 + 2] = 0;
+ /* FALLTHROUGH */
+ case 5: /*
bgpReceivedPathAttrTable */
+ if (infoendidx < 5)
+ break;
+
+ /* Obsolete, unimplemented */
+ if (!getnext)
+ goto nosuchinstance;
+
+ oid.o_id[OIDIDX_bgp4] = 6;
+ oid.o_id[OIDIDX_bgp4 + 1] = 0;
+ oid.o_id[OIDIDX_bgp4 + 2] = 0;
+ /* FALLTHROUGH */
+ case 6: /* bgp4PathAttrTable */
+ if (infoendidx < 6)
+ break;
+
+ if (!getnext)
+ goto nosuchinstance;
+
+ goto reply;
+ default:
+ nosuchinstance:
+ log_warnx("unknown index %i",
+ oid.o_id[OIDIDX_bgp4]);
+ if (snmp_agentx_varbind(resp, &sr.start,
+ AGENTX_NO_SUCH_INSTANCE, NULL, 0) == -1) {
+ log_warn("%s: unable to generate"
+ " response", __func__);
+ snmp_agentx_pdu_free(resp);
+ resp = NULL;
+ }
+ goto reply;
+ }
+ }
+
+ if (pdu->hdr->type == AGENTX_GET_BULK) {
+ if (nonrepeaters >= 0)
+ nonrepeaters--;
+ else if (repetitions < maxrepetitions) {
+ repetitions++;
+ goto repeat;
+ }
+ }
+ reply:
+ if (resp) {
+ snmp_agentx_send(snmp_agentx, resp);
+ }
+
+ break;
+
+ case AGENTX_TEST_SET:
+ case AGENTX_COMMIT_SET:
+ case AGENTX_UNDO_SET:
+ case AGENTX_CLEANUP_SET:
+ log_warnx("unimplemented request type '%s'",
+ snmp_agentx_type2name(pdu->hdr->type));
+ break;
+
+ case AGENTX_RESPONSE:
+ 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;
+ if (snmp_register() == -1)
+ log_warn("failed to register MIB");
+ break;
+
+ case AGENTX_CLOSE:
+ if (snmp_agentx_response(h, pdu) == -1)
+ break;
+ break;
+
+ case AGENTX_REGISTER:
+ if (snmp_agentx_response(h, pdu) == -1)
+ break;
+ break;
+
+ case AGENTX_UNREGISTER:
+ if (snmp_agentx_response(h, pdu) == -1)
+ break;
+ break;
+
+ default:
+ if (snmp_agentx_response(h, pdu) == -1)
+ break;
+ break;
+ }
+ break;
+
+ /* these are nonsensical for subagents to receive */
+ case AGENTX_OPEN:
+ case AGENTX_REGISTER:
+ case AGENTX_UNREGISTER:
+ case AGENTX_NOTIFY:
+ case AGENTX_PING:
+ case AGENTX_INDEX_ALLOCATE:
+ case AGENTX_INDEX_DEALLOCATE:
+ case AGENTX_ADD_AGENT_CAPS:
+ case AGENTX_REMOVE_AGENT_CAPS:
+ log_warnx("ignoring request type '%s'",
+ snmp_agentx_type2name(pdu->hdr->type));
+ break;
+
+ default:
+ log_warnx("unknown request type '%i'", pdu->hdr->type);
+ if (snmp_agentx_response(h, pdu) == -1)
+ break;
+ break;
+ }
+
+ snmp_agentx_pdu_free(pdu);
+ return;
+}
+
+#define PEER_MAX_SUBIDX 24
+
+int
+snmp_register(void)
+{
+ struct agentx_pdu *pdu;
+
+ if ((pdu = snmp_agentx_register_pdu(&bgpd4oid, 3, 0, 0)) == NULL)
+ return (-1);
+
+ if (snmp_agentx_send(snmp_agentx, pdu) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+snmp_unregister(void)
+{
+ struct agentx_pdu *pdu;
+
+ if ((pdu = snmp_agentx_unregister_pdu(&bgpd4oid, 0, 0)) == NULL)
+ return (-1);
+
+ if (snmp_agentx_send(snmp_agentx, pdu) == -1)
+ return (-1);
+
+ return (0);
+}
+
+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);
+}
+
+extern struct peer *peers;
+
+struct peer *
+snmp_peer_byidx(u_int *instanceidx, u_int *objectidx, u_int getnext,
+ u_int include)
+{
+ struct peer *peer;
+
+ if (*objectidx > PEER_MAX_SUBIDX)
+ return (NULL);
+ if (*objectidx == 0) {
+ if (!getnext)
+ return (NULL);
+ *objectidx = 1;
+ }
+
+ restart:
+ for (peer = peers; peer != NULL; peer = peer->next) {
+ if (peer->conf.id >= *instanceidx) {
+ if (getnext) {
+ /* Lexographical ordering */
+
+ /* 1) try the next instance index */
+ if (peer->conf.id == *instanceidx && !include)
+ peer = peer->next;
+ if (peer) {
+ *instanceidx = peer->conf.id;
+ return (peer);
+ }
+
+ /* 2) try the next object index */
+ if (*objectidx < PEER_MAX_SUBIDX) {
+ *objectidx += 1;
+ *instanceidx += 1;
+ include = 1;
+ goto restart;
+ }
+
+ /* 3) no further OIDs under this prefix */
+ return (peer);
+ }
+
+ if (peer->conf.id == *instanceidx)
+ return (peer);
+
+ return (NULL);
+ }
+ }
+ return (NULL);
+}
+
+int
+snmp_peer_entry(struct snmp_oid *oid, struct agentx_pdu *resp,
+ int getnext, uint32_t einstanceidx, uint32_t eobjectidx, u_int include)
+{
+ static char nullpeerid[4] = { 0, 0, 0, 0 };
+ static u_int32_t minasorigininterval = 15;
+ static u_int32_t routeadvertisemininterval = 1;
+ static u_int32_t nullcounter = 0;
+ static u_int32_t bgpport = BGP_PORT;
+ struct peer *peer;
+ u_int instanceidx, objectidx;
+ u_int32_t total, negproto, holdtime, keepalive, status;
+ u_int32_t connectretry;
+ char lasterror[2];
+
+ if (oid->o_id[OIDIDX_bgp4 + 1] != 1) /* bgpPeerEntry */
+ return (-1);
+
+ instanceidx = oid->o_id[OIDIDX_bgp4 + 3];
+ objectidx = oid->o_id[OIDIDX_bgp4 + 2];
+ peer = snmp_peer_byidx(&instanceidx, &objectidx, getnext, include);
+ if (peer == NULL)
+ return (-1);
+
+log_warnx("%s: found peer with id %d", __func__, peer->conf.id);
+
+ switch (oid->o_id[OIDIDX_bgp4 + 2]) {
+ case 1: /* bgpPeerIdentifier */
+ if (peer->state == STATE_OPENCONFIRM ||
+ peer->state == STATE_ESTABLISHED) {
+ if (snmp_agentx_varbind(resp, oid, AGENTX_IP_ADDRESS,
+ &peer->conf.remote_addr.v4,
+ sizeof(peer->conf.remote_addr.v4)) == -1)
+ return (-1);
+ } else {
+ if (snmp_agentx_varbind(resp, oid, AGENTX_IP_ADDRESS,
+ nullpeerid, sizeof(nullpeerid)) == -1)
+ return (-1);
+ }
+ break;
+ case 2: /* bgpPeerState */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &peer->state, sizeof(peer->state)) == -1)
+ return (-1);
+ break;
+ case 3: /* bgpPeerAdminStatus */
+ if (peer->state == STATE_IDLE &&
+ timer_running(peer, Timer_IdleHold, NULL) == 0)
+ status = 1; /* stop(1) */
+ else
+ status = 2; /* start(2) */
+
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &status, sizeof(status)) == -1)
+ return (-1);
+ break;
+ case 4: /* bgpPeerNegotiatedVersion */
+ if (peer->state == STATE_OPENCONFIRM ||
+ peer->state == STATE_ESTABLISHED)
+ negproto = 0;
+ else
+ negproto = 4;
+
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &negproto, sizeof(negproto)) == -1)
+ return (-1);
+ break;
+ case 5: /* bgpPeerLocalAddr */
+ if (peer->conf.local_addr.aid != AID_INET)
+ return (-1);
+ if (snmp_agentx_varbind(resp, oid, AGENTX_IP_ADDRESS,
+ &peer->conf.local_addr.v4,
+ sizeof(peer->conf.local_addr.v4)) == -1)
+ return (-1);
+ break;
+ case 6: /* bgpPeerLocalPort */
+ if (snmp_agentx_varbind(resp, oid,
+ AGENTX_INTEGER, &bgpport, sizeof(bgpport)) == -1)
+ return (-1);
+ break;
+ case 7: /* bgpPeerRemoteAddr */
+ if (peer->conf.remote_addr.aid != AID_INET)
+ return (-1);
+ if (snmp_agentx_varbind(resp, oid, AGENTX_IP_ADDRESS,
+ &peer->conf.remote_addr.v4,
+ sizeof(peer->conf.remote_addr.v4)) == -1)
+ return (-1);
+ break;
+ case 8: /* bgpPeerRemotePort */
+ if (snmp_agentx_varbind(resp, oid,
+ AGENTX_INTEGER, &bgpport, sizeof(bgpport)) == -1)
+ return (-1);
+ break;
+ case 9: /* bgpPeerRemoteAs */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &peer->conf.remote_as, sizeof(peer->conf.remote_as)) == -1)
+ return (-1);
+ break;
+ case 10: /* bgpPeerInUpdates */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &peer->stats.msg_rcvd_update,
+ sizeof(peer->stats.msg_rcvd_update)) == -1)
+ return (-1);
+ break;
+ case 11: /* bgpPeerOutUpdates */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &peer->stats.msg_sent_update,
+ sizeof(peer->stats.msg_sent_update)) == -1)
+ return (-1);
+ break;
+ case 12: /* bgpPeerInTotalMessages */
+ total =
+ peer->stats.msg_rcvd_open +
+ peer->stats.msg_rcvd_update +
+ peer->stats.msg_rcvd_notification +
+ peer->stats.msg_rcvd_keepalive +
+ peer->stats.msg_rcvd_rrefresh;
+
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER, &total,
+ sizeof(total)) == -1)
+ return (-1);
+ break;
+ case 13: /* bgpPeerOutTotalMessages */
+ total =
+ peer->stats.msg_sent_open +
+ peer->stats.msg_sent_update +
+ peer->stats.msg_sent_notification +
+ peer->stats.msg_sent_keepalive +
+ peer->stats.msg_sent_rrefresh;
+
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER, &total,
+ sizeof(total)) == -1)
+ return (-1);
+ break;
+ case 14: /* bgpPeerLastError */
+ lasterror[0] = peer->stats.last_sent_errcode;
+ lasterror[1] = peer->stats.last_sent_suberr;
+
+ if (snmp_agentx_varbind(resp, oid, AGENTX_OCTET_STRING,
+ lasterror, sizeof(lasterror)) == -1)
+ return (-1);
+ break;
+ case 15: /* bgpPeerFsmEstablishedTransitions */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_COUNTER32,
+ &nullcounter, sizeof(nullcounter)) == -1)
+ return (-1);
+ break;
+ case 16: /* bgpPeerFsmEstablishedTime */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_GAUGE32,
+ &nullcounter, sizeof(nullcounter)) == -1)
+ return (-1);
+ break;
+ case 17: /* bgpPeerConnectRetryInterval */
+ connectretry = conf->connectretry;
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &connectretry, sizeof(connectretry)) == -1)
+ return (-1);
+ break;
+ case 18: /* bgpPeerHoldTime */
+ holdtime = peer->holdtime;
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &holdtime, sizeof(holdtime)) == -1)
+ return (-1);
+ break;
+ case 19: /* bgpPeerKeepAlive */
+ keepalive = peer->holdtime / 3;
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &keepalive, sizeof(keepalive)) == -1)
+ return (-1);
+ break;
+ case 20: /* bgpPeerHoldTimeConfigured */
+ holdtime = peer->conf.holdtime;
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &holdtime, sizeof(holdtime)) == -1)
+ return (-1);
+ break;
+ case 21: /* bgpPeerKeepAliveConfigured */
+ keepalive = peer->conf.holdtime / 3;
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &keepalive, sizeof(keepalive)) == -1)
+ return (-1);
+ break;
+ case 22: /* bgpPeerMinASOriginInterval */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &minasorigininterval, sizeof(minasorigininterval)) == -1)
+ return (-1);
+ break;
+ case 23: /* bgpPeerMinRouteAdvertisementInterval */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_INTEGER,
+ &routeadvertisemininterval,
+ sizeof(routeadvertisemininterval)) == -1)
+ return (-1);
+ break;
+ case 24: /* bgpPeerInUpdateElapsedTime */
+ if (snmp_agentx_varbind(resp, oid, AGENTX_GAUGE32,
+ &nullcounter, sizeof(nullcounter)) == -1)
+ return (-1);
+ break;
+ default:
+ fatalx("unhandled table element id");
+ }
+
+ return (0);
+}