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);
+}

Reply via email to