diff options
author | Martijn van Duren <martijn@cvs.openbsd.org> | 2020-09-14 11:30:26 +0000 |
---|---|---|
committer | Martijn van Duren <martijn@cvs.openbsd.org> | 2020-09-14 11:30:26 +0000 |
commit | 5a7efb52ceb299c56ae984c1f8e8b18a92200fa3 (patch) | |
tree | 0418083853f17f5bcf65c95c9a2ff69a7e758bdb /usr.sbin | |
parent | 7846962f31ac6b46ede4ad574c9b6f4e07f905e5 (diff) |
Rewrite the agentx code of relayd. This new framework should allow us
to add new objects easier if so desired and should handle a lot more
corner-cases.
This commit should also fix the following:
- On most (all) tables it omits the *Entry elements, making it not map to
OPENBSD-RELAYD-MIB.txt.
- sstolen returns the size of the sockaddr_in{,6}, instead of the
sin{,6}_addr resulting in garbage data to be put in the ip-field.
- relaydSessionPortIn and relaydSessionPortOut are swapped
- relaydSessions only uses relaydSessionIndex, while
OPENBSD-RELAYD-MIB.txt says it should have 2 indices
- miscellaneous minor things related to the AGENTX-protocol, like wonky
index handeling and returning NOSUCHINSTANCE where NOSUCHOBJECT
should be returned, etc.
This commit does remove traps, but it's large enough as is and I intent
on adding it soon(tm). It also deprecates the snmp keyword in favour of
an agentx keyword. The snmp keyword is still available, but will be
removed in the future.
Tweaks and OK denis@ on the relayd parts
Tweaks and OK claudio@ on the agentx parts
"Get it in" deraadt@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/relayd/Makefile | 10 | ||||
-rw-r--r-- | usr.sbin/relayd/agentx.c | 2042 | ||||
-rw-r--r-- | usr.sbin/relayd/agentx.h | 232 | ||||
-rw-r--r-- | usr.sbin/relayd/agentx_control.c | 1114 | ||||
-rw-r--r-- | usr.sbin/relayd/parse.y | 81 | ||||
-rw-r--r-- | usr.sbin/relayd/pfe.c | 11 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.c | 7 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.conf.5 | 29 | ||||
-rw-r--r-- | usr.sbin/relayd/relayd.h | 28 | ||||
-rw-r--r-- | usr.sbin/relayd/snmp.c | 1745 | ||||
-rw-r--r-- | usr.sbin/relayd/snmp.h | 452 | ||||
-rw-r--r-- | usr.sbin/relayd/subagentx.c | 3994 | ||||
-rw-r--r-- | usr.sbin/relayd/subagentx.h | 147 | ||||
-rw-r--r-- | usr.sbin/relayd/subagentx_internal.h | 124 | ||||
-rw-r--r-- | usr.sbin/relayd/subagentx_log.c | 316 |
15 files changed, 7180 insertions, 3152 deletions
diff --git a/usr.sbin/relayd/Makefile b/usr.sbin/relayd/Makefile index 9ff3173f784..a9e28a278fc 100644 --- a/usr.sbin/relayd/Makefile +++ b/usr.sbin/relayd/Makefile @@ -1,12 +1,12 @@ -# $OpenBSD: Makefile,v 1.33 2020/06/30 16:59:38 martijn Exp $ +# $OpenBSD: Makefile,v 1.34 2020/09/14 11:30:25 martijn Exp $ PROG= relayd SRCS= parse.y -SRCS+= agentx.c ca.c carp.c check_icmp.c check_script.c \ - check_tcp.c check_tls.c config.c control.c hce.c log.c \ - name2id.c pfe.c pfe_filter.c pfe_route.c proc.c \ +SRCS+= agentx.c agentx_control.c ca.c carp.c check_icmp.c \ + check_script.c check_tcp.c check_tls.c config.c control.c \ + hce.c log.c name2id.c pfe.c pfe_filter.c pfe_route.c proc.c \ relay.c relay_http.c relay_udp.c relayd.c \ - shuffle.c snmp.c ssl.c util.c + shuffle.c ssl.c subagentx.c subagentx_log.c util.c MAN= relayd.8 relayd.conf.5 LDADD= -levent -ltls -lssl -lcrypto -lutil diff --git a/usr.sbin/relayd/agentx.c b/usr.sbin/relayd/agentx.c index 33708f0a4b9..4ab852f8bfd 100644 --- a/usr.sbin/relayd/agentx.c +++ b/usr.sbin/relayd/agentx.c @@ -1,6 +1,5 @@ -/* $OpenBSD: agentx.c,v 1.15 2020/06/05 19:50:59 denis Exp $ */ /* - * Copyright (c) 2013,2014 Bret Stephen Lambert <blambert@openbsd.org> + * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,1087 +13,1348 @@ * 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/un.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <endian.h> #include <errno.h> +#include <inttypes.h> #include <stdlib.h> +#include <stdint.h> #include <stdio.h> #include <string.h> +#include <strings.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) +#include "agentx.h" + +#define AGENTX_PDU_HEADER 20 + +static int agentx_pdu_need(struct agentx *, size_t); +static int agentx_pdu_header(struct agentx *, + enum agentx_pdu_type, uint8_t, uint32_t, uint32_t, uint32_t, + struct agentx_ostring *); +static uint32_t agentx_packetid(struct agentx *); +static uint32_t agentx_pdu_queue(struct agentx *); +static int agentx_pdu_add_uint16(struct agentx *, uint16_t); +static int agentx_pdu_add_uint32(struct agentx *, uint32_t); +static int agentx_pdu_add_uint64(struct agentx *, uint64_t); +static int agentx_pdu_add_oid(struct agentx *, struct agentx_oid *, int); +static int agentx_pdu_add_str(struct agentx *, struct agentx_ostring *); +static int agentx_pdu_add_varbindlist( struct agentx *, struct agentx_varbind *, + size_t); +static uint16_t agentx_pdutoh16(struct agentx_pdu_header *, uint8_t *); +static uint32_t agentx_pdutoh32(struct agentx_pdu_header *, uint8_t *); +static uint64_t agentx_pdutoh64(struct agentx_pdu_header *, uint8_t *); +static ssize_t agentx_pdutooid(struct agentx_pdu_header *, struct agentx_oid *, + uint8_t *, size_t); +static ssize_t agentx_pdutoostring(struct agentx_pdu_header *, + struct agentx_ostring *, uint8_t *, size_t); +static ssize_t agentx_pdutovarbind(struct agentx_pdu_header *, + struct agentx_varbind *, uint8_t *, size_t); + +struct agentx * +agentx_new(int fd) { - struct agentx_handle *h; + struct agentx *ax; - 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 (fd == -1) { + errno = EINVAL; + return NULL; + } - if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) + if ((ax = calloc(1, sizeof(*ax))) == NULL) + return NULL; + ax->ax_fd = fd; + if ((ax->ax_rbuf = malloc(512)) == NULL) goto fail; + ax->ax_rbsize = 512; + ax->ax_byteorder = AGENTX_BYTE_ORDER_NATIVE; - if ((h = snmp_agentx_fdopen(s, descr, oid)) == NULL) - goto fail; + return ax; - return (h); - fail: - close(s); - return (NULL); +fail: + free(ax); + 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) +void +agentx_free(struct agentx *ax) { - 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); + if (ax == NULL) + return; + close(ax->ax_fd); + free(ax->ax_rbuf); + free(ax->ax_wbuf); + free(ax->ax_packetids); + free(ax); } -void -snmp_agentx_free(struct agentx_handle *h) +struct agentx_pdu * +agentx_recv(struct agentx *ax) { 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); + struct agentx_pdu_header header; + struct agentx_pdu_response *response; + struct agentx_varbind *varbind; + struct agentx_pdu_searchrangelist *srl; + struct agentx_pdu_varbindlist *vbl; + struct agentx_searchrange *sr; + size_t rbsize, packetidx = 0, i, rawlen; + ssize_t nread; + uint8_t *u8; + uint8_t *rbuf; + int found; + + /* Only read a single packet at a time to make sure libevent triggers */ + if (ax->ax_rblen < AGENTX_PDU_HEADER) { + if ((nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, + AGENTX_PDU_HEADER - ax->ax_rblen)) == 0) { + errno = ECONNRESET; + return NULL; + } + if (nread == -1) + return NULL; + ax->ax_rblen += nread; + if (ax->ax_rblen < AGENTX_PDU_HEADER) { + errno = EAGAIN; + return NULL; + } } - while ((pdu = TAILQ_FIRST(&h->inflight))) { - TAILQ_REMOVE(&h->w, pdu, entry); - snmp_agentx_pdu_free(pdu); + u8 = ax->ax_rbuf; + header.aph_version = *u8++; + header.aph_type = *u8++; + header.aph_flags = *u8++; + u8++; + header.aph_sessionid = agentx_pdutoh32(&header, u8); + u8 += 4; + header.aph_transactionid = agentx_pdutoh32(&header, u8); + u8 += 4; + header.aph_packetid = agentx_pdutoh32(&header, u8); + u8 += 4; + header.aph_plength = agentx_pdutoh32(&header, u8); + u8 += 4; + + if (header.aph_version != 1) { + errno = EPROTO; + return NULL; + } + if (ax->ax_rblen < AGENTX_PDU_HEADER + header.aph_plength) { + if (AGENTX_PDU_HEADER + header.aph_plength > ax->ax_rbsize) { + rbsize = (((AGENTX_PDU_HEADER + header.aph_plength) + / 512) + 1) * 512; + if ((rbuf = recallocarray(ax->ax_rbuf, ax->ax_rbsize, + rbsize, sizeof(*rbuf))) == NULL) + return NULL; + ax->ax_rbsize = rbsize; + ax->ax_rbuf = rbuf; + } + nread = read(ax->ax_fd, ax->ax_rbuf + ax->ax_rblen, + header.aph_plength - (ax->ax_rblen - AGENTX_PDU_HEADER)); + if (nread == 0) + errno = ECONNRESET; + if (nread <= 0) + return NULL; + ax->ax_rblen += nread; + if (ax->ax_rblen < AGENTX_PDU_HEADER + header.aph_plength) { + errno = EAGAIN; + return NULL; + } } - 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); + return NULL; + + memcpy(&(pdu->ap_header), &header, sizeof(header)); + +#if defined(AGENTX_DEBUG) && defined(AGENTX_DEBUG_VERBOSE) + { + char chars[4]; + int print = 1; + + fprintf(stderr, "received packet:\n"); + for (i = 0; i < pdu->ap_header.aph_plength + AGENTX_PDU_HEADER; + i++) { + fprintf(stderr, "%02hhx ", ax->ax_rbuf[i]); + chars[i % 4] = ax->ax_rbuf[i]; + if (!isprint(ax->ax_rbuf[i])) + print = 0; + if (i % 4 == 3) { + if (print) + fprintf(stderr, "%.4s", chars); + fprintf(stderr, "\n"); + print = 1; + } + } } +#endif - pdu->buflen = PDU_BUFLEN; - - bzero(pdu->buffer, pdu->buflen); - pdu->ptr = pdu->buffer + sizeof(struct agentx_hdr); - pdu->ioptr = pdu->buffer; - pdu->hdr = (struct agentx_hdr *)pdu->buffer; - pdu->hdr->version = AGENTX_VERSION; - pdu->hdr->flags = AGENTX_LOCAL_BYTE_ORDER_FLAG; - pdu->hdr->reserved = 0; - pdu->hdr->length = 0; - pdu->datalen = sizeof(struct agentx_hdr); - - return (pdu); -} - -/* - * Read the response PDU for a generic operation. - */ -int -snmp_agentx_response(struct agentx_handle *h, struct agentx_pdu *pdu) -{ - struct agentx_response_data resp; - - if (snmp_agentx_read_raw(pdu, &resp, sizeof(resp)) == -1) - return (-1); - - if (!snmp_agentx_byteorder_native(pdu->hdr)) { - resp.error = snmp_agentx_int16_byteswap(resp.error); - resp.index = snmp_agentx_int16_byteswap(resp.index); + u8 = (ax->ax_rbuf) + AGENTX_PDU_HEADER; + rawlen = pdu->ap_header.aph_plength; + if (pdu->ap_header.aph_flags & AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) { + nread = agentx_pdutoostring(&header, &(pdu->ap_context), u8, + rawlen); + if (nread == -1) + goto fail; + rawlen -= nread; + u8 += nread; } - h->error = resp.error; - if (resp.error != AGENTX_ERR_NONE) - return (-1); - - return (0); -} - -/* - * Read the response PDU for an open operation. - */ -int -snmp_agentx_open_response(struct agentx_handle *h, struct agentx_pdu *pdu) -{ - - if (snmp_agentx_response(h, pdu) == -1) - return (-1); - - h->sessionid = pdu->hdr->sessionid; - return (0); -} - -void -snmp_agentx_pdu_free(struct agentx_pdu *pdu) -{ - free(pdu->buffer); - 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); + switch (pdu->ap_header.aph_type) { + case AGENTX_PDU_TYPE_GETBULK: + if (rawlen < 4) { + errno = EPROTO; + goto fail; + } + pdu->ap_payload.ap_getbulk.ap_nonrep = + agentx_pdutoh16(&header, u8); + u8 += 2; + pdu->ap_payload.ap_getbulk.ap_maxrep = + agentx_pdutoh16(&header, u8); + u8 += 2; + srl = &(pdu->ap_payload.ap_getbulk.ap_srl); + rawlen -= 4; + /* FALLTHROUGH */ + case AGENTX_PDU_TYPE_GET: + case AGENTX_PDU_TYPE_GETNEXT: + if (pdu->ap_header.aph_type != AGENTX_PDU_TYPE_GETBULK) + srl = &(pdu->ap_payload.ap_srl); + while (rawlen > 0 ) { + srl->ap_nsr++; + sr = reallocarray(srl->ap_sr, srl->ap_nsr, sizeof(*sr)); + if (sr == NULL) + goto fail; + srl->ap_sr = sr; + sr += (srl->ap_nsr - 1); + if ((nread = agentx_pdutooid(&header, &(sr->asr_start), + u8, rawlen)) == -1) + goto fail; + rawlen -= nread; + u8 += nread; + if ((nread = agentx_pdutooid(&header, &(sr->asr_stop), + u8, rawlen)) == -1) + goto fail; + rawlen -= nread; + u8 += nread; + } + break; + case AGENTX_PDU_TYPE_TESTSET: + vbl = &(pdu->ap_payload.ap_vbl); + while (rawlen > 0) { + varbind = recallocarray(vbl->ap_varbind, + vbl->ap_nvarbind, vbl->ap_nvarbind + 1, + sizeof(*(vbl->ap_varbind))); + if (varbind == NULL) + goto fail; + vbl->ap_varbind = varbind; + nread = agentx_pdutovarbind(&header, + &(vbl->ap_varbind[vbl->ap_nvarbind]), u8, rawlen); + if (nread == -1) + goto fail; + vbl->ap_nvarbind++; + u8 += nread; + rawlen -= nread; + } + break; + case AGENTX_PDU_TYPE_COMMITSET: + case AGENTX_PDU_TYPE_UNDOSET: + case AGENTX_PDU_TYPE_CLEANUPSET: + if (rawlen != 0) { + errno = EPROTO; + goto fail; + } + break; + case AGENTX_PDU_TYPE_RESPONSE: + if (ax->ax_packetids != NULL) { + found = 0; + for (i = 0; ax->ax_packetids[i] != 0; i++) { + if (ax->ax_packetids[i] == + pdu->ap_header.aph_packetid) { + packetidx = i; + found = 1; + } + } + if (found) { + ax->ax_packetids[packetidx] = + ax->ax_packetids[i - 1]; + ax->ax_packetids[i - 1] = 0; + } else { + errno = EPROTO; + goto fail; + } + } + if (rawlen < 8) { + errno = EPROTO; + goto fail; + } + response = &(pdu->ap_payload.ap_response); + response->ap_uptime = agentx_pdutoh32(&header, u8); + u8 += 4; + response->ap_error = agentx_pdutoh16(&header, u8); + u8 += 2; + response->ap_index = agentx_pdutoh16(&header, u8); + u8 += 2; + rawlen -= 8; + while (rawlen > 0) { + varbind = recallocarray(response->ap_varbindlist, + response->ap_nvarbind, response->ap_nvarbind + 1, + sizeof(*(response->ap_varbindlist))); + if (varbind == NULL) + goto fail; + response->ap_varbindlist = varbind; + nread = agentx_pdutovarbind(&header, + &(response->ap_varbindlist[response->ap_nvarbind]), + u8, rawlen); + if (nread == -1) + goto fail; + response->ap_nvarbind++; + u8 += nread; + rawlen -= nread; + } + break; + default: + pdu->ap_payload.ap_raw = malloc(pdu->ap_header.aph_plength); + if (pdu == NULL) + goto fail; + memcpy(pdu->ap_payload.ap_raw, ax->ax_rbuf + AGENTX_PDU_HEADER, + pdu->ap_header.aph_plength); + break; + } - b->datalen -= len + padding; - b->ptr += len + padding; + ax->ax_rblen = 0; - return (0); + return pdu; +fail: + agentx_pdu_free(pdu); + return NULL; } -/* - * Send an AgentX PDU. Flushes any already-enqueued PDUs. - */ -int -snmp_agentx_send(struct agentx_handle *h, struct agentx_pdu *pdu) +static int +agentx_pdu_need(struct agentx *ax, size_t need) { - 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; + uint8_t *wbuf; + size_t wbsize; + + if (ax->ax_wbtlen >= ax->ax_wbsize) { + wbsize = ((ax->ax_wbtlen / 512) + 1) * 512; + wbuf = recallocarray(ax->ax_wbuf, ax->ax_wbsize, wbsize, 1); + if (wbuf == NULL) { + ax->ax_wbtlen = ax->ax_wblen; + return -1; } - - pdu->hdr->transactid = h->transactid; - pdu->hdr->packetid = h->packetid; - TAILQ_INSERT_TAIL(&h->w, pdu, entry); + ax->ax_wbsize = wbsize; + ax->ax_wbuf = wbuf; } - 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; + return 0; } -/* - * Attempt to read a single AgentX PDU. - */ -struct agentx_pdu * -snmp_agentx_recv(struct agentx_handle *h) +ssize_t +agentx_send(struct agentx *ax) { - struct agentx_pdu *pdu, *match; - ssize_t n; - - h->error = AGENTX_ERR_NONE; + ssize_t nwrite; - if (h->r == NULL) { - if ((h->r = snmp_agentx_pdu_alloc()) == NULL) - return (NULL); - h->r->datalen = 0; /* XXX force this for receive buffers */ + if (ax->ax_wblen != ax->ax_wbtlen) { + errno = EALREADY; + return -1; } - 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 (ax->ax_wblen == 0) + return 0; + +#if defined(AGENTX_DEBUG) && defined(AGENTX_DEBUG_VERBOSE) + { + size_t i; + char chars[4]; + int print = 1; + + fprintf(stderr, "sending packet:\n"); + for (i = 0; i < ax->ax_wblen; i++) { + fprintf(stderr, "%02hhx ", ax->ax_wbuf[i]); + chars[i % 4] = ax->ax_wbuf[i]; + if (!isprint(ax->ax_wbuf[i])) + print = 0; + if (i % 4 == 3) { + if (print) + fprintf(stderr, "%.4s", chars); + fprintf(stderr, "\n"); + print = 1; + } } - - if (snmp_agentx_buffercheck(pdu, pdu->hdr->length) == -1) - return (NULL); - } - - /* read body */ - if (pdu->hdr->length > 0) { - 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); - } - - if (pdu->hdr->version != AGENTX_VERSION) { - h->error = AGENTX_ERR_PARSE_ERROR; - goto fail; } +#endif - /* If this is an open on a new connection, fix it up */ - if (pdu->hdr->type == AGENTX_OPEN && h->sessionid == 0) { - pdu->hdr->sessionid = 0; /* ignored, per RFC */ - h->transactid = pdu->hdr->transactid; - h->packetid = pdu->hdr->packetid; - } + if ((nwrite = send(ax->ax_fd, ax->ax_wbuf, ax->ax_wblen, + MSG_NOSIGNAL | MSG_DONTWAIT)) == -1) + return -1; - if (pdu->hdr->type == AGENTX_RESPONSE) { + memmove(ax->ax_wbuf, ax->ax_wbuf + nwrite, ax->ax_wblen - nwrite); + ax->ax_wblen -= nwrite; + ax->ax_wbtlen = ax->ax_wblen; - match = agentx_find_inflight(h, pdu->hdr->transactid, - pdu->hdr->packetid); - if (match == NULL) { - errno = ESRCH; /* XXX */ - goto fail; - } + return ax->ax_wblen; +} - TAILQ_REMOVE(&h->inflight, match, entry); - pdu->request = match; - h->r = NULL; +uint32_t +agentx_open(struct agentx *ax, uint8_t timeout, struct agentx_oid *oid, + struct agentx_ostring *descr) +{ + if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_OPEN, 0, 0, 0, 0, + NULL) == -1) + return 0; + agentx_pdu_need(ax, 4); + ax->ax_wbuf[ax->ax_wbtlen++] = timeout; + memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); + ax->ax_wbtlen += 3; + if (agentx_pdu_add_oid(ax, oid, 0) == -1) + return 0; + if (agentx_pdu_add_str(ax, descr) == -1) + return 0; + + return agentx_pdu_queue(ax); +} - } else { - if (pdu->hdr->sessionid != h->sessionid) { - h->error = AGENTX_ERR_NOT_OPEN; - goto fail; - } +uint32_t +agentx_close(struct agentx *ax, uint32_t sessionid, + enum agentx_close_reason reason) +{ + if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_CLOSE, 0, sessionid, 0, 0, + NULL) == -1) + return 0; - if (pdu->hdr->flags & AGENTX_NON_DEFAULT_CONTEXT) { - h->error = AGENTX_ERR_UNSUPPORTED_CONTEXT; - goto fail; - } + if (agentx_pdu_need(ax, 4) == -1) + return 0; + ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t)reason; + memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 3); + ax->ax_wbtlen += 3; - snmp_agentx_update_ids(h, pdu); /* XXX */ + return agentx_pdu_queue(ax); +} - if (pdu->datalen != pdu->hdr->length + sizeof(*pdu->hdr)) { - h->error = AGENTX_ERR_PARSE_ERROR; - goto fail; - } +uint32_t +agentx_indexallocate(struct agentx *ax, uint8_t flags, uint32_t sessionid, + struct agentx_ostring *context, struct agentx_varbind *vblist, size_t nvb) +{ + if (flags & ~(AGENTX_PDU_FLAG_NEW_INDEX | AGENTX_PDU_FLAG_ANY_INDEX)) { + errno = EINVAL; + return 0; } -#ifdef DEBUG - snmp_agentx_dump_hdr(pdu->hdr); -#endif - h->r = NULL; - - return (pdu); + if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_INDEXALLOCATE, flags, + sessionid, 0, 0, context) == -1) + return 0; - fail: -#ifdef DEBUG - snmp_agentx_dump_hdr(pdu->hdr); -#endif - snmp_agentx_pdu_free(pdu); - h->r = NULL; + if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1) + return 0; - return (NULL); + return agentx_pdu_queue(ax); } -/* - * Synchonous request and receipt of response. - */ -struct agentx_pdu * -snmp_agentx_request(struct agentx_handle *h, struct agentx_pdu *pdu) +uint32_t +agentx_indexdeallocate(struct agentx *ax, uint32_t sessionid, + struct agentx_ostring *context, struct agentx_varbind *vblist, size_t nvb) { + if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_INDEXDEALLOCATE, 0, + sessionid, 0, 0, context) == -1) + return 0; - 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; + if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1) + return 0; - return (pdu); + return agentx_pdu_queue(ax); } -struct agentx_pdu * -agentx_find_inflight(struct agentx_handle *h, uint32_t tid, uint32_t pid) +uint32_t +agentx_addagentcaps(struct agentx *ax, uint32_t sessionid, + struct agentx_ostring *context, struct agentx_oid *id, + struct agentx_ostring *descr) { - struct agentx_pdu *pdu; - - TAILQ_FOREACH(pdu, &h->inflight, entry) - if (pdu->hdr->transactid == tid && pdu->hdr->packetid == pid) - break; - return (pdu); + if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_ADDAGENTCAPS, 0, + sessionid, 0, 0, context) == -1) + return 0; + if (agentx_pdu_add_oid(ax, id, 0) == -1) + return 0; + if (agentx_pdu_add_str(ax, descr) == -1) + return 0; + + return agentx_pdu_queue(ax); } -int -snmp_agentx_buffercheck(struct agentx_pdu *pdu, size_t len) +uint32_t +agentx_removeagentcaps(struct agentx *ax, uint32_t sessionid, + struct agentx_ostring *context, struct agentx_oid *id) { - 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 (agentx_pdu_header(ax, AGENTX_PDU_TYPE_REMOVEAGENTCAPS, 0, + sessionid, 0, 0, context) == -1) + return 0; + if (agentx_pdu_add_oid(ax, id, 0) == -1) + return 0; - if ((newptr = realloc(pdu->buffer, newlen)) == NULL) - return (-1); + return agentx_pdu_queue(ax); - 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) +uint32_t +agentx_register(struct agentx *ax, uint8_t flags, uint32_t sessionid, + struct agentx_ostring *context, uint8_t timeout, uint8_t priority, + uint8_t range_subid, struct agentx_oid *subtree, uint32_t upperbound) { - 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; + if (flags & ~(AGENTX_PDU_FLAG_INSTANCE_REGISTRATION)) { + errno = EINVAL; + return 0; } - 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; + if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_REGISTER, flags, + sessionid, 0, 0, context) == -1) + return 0; + + if (agentx_pdu_need(ax, 4) == -1) + return 0; + ax->ax_wbuf[ax->ax_wbtlen++] = timeout; + ax->ax_wbuf[ax->ax_wbtlen++] = priority; + ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + if (agentx_pdu_add_oid(ax, subtree, 0) == -1) + return 0; + if (range_subid != 0) { + if (agentx_pdu_add_uint32(ax, upperbound) == -1) + return 0; + } - return (pdu); - fail: - snmp_agentx_pdu_free(pdu); - return (NULL); + return agentx_pdu_queue(ax); } -struct agentx_pdu * -snmp_agentx_close_pdu(struct agentx_handle *h, uint8_t reason) +uint32_t +agentx_unregister(struct agentx *ax, uint32_t sessionid, + struct agentx_ostring *context, uint8_t priority, uint8_t range_subid, + struct agentx_oid *subtree, uint32_t upperbound) { - 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); + if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_UNREGISTER, 0, + sessionid, 0, 0, context) == -1) + return 0; + + if (agentx_pdu_need(ax, 4) == -1) + return 0; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + ax->ax_wbuf[ax->ax_wbtlen++] = priority; + ax->ax_wbuf[ax->ax_wbtlen++] = range_subid; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + if (agentx_pdu_add_oid(ax, subtree, 0) == -1) + return 0; + if (range_subid != 0) { + if (agentx_pdu_add_uint32(ax, upperbound) == -1) + return 0; } - return (pdu); + return agentx_pdu_queue(ax); } -struct agentx_pdu * -snmp_agentx_notify_pdu(struct snmp_oid *oid) +int +agentx_response(struct agentx *ax, uint32_t sessionid, uint32_t transactionid, + uint32_t packetid, struct agentx_ostring *context, uint32_t sysuptime, + uint16_t error, uint16_t index, struct agentx_varbind *vblist, size_t nvb) { - struct agentx_pdu *pdu; - - if ((pdu = snmp_agentx_pdu_alloc()) == NULL) - return (NULL); - pdu->hdr->type = AGENTX_NOTIFY; + if (agentx_pdu_header(ax, AGENTX_PDU_TYPE_RESPONSE, 0, sessionid, + transactionid, packetid, context) == -1) + return -1; + + if (agentx_pdu_add_uint32(ax, sysuptime) == -1 || + agentx_pdu_add_uint16(ax, error) == -1 || + agentx_pdu_add_uint16(ax, index) == -1) + return -1; + + if (agentx_pdu_add_varbindlist(ax, vblist, nvb) == -1) + return -1; + if (agentx_pdu_queue(ax) == 0) + return -1; + return 0; +} - if (snmp_agentx_varbind(pdu, &trapoid_0, - AGENTX_OBJECT_IDENTIFIER, oid, sizeof(*oid)) == -1) { - snmp_agentx_pdu_free(pdu); - return (NULL); +void +agentx_pdu_free(struct agentx_pdu *pdu) +{ + size_t i; + struct agentx_pdu_response *response; + + if (pdu->ap_header.aph_flags & AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) + free(pdu->ap_context.aos_string); + + switch (pdu->ap_header.aph_type) { + case AGENTX_PDU_TYPE_GET: + case AGENTX_PDU_TYPE_GETNEXT: + case AGENTX_PDU_TYPE_GETBULK: + free(pdu->ap_payload.ap_srl.ap_sr); + break; + case AGENTX_PDU_TYPE_RESPONSE: + response = &(pdu->ap_payload.ap_response); + for (i = 0; i < response->ap_nvarbind; i++) + agentx_varbind_free(&(response->ap_varbindlist[i])); + free(response->ap_varbindlist); + break; + default: + free(pdu->ap_payload.ap_raw); + break; } - - return (pdu); + free(pdu); } -struct agentx_pdu * -snmp_agentx_response_pdu(int uptime, int error, int idx) +void +agentx_varbind_free(struct agentx_varbind *varbind) { - 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); + switch (varbind->avb_type) { + case AGENTX_DATA_TYPE_OCTETSTRING: + case AGENTX_DATA_TYPE_IPADDRESS: + case AGENTX_DATA_TYPE_OPAQUE: + free(varbind->avb_data.avb_ostring.aos_string); + break; + default: + break; } - - return (pdu); } -struct agentx_pdu * -snmp_agentx_ping_pdu(void) +const char * +agentx_error2string(enum agentx_pdu_error error) { - 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); + static char buffer[64]; + switch (error) { + case AGENTX_PDU_ERROR_NOERROR: + return "No error"; + case AGENTX_PDU_ERROR_GENERR: + return "Generic error"; + case AGENTX_PDU_ERROR_NOACCESS: + return "No access"; + case AGENTX_PDU_ERROR_WRONGTYPE: + return "Wrong type"; + case AGENTX_PDU_ERROR_WRONGLENGTH: + return "Wrong length"; + case AGENTX_PDU_ERROR_WRONGENCODING: + return "Wrong encoding"; + case AGENTX_PDU_ERROR_WRONGVALUE: + return "Wrong value"; + case AGENTX_PDU_ERROR_NOCREATION: + return "No creation"; + case AGENTX_PDU_ERROR_INCONSISTENTVALUE: + return "Inconsistent value"; + case AGENTX_PDU_ERROR_RESOURCEUNAVAILABLE: + return "Resource unavailable"; + case AGENTX_PDU_ERROR_COMMITFAILED: + return "Commit failed"; + case AGENTX_PDU_ERROR_UNDOFAILED: + return "Undo failed"; + case AGENTX_PDU_ERROR_NOTWRITABLE: + return "Not writable"; + case AGENTX_PDU_ERROR_INCONSISTENTNAME: + return "Inconsistent name"; + case AGENTX_PDU_ERROR_OPENFAILED: + return "Open Failed"; + case AGENTX_PDU_ERROR_NOTOPEN: + return "Not open"; + case AGENTX_PDU_ERROR_INDEXWRONGTYPE: + return "Index wrong type"; + case AGENTX_PDU_ERROR_INDEXALREADYALLOCATED: + return "Index already allocated"; + case AGENTX_PDU_ERROR_INDEXNONEAVAILABLE: + return "Index none available"; + case AGENTX_PDU_ERROR_INDEXNOTALLOCATED: + return "Index not allocated"; + case AGENTX_PDU_ERROR_UNSUPPORTEDCONETXT: + return "Unsupported context"; + case AGENTX_PDU_ERROR_DUPLICATEREGISTRATION: + return "Duplicate registration"; + case AGENTX_PDU_ERROR_UNKNOWNREGISTRATION: + return "Unkown registration"; + case AGENTX_PDU_ERROR_UNKNOWNAGENTCAPS: + return "Unknown agent capabilities"; + case AGENTX_PDU_ERROR_PARSEERROR: + return "Parse error"; + case AGENTX_PDU_ERROR_REQUESTDENIED: + return "Request denied"; + case AGENTX_PDU_ERROR_PROCESSINGERROR: + return "Processing error"; + } + snprintf(buffer, sizeof(buffer), "Unknown error: %d", error); + return buffer; } -struct agentx_pdu * -snmp_agentx_register_pdu(struct snmp_oid *oid, int timeout, int range_index, - int range_bound) +const char * +agentx_pdutype2string(enum agentx_pdu_type type) { - 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); + static char buffer[64]; + switch(type) { + case AGENTX_PDU_TYPE_OPEN: + return "agentx-Open-PDU"; + case AGENTX_PDU_TYPE_CLOSE: + return "agentx-Close-PDU"; + case AGENTX_PDU_TYPE_REGISTER: + return "agentx-Register-PDU"; + case AGENTX_PDU_TYPE_UNREGISTER: + return "agentx-Unregister-PDU"; + case AGENTX_PDU_TYPE_GET: + return "agentx-Get-PDU"; + case AGENTX_PDU_TYPE_GETNEXT: + return "agentx-GetNext-PDU"; + case AGENTX_PDU_TYPE_GETBULK: + return "agentx-GetBulk-PDU"; + case AGENTX_PDU_TYPE_TESTSET: + return "agentx-TestSet-PDU"; + case AGENTX_PDU_TYPE_COMMITSET: + return "agentx-CommitSet-PDU"; + case AGENTX_PDU_TYPE_UNDOSET: + return "agentx-UndoSet-PDU"; + case AGENTX_PDU_TYPE_CLEANUPSET: + return "agentx-CleanupSet-PDU"; + case AGENTX_PDU_TYPE_NOTIFY: + return "agentx-Notify-PDU"; + case AGENTX_PDU_TYPE_PING: + return "agentx-Ping-PDU"; + case AGENTX_PDU_TYPE_INDEXALLOCATE: + return "agentx-IndexAllocate-PDU"; + case AGENTX_PDU_TYPE_INDEXDEALLOCATE: + return "agentx-IndexDeallocate-PDU"; + case AGENTX_PDU_TYPE_ADDAGENTCAPS: + return "agentx-AddAgentCaps-PDU"; + case AGENTX_PDU_TYPE_REMOVEAGENTCAPS: + return "agentx-RemoveAgentCaps-PDU"; + case AGENTX_PDU_TYPE_RESPONSE: + return "agentx-Response-PDU"; } - - return (pdu); + snprintf(buffer, sizeof(buffer), "Unknown type: %d", type); + return buffer; } -struct agentx_pdu * -snmp_agentx_unregister_pdu(struct snmp_oid *oid, int range_index, - int range_bound) +const char * +agentx_closereason2string(enum agentx_close_reason reason) { - 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 || - (range_index && snmp_agentx_int(pdu, &range_bound) == -1)) { - snmp_agentx_pdu_free(pdu); - return (NULL); + static char buffer[64]; + + switch (reason) { + case AGENTX_CLOSE_OTHER: + return "Undefined reason"; + case AGENTX_CLOSEN_PARSEERROR: + return "Too many AgentX parse errors from peer"; + case AGENTX_CLOSE_PROTOCOLERROR: + return "Too many AgentX protocol errors from peer"; + case AGENTX_CLOSE_TIMEOUTS: + return "Too many timeouts waiting for peer"; + case AGENTX_CLOSE_SHUTDOWN: + return "shutting down"; + case AGENTX_CLOSE_BYMANAGER: + return "Manager shuts down"; } - - return (pdu); + snprintf(buffer, sizeof(buffer), "Unknown reason: %d", reason); + return buffer; } -struct agentx_pdu * -snmp_agentx_get_pdu(struct snmp_oid oid[], int noid) +const char * +agentx_oid2string(struct agentx_oid *oid) { - 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)); + return agentx_oidrange2string(oid, 0, 0); +} - 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); +const char * +agentx_oidrange2string(struct agentx_oid *oid, uint8_t range_subid, + uint32_t upperbound) +{ + static char buf[1024]; + char *p; + size_t i, rest; + int ret; + + rest = sizeof(buf); + p = buf; + for (i = 0; i < oid->aoi_idlen; i++) { + if (range_subid != 0 && range_subid - 1 == (uint8_t)i) + ret = snprintf(p, rest, ".[%u-%u]", oid->aoi_id[i], + upperbound); + else + ret = snprintf(p, rest, ".%u", oid->aoi_id[i]); + if ((size_t) ret >= rest) { + snprintf(buf, sizeof(buf), "Couldn't parse oid"); + return buf; } + p += ret; + rest -= (size_t) ret; } - - return (pdu); + return buf; } -/* - * AgentX PDU write routines. - */ - -int -snmp_agentx_raw(struct agentx_pdu *pdu, void *data, int len) +const char * +agentx_varbind2string(struct agentx_varbind *vb) { - - 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); + static char buf[1024]; + char tmpbuf[1024]; + size_t i, bufleft; + int ishex = 0; + char *p; + int ret; + + switch (vb->avb_type) { + case AGENTX_DATA_TYPE_INTEGER: + (void) snprintf(buf, sizeof(buf), "%s: (int)%u", + agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); + break; + case AGENTX_DATA_TYPE_OCTETSTRING: + for (i = 0; + i < vb->avb_data.avb_ostring.aos_slen && !ishex; i++) { + if (!isprint(vb->avb_data.avb_ostring.aos_string[i])) + ishex = 1; + } + if (ishex) { + p = tmpbuf; + bufleft = sizeof(tmpbuf); + for (i = 0; + i < vb->avb_data.avb_ostring.aos_slen; i++) { + ret = snprintf(p, bufleft, " %02hhX", + vb->avb_data.avb_ostring.aos_string[i]); + if (ret >= (int) bufleft) { + p = strrchr(p, ' '); + strlcpy(p, "...", 4); + break; + } + p += 3; + bufleft -= 3; + } + ret = snprintf(buf, sizeof(buf), "%s: (hex-string)%s", + agentx_oid2string(&(vb->avb_oid)), tmpbuf); + if (ret >= (int) sizeof(buf)) { + p = strrchr(buf, ' '); + strlcpy(p, "...", 4); + } + } else { + ret = snprintf(buf, sizeof(buf), "%s: (string)", + agentx_oid2string(&(vb->avb_oid))); + if (ret >= (int) sizeof(buf)) { + snprintf(buf, sizeof(buf), "<too large OID>: " + "(string)<too large string>"); + break; + } + p = buf + ret; + bufleft = (int) sizeof(buf) - ret; + if (snprintf(p, bufleft, "%.*s", + vb->avb_data.avb_ostring.aos_slen, + vb->avb_data.avb_ostring.aos_string) >= + (int) bufleft) { + p = buf + sizeof(buf) - 4; + (void) strlcpy(p, "...", 4); + } + } + break; + case AGENTX_DATA_TYPE_NULL: + (void) snprintf(buf, sizeof(buf), "%s: <null>", + agentx_oid2string(&(vb->avb_oid))); + break; + case AGENTX_DATA_TYPE_OID: + (void) strlcpy(tmpbuf, + agentx_oid2string(&(vb->avb_data.avb_oid)), sizeof(tmpbuf)); + snprintf(buf, sizeof(buf), "%s: (oid)%s", + agentx_oid2string(&(vb->avb_oid)), tmpbuf); + break; + case AGENTX_DATA_TYPE_IPADDRESS: + if (vb->avb_data.avb_ostring.aos_slen != 4) { + snprintf(buf, sizeof(buf), "%s: (ipaddress)<invalid>", + agentx_oid2string(&(vb->avb_oid))); + break; + } + if (inet_ntop(PF_INET, vb->avb_data.avb_ostring.aos_string, + tmpbuf, sizeof(tmpbuf)) == NULL) { + snprintf(buf, sizeof(buf), "%s: (ipaddress)" + "<unparseable>: %s", + agentx_oid2string(&(vb->avb_oid)), + strerror(errno)); + break; + } + snprintf(buf, sizeof(buf), "%s: (ipaddress)%s", + agentx_oid2string(&(vb->avb_oid)), tmpbuf); + break; + case AGENTX_DATA_TYPE_COUNTER32: + (void) snprintf(buf, sizeof(buf), "%s: (counter32)%u", + agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); + break; + case AGENTX_DATA_TYPE_GAUGE32: + (void) snprintf(buf, sizeof(buf), "%s: (gauge32)%u", + agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); + break; + case AGENTX_DATA_TYPE_TIMETICKS: + (void) snprintf(buf, sizeof(buf), "%s: (timeticks)%u", + agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint32); + break; + case AGENTX_DATA_TYPE_OPAQUE: + p = tmpbuf; + bufleft = sizeof(tmpbuf); + for (i = 0; + i < vb->avb_data.avb_ostring.aos_slen; i++) { + ret = snprintf(p, bufleft, " %02hhX", + vb->avb_data.avb_ostring.aos_string[i]); + if (ret >= (int) bufleft) { + p = strrchr(p, ' '); + strlcpy(p, "...", 4); + break; + } + p += 3; + bufleft -= 3; + } + ret = snprintf(buf, sizeof(buf), "%s: (opaque)%s", + agentx_oid2string(&(vb->avb_oid)), tmpbuf); + if (ret >= (int) sizeof(buf)) { + p = strrchr(buf, ' '); + strlcpy(p, "...", 4); + } + break; + case AGENTX_DATA_TYPE_COUNTER64: + (void) snprintf(buf, sizeof(buf), "%s: (counter64)%"PRIu64, + agentx_oid2string(&(vb->avb_oid)), vb->avb_data.avb_uint64); + break; + case AGENTX_DATA_TYPE_NOSUCHOBJECT: + (void) snprintf(buf, sizeof(buf), "%s: <noSuchObject>", + agentx_oid2string(&(vb->avb_oid))); + break; + case AGENTX_DATA_TYPE_NOSUCHINSTANCE: + (void) snprintf(buf, sizeof(buf), "%s: <noSuchInstance>", + agentx_oid2string(&(vb->avb_oid))); + break; + case AGENTX_DATA_TYPE_ENDOFMIBVIEW: + (void) snprintf(buf, sizeof(buf), "%s: <endOfMibView>", + agentx_oid2string(&(vb->avb_oid))); + break; + } + return buf; } int -snmp_agentx_int(struct agentx_pdu *pdu, uint32_t *i) +agentx_oid_cmp(struct agentx_oid *o1, struct agentx_oid *o2) { - return (snmp_agentx_raw(pdu, i, sizeof(*i))); + size_t i, min; + + min = o1->aoi_idlen < o2->aoi_idlen ? o1->aoi_idlen : o2->aoi_idlen; + for (i = 0; i < min; i++) { + if (o1->aoi_id[i] < o2->aoi_id[i]) + return -1; + if (o1->aoi_id[i] > o2->aoi_id[i]) + return 1; + } + /* o1 is parent of o2 */ + if (o1->aoi_idlen < o2->aoi_idlen) + return -2; + /* o1 is child of o2 */ + if (o1->aoi_idlen > o2->aoi_idlen) + return 2; + return 0; } int -snmp_agentx_int64(struct agentx_pdu *pdu, uint64_t *i) +agentx_oid_add(struct agentx_oid *oid, uint32_t value) { - return (snmp_agentx_raw(pdu, i, sizeof(*i))); + if (oid->aoi_idlen == AGENTX_OID_MAX_LEN) + return -1; + oid->aoi_id[oid->aoi_idlen++] = value; + return 0; } -int -snmp_agentx_octetstring(struct agentx_pdu *pdu, char *str, int len) +static uint32_t +agentx_pdu_queue(struct agentx *ax) { - static uint8_t pad[4] = { 0, 0, 0, 0 }; - int padding; - uint32_t l; + struct agentx_pdu_header header; + uint32_t packetid, plength; + size_t wbtlen = ax->ax_wbtlen; - padding = ((len + 3) & ~0x03) - len; + header.aph_flags = ax->ax_byteorder == AGENTX_BYTE_ORDER_BE ? + AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER : 0; + packetid = agentx_pdutoh32(&header, &(ax->ax_wbuf[ax->ax_wblen + 12])); + plength = (ax->ax_wbtlen - ax->ax_wblen) - AGENTX_PDU_HEADER; + ax->ax_wbtlen = ax->ax_wblen + 16; + (void)agentx_pdu_add_uint32(ax, plength); - 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); + ax->ax_wblen = ax->ax_wbtlen = wbtlen; - return (0); + return packetid; } -int -snmp_agentx_oid(struct agentx_pdu *pdu, struct snmp_oid *oid) +static int +agentx_pdu_header(struct agentx *ax, enum agentx_pdu_type type, uint8_t flags, + uint32_t sessionid, uint32_t transactionid, uint32_t packetid, + struct agentx_ostring *context) { - 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 (ax->ax_wblen != ax->ax_wbtlen) { + errno = EALREADY; + return -1; } - 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); + ax->ax_wbtlen = ax->ax_wblen; + if (agentx_pdu_need(ax, 4) == -1) + return -1; + ax->ax_wbuf[ax->ax_wbtlen++] = 1; + ax->ax_wbuf[ax->ax_wbtlen++] = (uint8_t) type; + if (context != NULL) + flags |= AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT; + if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE) + flags |= AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER; + ax->ax_wbuf[ax->ax_wbtlen++] = flags; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; + if (packetid == 0) + packetid = agentx_packetid(ax); + if (agentx_pdu_add_uint32(ax, sessionid) == -1 || + agentx_pdu_add_uint32(ax, transactionid) == -1 || + agentx_pdu_add_uint32(ax, packetid) == -1 || + agentx_pdu_need(ax, 4) == -1) + return -1; + ax->ax_wbtlen += 4; + if (context != NULL) { + if (agentx_pdu_add_str(ax, context) == -1) + return -1; + } - return (0); + return 0; } -int -snmp_agentx_varbind(struct agentx_pdu *pdu, struct snmp_oid *oid, int type, - void *data, int len) +static uint32_t +agentx_packetid(struct agentx *ax) { - 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)); + uint32_t packetid, *packetids; + size_t npackets = 0, i; + int found; - 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); + if (ax->ax_packetids != NULL) { + for (npackets = 0; ax->ax_packetids[npackets] != 0; npackets++) + continue; } - /* NOTREACHED */ -} - -/* - * AgentX PDU read routines. - */ + if (ax->ax_packetidsize == 0 || npackets == ax->ax_packetidsize - 1) { + packetids = recallocarray(ax->ax_packetids, ax->ax_packetidsize, + ax->ax_packetidsize + 25, sizeof(*packetids)); + if (packetids == NULL) + return 0; + ax->ax_packetidsize += 25; + ax->ax_packetids = packetids; + } + do { + found = 0; + packetid = arc4random(); + for (i = 0; ax->ax_packetids[i] != 0; i++) { + if (ax->ax_packetids[i] == packetid) { + found = 1; + break; + } + } + } while (packetid == 0 || found); + ax->ax_packetids[npackets] = packetid; -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); + return packetid; } -int -snmp_agentx_copy_raw(struct agentx_pdu *pdu, void *v, int len) +static int +agentx_pdu_add_uint16(struct agentx *ax, uint16_t value) { - return (snmp_agentx_do_read_raw(pdu, v, len, 0)); -} + if (agentx_pdu_need(ax, sizeof(value)) == -1) + return -1; -int -snmp_agentx_read_raw(struct agentx_pdu *pdu, void *v, int len) -{ - return (snmp_agentx_do_read_raw(pdu, v, len, 1)); + if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE) + value = htobe16(value); + else + value = htole16(value); + memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); + ax->ax_wbtlen += sizeof(value); + return 0; } -int -snmp_agentx_do_read_raw(struct agentx_pdu *pdu, void *v, int len, int consume) +static int +agentx_pdu_add_uint32(struct agentx *ax, uint32_t value) { - void *ptr = pdu->ptr; - - if (consume) - if (snmp_agentx_buffer_consume(pdu, len) == -1) - return (-1); + if (agentx_pdu_need(ax, sizeof(value)) == -1) + return -1; - memcpy(v, ptr, len); - - return (0); + if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE) + value = htobe32(value); + else + value = htole32(value); + memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); + ax->ax_wbtlen += sizeof(value); + return 0; } -int -snmp_agentx_read_int(struct agentx_pdu *pdu, uint32_t *i) +static int +agentx_pdu_add_uint64(struct agentx *ax, uint64_t value) { - 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); -} + if (agentx_pdu_need(ax, sizeof(value)) == -1) + return -1; -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); + if (ax->ax_byteorder == AGENTX_BYTE_ORDER_BE) + value = htobe64(value); + else + value = htole64(value); + memcpy(ax->ax_wbuf + ax->ax_wbtlen, &value, sizeof(value)); + ax->ax_wbtlen += sizeof(value); + 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) +static int +agentx_pdu_add_oid(struct agentx *ax, struct agentx_oid *oid, int include) { - struct agentx_oid_hdr ohdr; - int i = 0; + static struct agentx_oid nulloid = {0}; + uint8_t prefix = 0, n_subid, i = 0; - if (snmp_agentx_read_raw(pdu, &ohdr, sizeof(ohdr)) == -1) - return (-1); + n_subid = oid->aoi_idlen; - bzero(oid, sizeof(*oid)); + if (oid == NULL) + oid = &nulloid; - 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; + if (oid->aoi_idlen > 4 && + oid->aoi_id[0] == 1 && oid->aoi_id[1] == 3 && + oid->aoi_id[2] == 6 && oid->aoi_id[3] == 1 && + oid->aoi_id[4] <= UINT8_MAX) { + prefix = oid->aoi_id[4]; i = 5; } - while (ohdr.n_subid--) - if (snmp_agentx_read_int(pdu, &oid->o_id[i++]) == -1) - return (-1); + if (agentx_pdu_need(ax, 4) == -1) + return -1; + ax->ax_wbuf[ax->ax_wbtlen++] = n_subid - i; + ax->ax_wbuf[ax->ax_wbtlen++] = prefix; + ax->ax_wbuf[ax->ax_wbtlen++] = !!include; + ax->ax_wbuf[ax->ax_wbtlen++] = 0; - oid->o_n = i; - *include = ohdr.include; + for (; i < n_subid; i++) { + if (agentx_pdu_add_uint32(ax, oid->aoi_id[i]) == -1) + return -1; + } - return (0); + return 0; } -int -snmp_agentx_read_searchrange(struct agentx_pdu *pdu, - struct agentx_search_range *sr) +static int +agentx_pdu_add_str(struct agentx *ax, struct agentx_ostring *str) { - if (snmp_agentx_do_read_oid(pdu, &sr->start, &sr->include) == -1 || - snmp_agentx_read_oid(pdu, &sr->end) == -1) - return (-1); - - return (0); + size_t length, zeroes; + + if (agentx_pdu_add_uint32(ax, str->aos_slen) == -1) + return -1; + + if ((zeroes = (4 - (str->aos_slen % 4))) == 4) + zeroes = 0; + length = str->aos_slen + zeroes; + if (agentx_pdu_need(ax, length) == -1) + return -1; + + memcpy(&(ax->ax_wbuf[ax->ax_wbtlen]), str->aos_string, str->aos_slen); + ax->ax_wbtlen += str->aos_slen; + memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, zeroes); + ax->ax_wbtlen += zeroes; + return 0; } -char * -snmp_agentx_read_octetstr(struct agentx_pdu *pdu, int *len) +static int +agentx_pdu_add_varbindlist(struct agentx *ax, + struct agentx_varbind *vblist, size_t nvb) { - 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); + size_t i; + uint16_t temp; + + for (i = 0; i < nvb; i++) { + temp = (uint16_t) vblist[i].avb_type; + if (agentx_pdu_add_uint16(ax, temp) == -1 || + agentx_pdu_need(ax, 2)) + return -1; + memset(&(ax->ax_wbuf[ax->ax_wbtlen]), 0, 2); + ax->ax_wbtlen += 2; + if (agentx_pdu_add_oid(ax, &(vblist[i].avb_oid), 0) == -1) + return -1; + switch (vblist[i].avb_type) { + case AGENTX_DATA_TYPE_INTEGER: + case AGENTX_DATA_TYPE_COUNTER32: + case AGENTX_DATA_TYPE_GAUGE32: + case AGENTX_DATA_TYPE_TIMETICKS: + if (agentx_pdu_add_uint32(ax, + vblist[i].avb_data.avb_uint32) == -1) + return -1; + break; + case AGENTX_DATA_TYPE_COUNTER64: + if (agentx_pdu_add_uint64(ax, + vblist[i].avb_data.avb_uint64) == -1) + return -1; + break; + case AGENTX_DATA_TYPE_OCTETSTRING: + case AGENTX_DATA_TYPE_IPADDRESS: + case AGENTX_DATA_TYPE_OPAQUE: + if (agentx_pdu_add_str(ax, + &(vblist[i].avb_data.avb_ostring)) == -1) + return -1; + break; + case AGENTX_DATA_TYPE_OID: + if (agentx_pdu_add_oid(ax, + &(vblist[i].avb_data.avb_oid), 1) == -1) + return -1; + break; + case AGENTX_DATA_TYPE_NULL: + case AGENTX_DATA_TYPE_NOSUCHOBJECT: + case AGENTX_DATA_TYPE_NOSUCHINSTANCE: + case AGENTX_DATA_TYPE_ENDOFMIBVIEW: + break; + default: + errno = EINVAL; + return -1; + } } - *len = l; - - return (str); + return 0; } -/* - * Synchronous AgentX calls. - */ - -int -snmp_agentx_ping(struct agentx_handle *h) +static uint16_t +agentx_pdutoh16(struct agentx_pdu_header *header, uint8_t *buf) { - struct agentx_pdu *pdu; - int error = 0; + uint16_t value; - 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); + memcpy(&value, buf, sizeof(value)); + if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER) + return be16toh(value); + return le16toh(value); } -/* - * Internal utility functions. - */ - -void -snmp_agentx_update_ids(struct agentx_handle *h, struct agentx_pdu *pdu) +static uint32_t +agentx_pdutoh32(struct agentx_pdu_header *header, uint8_t *buf) { - /* 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); + uint32_t value; - for (i = 0; i < o->o_n; i++) { - snprintf(str, sizeof(str), "%u", o->o_id[i]); - strlcat(buf, str, len); - if (i < (o->o_n - 1)) - strlcat(buf, ".", len); - } - - return (buf); + memcpy(&value, buf, sizeof(value)); + if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER) + return be32toh(value); + return le32toh(value); } -int -snmp_oid_cmp(struct snmp_oid *a, struct snmp_oid *b) +static uint64_t +agentx_pdutoh64(struct agentx_pdu_header *header, uint8_t *buf) { - 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; - } + uint64_t value; - /* b and a are identical */ - return (0); + memcpy(&value, buf, sizeof(value)); + if (header->aph_flags & AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER) + return be64toh(value); + return le64toh(value); } -void -snmp_oid_increment(struct snmp_oid *o) +static ssize_t +agentx_pdutooid(struct agentx_pdu_header *header, struct agentx_oid *oid, + uint8_t *buf, size_t rawlen) { - u_int i; + size_t i = 0; + ssize_t nread; - for (i = o->o_n; i > 0; i--) { - o->o_id[i - 1]++; - if (o->o_id[i - 1] != 0) - break; + if (rawlen < 4) + goto fail; + rawlen -= 4; + nread = 4; + oid->aoi_idlen = *buf++; + if (rawlen < (oid->aoi_idlen * 4)) + goto fail; + nread += oid->aoi_idlen * 4; + if (*buf != 0) { + oid->aoi_id[0] = 1; + oid->aoi_id[1] = 3; + oid->aoi_id[2] = 6; + oid->aoi_id[3] = 1; + oid->aoi_id[4] = *buf; + oid->aoi_idlen += 5; + i = 5; } + buf++; + oid->aoi_include = *buf; + for (buf += 2; i < oid->aoi_idlen; i++, buf += 4) + oid->aoi_id[i] = agentx_pdutoh32(header, buf); + + return nread; + +fail: + errno = EPROTO; + return -1; } -char * -snmp_agentx_type2name(int type) +static ssize_t +agentx_pdutoostring(struct agentx_pdu_header *header, + struct agentx_ostring *ostring, uint8_t *buf, size_t rawlen) { - 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]); + ssize_t nread; + + if (rawlen < 4) + goto fail; + + ostring->aos_slen = agentx_pdutoh32(header, buf); + rawlen -= 4; + buf += 4; + if (ostring->aos_slen > rawlen) + goto fail; + if ((ostring->aos_string = malloc(ostring->aos_slen + 1)) == NULL) + return -1; + memcpy(ostring->aos_string, buf, ostring->aos_slen); + ostring->aos_string[ostring->aos_slen] = '\0'; + + nread = 4 + ostring->aos_slen; + if (ostring->aos_slen % 4 != 0) + nread += 4 - (ostring->aos_slen % 4); + + return nread; + +fail: + errno = EPROTO; + return -1; } -#ifdef DEBUG -static void -snmp_agentx_dump_hdr(struct agentx_hdr *hdr) +static ssize_t +agentx_pdutovarbind(struct agentx_pdu_header *header, + struct agentx_varbind *varbind, uint8_t *buf, size_t rawlen) { - if (hdr == NULL) { - printf("NULL\n"); - return; - } + ssize_t nread, rread = 4; - 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 (rawlen == 0) + return 0; - 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); + if (rawlen < 8) + goto fail; + varbind->avb_type = agentx_pdutoh16(header, buf); + + buf += 4; + rawlen -= 4; + nread = agentx_pdutooid(header, &(varbind->avb_oid), buf, rawlen); + if (nread == -1) + return -1; + rread += nread; + buf += nread; + rawlen -= nread; + + switch(varbind->avb_type) { + case AGENTX_DATA_TYPE_INTEGER: + case AGENTX_DATA_TYPE_COUNTER32: + case AGENTX_DATA_TYPE_GAUGE32: + case AGENTX_DATA_TYPE_TIMETICKS: + if (rawlen < 4) + goto fail; + varbind->avb_data.avb_uint32 = agentx_pdutoh32(header, buf); + return rread + 4; + case AGENTX_DATA_TYPE_COUNTER64: + if (rawlen < 8) + goto fail; + varbind->avb_data.avb_uint64 = agentx_pdutoh64(header, buf); + return rread + 8; + case AGENTX_DATA_TYPE_OCTETSTRING: + case AGENTX_DATA_TYPE_IPADDRESS: + case AGENTX_DATA_TYPE_OPAQUE: + nread = agentx_pdutoostring(header, + &(varbind->avb_data.avb_ostring), buf, rawlen); + if (rread == -1) + return -1; + return nread + rread; + case AGENTX_DATA_TYPE_OID: + nread = agentx_pdutooid(header, &(varbind->avb_data.avb_oid), + buf, rawlen); + if (rread == -1) + return -1; + return nread + rread; + case AGENTX_DATA_TYPE_NULL: + case AGENTX_DATA_TYPE_NOSUCHOBJECT: + case AGENTX_DATA_TYPE_NOSUCHINSTANCE: + case AGENTX_DATA_TYPE_ENDOFMIBVIEW: + return rread; } - fprintf(stderr, "\n"); +fail: + errno = EPROTO; + return -1; } -#endif diff --git a/usr.sbin/relayd/agentx.h b/usr.sbin/relayd/agentx.h new file mode 100644 index 00000000000..8adde286a9b --- /dev/null +++ b/usr.sbin/relayd/agentx.h @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdint.h> + +#define AGENTX_PDU_FLAG_INSTANCE_REGISTRATION (1 << 0) +#define AGENTX_PDU_FLAG_NEW_INDEX (1 << 1) +#define AGENTX_PDU_FLAG_ANY_INDEX (1 << 2) +#define AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT (1 << 3) +#define AGENTX_PDU_FLAG_NETWORK_BYTE_ORDER (1 << 4) + +#define AGENTX_PRIORITY_DEFAULT 127 + +enum agentx_byte_order { + AGENTX_BYTE_ORDER_BE, + AGENTX_BYTE_ORDER_LE +}; + +#if BYTE_ORDER == BIG_ENDIAN +#define AGENTX_BYTE_ORDER_NATIVE AGENTX_BYTE_ORDER_BE +#else +#define AGENTX_BYTE_ORDER_NATIVE AGENTX_BYTE_ORDER_LE +#endif + +enum agentx_pdu_type { + AGENTX_PDU_TYPE_OPEN = 1, + AGENTX_PDU_TYPE_CLOSE = 2, + AGENTX_PDU_TYPE_REGISTER = 3, + AGENTX_PDU_TYPE_UNREGISTER = 4, + AGENTX_PDU_TYPE_GET = 5, + AGENTX_PDU_TYPE_GETNEXT = 6, + AGENTX_PDU_TYPE_GETBULK = 7, + AGENTX_PDU_TYPE_TESTSET = 8, + AGENTX_PDU_TYPE_COMMITSET = 9, + AGENTX_PDU_TYPE_UNDOSET = 10, + AGENTX_PDU_TYPE_CLEANUPSET = 11, + AGENTX_PDU_TYPE_NOTIFY = 12, + AGENTX_PDU_TYPE_PING = 13, + AGENTX_PDU_TYPE_INDEXALLOCATE = 14, + AGENTX_PDU_TYPE_INDEXDEALLOCATE = 15, + AGENTX_PDU_TYPE_ADDAGENTCAPS = 16, + AGENTX_PDU_TYPE_REMOVEAGENTCAPS = 17, + AGENTX_PDU_TYPE_RESPONSE = 18 +}; + +enum agentx_pdu_error { + AGENTX_PDU_ERROR_NOERROR = 0, + AGENTX_PDU_ERROR_GENERR = 5, + AGENTX_PDU_ERROR_NOACCESS = 6, + AGENTX_PDU_ERROR_WRONGTYPE = 7, + AGENTX_PDU_ERROR_WRONGLENGTH = 8, + AGENTX_PDU_ERROR_WRONGENCODING = 9, + AGENTX_PDU_ERROR_WRONGVALUE = 10, + AGENTX_PDU_ERROR_NOCREATION = 11, + AGENTX_PDU_ERROR_INCONSISTENTVALUE = 12, + AGENTX_PDU_ERROR_RESOURCEUNAVAILABLE = 13, + AGENTX_PDU_ERROR_COMMITFAILED = 14, + AGENTX_PDU_ERROR_UNDOFAILED = 15, + AGENTX_PDU_ERROR_NOTWRITABLE = 17, + AGENTX_PDU_ERROR_INCONSISTENTNAME = 18, + AGENTX_PDU_ERROR_OPENFAILED = 256, + AGENTX_PDU_ERROR_NOTOPEN = 257, + AGENTX_PDU_ERROR_INDEXWRONGTYPE = 258, + AGENTX_PDU_ERROR_INDEXALREADYALLOCATED = 259, + AGENTX_PDU_ERROR_INDEXNONEAVAILABLE = 260, + AGENTX_PDU_ERROR_INDEXNOTALLOCATED = 261, + AGENTX_PDU_ERROR_UNSUPPORTEDCONETXT = 262, + AGENTX_PDU_ERROR_DUPLICATEREGISTRATION = 263, + AGENTX_PDU_ERROR_UNKNOWNREGISTRATION = 264, + AGENTX_PDU_ERROR_UNKNOWNAGENTCAPS = 265, + AGENTX_PDU_ERROR_PARSEERROR = 266, + AGENTX_PDU_ERROR_REQUESTDENIED = 267, + AGENTX_PDU_ERROR_PROCESSINGERROR = 268 +}; + +enum agentx_data_type { + AGENTX_DATA_TYPE_INTEGER = 2, + AGENTX_DATA_TYPE_OCTETSTRING = 4, + AGENTX_DATA_TYPE_NULL = 5, + AGENTX_DATA_TYPE_OID = 6, + AGENTX_DATA_TYPE_IPADDRESS = 64, + AGENTX_DATA_TYPE_COUNTER32 = 65, + AGENTX_DATA_TYPE_GAUGE32 = 66, + AGENTX_DATA_TYPE_TIMETICKS = 67, + AGENTX_DATA_TYPE_OPAQUE = 68, + AGENTX_DATA_TYPE_COUNTER64 = 70, + AGENTX_DATA_TYPE_NOSUCHOBJECT = 128, + AGENTX_DATA_TYPE_NOSUCHINSTANCE = 129, + AGENTX_DATA_TYPE_ENDOFMIBVIEW = 130 +}; + +enum agentx_close_reason { + AGENTX_CLOSE_OTHER = 1, + AGENTX_CLOSEN_PARSEERROR = 2, + AGENTX_CLOSE_PROTOCOLERROR = 3, + AGENTX_CLOSE_TIMEOUTS = 4, + AGENTX_CLOSE_SHUTDOWN = 5, + AGENTX_CLOSE_BYMANAGER = 6 +}; + +struct agentx { + int ax_fd; + enum agentx_byte_order ax_byteorder; + uint8_t *ax_rbuf; + size_t ax_rblen; + size_t ax_rbsize; + uint8_t *ax_wbuf; + size_t ax_wblen; + size_t ax_wbtlen; + size_t ax_wbsize; + uint32_t *ax_packetids; + size_t ax_packetidsize; +}; + +#ifndef AGENTX_PRIMITIVE +#define AGENTX_PRIMITIVE + +#define AGENTX_OID_MAX_LEN 128 + +struct agentx_oid { + uint8_t aoi_include; + uint32_t aoi_id[AGENTX_OID_MAX_LEN]; + size_t aoi_idlen; +}; + +struct agentx_ostring { + unsigned char *aos_string; + uint32_t aos_slen; +}; +#endif + +struct agentx_searchrange { + struct agentx_oid asr_start; + struct agentx_oid asr_stop; +}; + +struct agentx_pdu_header { + uint8_t aph_version; + uint8_t aph_type; + uint8_t aph_flags; + uint8_t aph_reserved; + uint32_t aph_sessionid; + uint32_t aph_transactionid; + uint32_t aph_packetid; + uint32_t aph_plength; +} __packed; + +struct agentx_varbind { + enum agentx_data_type avb_type; + struct agentx_oid avb_oid; + union agentx_data { + uint32_t avb_uint32; + uint64_t avb_uint64; + struct agentx_ostring avb_ostring; + struct agentx_oid avb_oid; + } avb_data; +}; + +struct agentx_pdu { + struct agentx_pdu_header ap_header; + struct agentx_ostring ap_context; + union { + struct agentx_pdu_searchrangelist { + size_t ap_nsr; + struct agentx_searchrange *ap_sr; + } ap_srl; + struct agentx_pdu_getbulk { + uint16_t ap_nonrep; + uint16_t ap_maxrep; + struct agentx_pdu_searchrangelist ap_srl; + } ap_getbulk; + struct agentx_pdu_varbindlist { + struct agentx_varbind *ap_varbind; + size_t ap_nvarbind; + } ap_vbl; + struct agentx_pdu_response { + uint32_t ap_uptime; + enum agentx_pdu_error ap_error; + uint16_t ap_index; + struct agentx_varbind *ap_varbindlist; + size_t ap_nvarbind; + } ap_response; + void *ap_raw; + } ap_payload; +}; + +struct agentx *agentx_new(int); +void agentx_free(struct agentx *); +struct agentx_pdu *agentx_recv(struct agentx *); +ssize_t agentx_send(struct agentx *); +uint32_t agentx_open(struct agentx *, uint8_t, struct agentx_oid *, + struct agentx_ostring *); +uint32_t agentx_close(struct agentx *, uint32_t, enum agentx_close_reason); +uint32_t agentx_indexallocate(struct agentx *, uint8_t, uint32_t, + struct agentx_ostring *, struct agentx_varbind *, size_t); +uint32_t agentx_indexdeallocate(struct agentx *, uint32_t, + struct agentx_ostring *, struct agentx_varbind *, size_t); +uint32_t agentx_addagentcaps(struct agentx *, uint32_t, struct agentx_ostring *, + struct agentx_oid *, struct agentx_ostring *); +uint32_t agentx_removeagentcaps(struct agentx *, uint32_t, + struct agentx_ostring *, struct agentx_oid *); +uint32_t agentx_register(struct agentx *, uint8_t, uint32_t, + struct agentx_ostring *, uint8_t, uint8_t, uint8_t, struct agentx_oid *, + uint32_t); +uint32_t agentx_unregister(struct agentx *, uint32_t, struct agentx_ostring *, + uint8_t, uint8_t, struct agentx_oid *, uint32_t); +int agentx_response(struct agentx *, uint32_t, uint32_t, uint32_t, + struct agentx_ostring *, uint32_t, uint16_t, uint16_t, + struct agentx_varbind *, size_t); +void agentx_pdu_free(struct agentx_pdu *); +void agentx_varbind_free(struct agentx_varbind *); +const char *agentx_error2string(enum agentx_pdu_error); +const char *agentx_pdutype2string(enum agentx_pdu_type); +const char *agentx_oid2string(struct agentx_oid *); +const char *agentx_oidrange2string(struct agentx_oid *, uint8_t, uint32_t); +const char *agentx_varbind2string(struct agentx_varbind *); +const char *agentx_closereason2string(enum agentx_close_reason); +int agentx_oid_cmp(struct agentx_oid *, struct agentx_oid *); +int agentx_oid_add(struct agentx_oid *, uint32_t); diff --git a/usr.sbin/relayd/agentx_control.c b/usr.sbin/relayd/agentx_control.c new file mode 100644 index 00000000000..5d4887f1930 --- /dev/null +++ b/usr.sbin/relayd/agentx_control.c @@ -0,0 +1,1114 @@ +/* $OpenBSD: agentx_control.c,v 1.1 2020/09/14 11:30:25 martijn Exp $ */ + +/* + * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> + * Copyright (c) 2008 - 2014 Reyk Floeter <reyk@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/un.h> + +#include <netinet/in.h> + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <event.h> +#include <imsg.h> + +#include "relayd.h" +#include "subagentx.h" + +#define RELAYD_MIB "1.3.6.1.4.1.30155.3" +#define SNMP_ELEMENT(x...) do { \ + if (snmp_element(RELAYD_MIB x) == -1) \ + goto done; \ +} while (0) + +/* +static struct snmp_oid hosttrapoid = { + { 1, 3, 6, 1, 4, 1, 30155, 3, 1, 0 }, + 10 +}; +*/ + +#define RELAYDINFO SUBAGENTX_ENTERPRISES, 30155, 3, 2 +#define RELAYDREDIRECTS RELAYDINFO, 1 +#define RELAYDREDIRECTENTRY RELAYDREDIRECTS, 1 +#define RELAYDREDIRECTINDEX RELAYDREDIRECTENTRY, 1 +#define RELAYDREDIRECTSTATUS RELAYDREDIRECTENTRY, 2 +#define RELAYDREDIRECTNAME RELAYDREDIRECTENTRY, 3 +#define RELAYDREDIRECTCNT RELAYDREDIRECTENTRY, 4 +#define RELAYDREDIRECTAVG RELAYDREDIRECTENTRY, 5 +#define RELAYDREDIRECTLAST RELAYDREDIRECTENTRY, 6 +#define RELAYDREDIRECTAVGHOUR RELAYDREDIRECTENTRY, 7 +#define RELAYDREDIRECTLASTHOUR RELAYDREDIRECTENTRY, 8 +#define RELAYDREDIRECTAVGDAY RELAYDREDIRECTENTRY, 9 +#define RELAYDREDIRECTLASTDAY RELAYDREDIRECTENTRY, 10 +#define RELAYDRELAYS RELAYDINFO, 2 +#define RELAYDRELAYENTRY RELAYDRELAYS, 1 +#define RELAYDRELAYINDEX RELAYDRELAYENTRY, 1 +#define RELAYDRELAYSTATUS RELAYDRELAYENTRY, 2 +#define RELAYDRELAYNAME RELAYDRELAYENTRY, 3 +#define RELAYDRELAYCNT RELAYDRELAYENTRY, 4 +#define RELAYDRELAYAVG RELAYDRELAYENTRY, 5 +#define RELAYDRELAYLAST RELAYDRELAYENTRY, 6 +#define RELAYDRELAYAVGHOUR RELAYDRELAYENTRY, 7 +#define RELAYDRELAYLASTHOUR RELAYDRELAYENTRY, 8 +#define RELAYDRELAYAVGDAY RELAYDRELAYENTRY, 9 +#define RELAYDRELAYLASTDAY RELAYDRELAYENTRY, 10 +#define RELAYDROUTERS RELAYDINFO, 3 +#define RELAYDROUTERENTRY RELAYDROUTERS, 1 +#define RELAYDROUTERINDEX RELAYDROUTERENTRY, 1 +#define RELAYDROUTERTABLEINDEX RELAYDROUTERENTRY, 2 +#define RELAYDROUTERSTATUS RELAYDROUTERENTRY, 3 +#define RELAYDROUTERNAME RELAYDROUTERENTRY, 4 +#define RELAYDROUTERLABEL RELAYDROUTERENTRY, 5 +#define RELAYDROUTERRTABLE RELAYDROUTERENTRY, 6 +#define RELAYDNETROUTES RELAYDINFO, 4 +#define RELAYDNETROUTEENTRY RELAYDNETROUTES, 1 +#define RELAYDNETROUTEINDEX RELAYDNETROUTEENTRY, 1 +#define RELAYDNETROUTEADDR RELAYDNETROUTEENTRY, 2 +#define RELAYDNETROUTEADDRTYPE RELAYDNETROUTEENTRY, 3 +#define RELAYDNETROUTEPREFIXLEN RELAYDNETROUTEENTRY, 4 +#define RELAYDNETROUTEROUTERINDEX RELAYDNETROUTEENTRY, 5 +#define RELAYDHOSTS RELAYDINFO, 5 +#define RELAYDHOSTENTRY RELAYDHOSTS, 1 +#define RELAYDHOSTINDEX RELAYDHOSTENTRY, 1 +#define RELAYDHOSTPARENTINDEX RELAYDHOSTENTRY, 2 +#define RELAYDHOSTTABLEINDEX RELAYDHOSTENTRY, 3 +#define RELAYDHOSTNAME RELAYDHOSTENTRY, 4 +#define RELAYDHOSTADDRESS RELAYDHOSTENTRY, 5 +#define RELAYDHOSTADDRESSTYPE RELAYDHOSTENTRY, 6 +#define RELAYDHOSTSTATUS RELAYDHOSTENTRY, 7 +#define RELAYDHOSTCHECKCNT RELAYDHOSTENTRY, 8 +#define RELAYDHOSTUPCNT RELAYDHOSTENTRY, 9 +#define RELAYDHOSTERRNO RELAYDHOSTENTRY, 10 +#define RELAYDSESSIONS RELAYDINFO, 6 +#define RELAYDSESSIONENTRY RELAYDSESSIONS, 1 +#define RELAYDSESSIONINDEX RELAYDSESSIONENTRY, 1 +#define RELAYDSESSIONRELAYINDEX RELAYDSESSIONENTRY, 2 +#define RELAYDSESSIONINADDR RELAYDSESSIONENTRY, 3 +#define RELAYDSESSIONINADDRTYPE RELAYDSESSIONENTRY, 4 +#define RELAYDSESSIONOUTADDR RELAYDSESSIONENTRY, 5 +#define RELAYDSESSIONOUTADDRTYPE RELAYDSESSIONENTRY, 6 +#define RELAYDSESSIONPORTIN RELAYDSESSIONENTRY, 7 +#define RELAYDSESSIONPORTOUT RELAYDSESSIONENTRY, 8 +#define RELAYDSESSIONAGE RELAYDSESSIONENTRY, 9 +#define RELAYDSESSIONIDLE RELAYDSESSIONENTRY, 10 +#define RELAYDSESSIONSTATUS RELAYDSESSIONENTRY, 11 +#define RELAYDSESSIONPID RELAYDSESSIONENTRY, 12 +#define RELAYDTABLES RELAYDINFO, 7 +#define RELAYDTABLEENTRY RELAYDTABLES, 1 +#define RELAYDTABLEINDEX RELAYDTABLEENTRY, 1 +#define RELAYDTABLENAME RELAYDTABLEENTRY, 2 +#define RELAYDTABLESTATUS RELAYDTABLEENTRY, 3 + +void agentx_needsock(struct subagentx *, void *, int); + +struct relayd *env; + +struct subagentx *sa = NULL; +struct subagentx_index *relaydRedirectIdx, *relaydRelayIdx; +struct subagentx_index *relaydRouterIdx, *relaydNetRouteIdx; +struct subagentx_index *relaydHostIdx, *relaydSessionRelayIdx; +struct subagentx_index *relaydSessionIdx, *relaydTableIdx; + +struct subagentx_object *relaydRedirectIndex, *relaydRedirectStatus; +struct subagentx_object *relaydRedirectName, *relaydRedirectCnt; +struct subagentx_object *relaydRedirectAvg, *relaydRedirectLast; +struct subagentx_object *relaydRedirectAvgHour, *relaydRedirectLastHour; +struct subagentx_object *relaydRedirectAvgDay, *relaydRedirectLastDay; + +struct subagentx_object *relaydRelayIndex, *relaydRelayStatus; +struct subagentx_object *relaydRelayName, *relaydRelayCnt; +struct subagentx_object *relaydRelayAvg, *relaydRelayLast; +struct subagentx_object *relaydRelayAvgHour, *relaydRelayLastHour; +struct subagentx_object *relaydRelayAvgDay, *relaydRelayLastDay; + +struct subagentx_object *relaydRouterIndex, *relaydRouterTableIndex; +struct subagentx_object *relaydRouterStatus, *relaydRouterName; +struct subagentx_object *relaydRouterLabel, *relaydRouterRtable; + +struct subagentx_object *relaydNetRouteIndex, *relaydNetRouteAddr; +struct subagentx_object *relaydNetRouteAddrType, *relaydNetRoutePrefixLen; +struct subagentx_object *relaydNetRouteRouterIndex; + +struct subagentx_object *relaydHostIndex, *relaydHostParentIndex; +struct subagentx_object *relaydHostTableIndex, *relaydHostName; +struct subagentx_object *relaydHostAddress, *relaydHostAddressType; +struct subagentx_object *relaydHostStatus, *relaydHostCheckCnt; +struct subagentx_object *relaydHostUpCnt, *relaydHostErrno; + +struct subagentx_object *relaydSessionIndex, *relaydSessionRelayIndex; +struct subagentx_object *relaydSessionInAddr, *relaydSessionInAddrType; +struct subagentx_object *relaydSessionOutAddr, *relaydSessionOutAddrType; +struct subagentx_object *relaydSessionPortIn, *relaydSessionPortOut; +struct subagentx_object *relaydSessionAge, *relaydSessionIdle; +struct subagentx_object *relaydSessionStatus, *relaydSessionPid; + +struct subagentx_object *relaydTableIndex, *relaydTableName, *relaydTableStatus; + +void *sstodata(struct sockaddr_storage *); +size_t sstolen(struct sockaddr_storage *); + +struct rdr *agentx_rdr_byidx(uint32_t, enum subagentx_request_type); +void agentx_redirect(struct subagentx_varbind *); +struct relay *agentx_relay_byidx(uint32_t, enum subagentx_request_type); +void agentx_relay(struct subagentx_varbind *); +struct router *agentx_router_byidx(uint32_t, enum subagentx_request_type); +void agentx_router(struct subagentx_varbind *); +struct netroute *agentx_netroute_byidx(uint32_t, enum subagentx_request_type); +void agentx_netroute(struct subagentx_varbind *); +struct host *agentx_host_byidx(uint32_t, enum subagentx_request_type); +void agentx_host(struct subagentx_varbind *); +struct rsession *agentx_session_byidx(uint32_t, uint32_t, + enum subagentx_request_type); +void agentx_session(struct subagentx_varbind *); +struct table *agentx_table_byidx(uint32_t, enum subagentx_request_type); +void agentx_table(struct subagentx_varbind *); + +void agentx_sock(int, short, void *); +#if 0 +int snmp_element(const char *, enum snmp_type, void *, int64_t, + struct agentx_pdu *); +int snmp_string2oid(const char *, struct snmp_oid *); +#endif + +void +agentx_init(struct relayd *nenv) +{ + struct subagentx_session *sas; + struct subagentx_context *sac; + struct subagentx_region *sar; + struct subagentx_index *session_idxs[2]; + + subagentx_log_fatal = fatalx; + subagentx_log_warn = log_warnx; + subagentx_log_info = log_info; + subagentx_log_debug = log_debug; + + env = nenv; + + if ((env->sc_conf.flags & F_AGENTX) == 0) { + if (sa != NULL) + subagentx_free(sa); + return; + } + if (sa != NULL) + return; + + if ((sa = subagentx(agentx_needsock, NULL)) == NULL) + fatal("%s: agentx alloc", __func__); + if ((sas = subagentx_session(sa, NULL, 0, "relayd", 0)) == NULL) + fatal("%s: agentx session alloc", __func__); + if ((sac = subagentx_context(sas, + env->sc_conf.agentx_context[0] == '\0' ? NULL : + env->sc_conf.agentx_context)) == NULL) + fatal("%s: agentx context alloc", __func__); + sar = subagentx_region(sac, SUBAGENTX_OID(RELAYDINFO), 0); + if (sar == NULL) + fatal("%s: agentx region alloc", __func__); + if ((relaydRedirectIdx = subagentx_index_integer_dynamic(sar, + SUBAGENTX_OID(RELAYDREDIRECTINDEX))) == NULL || + (relaydRelayIdx = subagentx_index_integer_dynamic(sar, + SUBAGENTX_OID(RELAYDRELAYINDEX))) == NULL || + (relaydRouterIdx = subagentx_index_integer_dynamic(sar, + SUBAGENTX_OID(RELAYDROUTERINDEX))) == NULL || + (relaydNetRouteIdx = subagentx_index_integer_dynamic(sar, + SUBAGENTX_OID(RELAYDNETROUTEINDEX))) == NULL || + (relaydHostIdx = subagentx_index_integer_dynamic(sar, + SUBAGENTX_OID(RELAYDHOSTINDEX))) == NULL || + (relaydSessionIdx = subagentx_index_integer_dynamic(sar, + SUBAGENTX_OID(RELAYDSESSIONINDEX))) == NULL || + (relaydSessionRelayIdx = subagentx_index_integer_dynamic(sar, + SUBAGENTX_OID(RELAYDSESSIONRELAYINDEX))) == NULL || + (relaydTableIdx = subagentx_index_integer_dynamic(sar, + SUBAGENTX_OID(RELAYDTABLEINDEX))) == NULL) + fatal("%s: agentx index alloc", __func__); + session_idxs[0] = relaydSessionRelayIdx; + session_idxs[1] = relaydSessionIdx; + if ((relaydRedirectIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTINDEX), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectStatus = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTSTATUS), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectName = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTNAME), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectCnt = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTCNT), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectAvg = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTAVG), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectLast = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTLAST), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectAvgHour = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTAVGHOUR), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectLastHour = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTLASTHOUR), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectAvgDay = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTAVGDAY), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRedirectLastDay = subagentx_object(sar, + SUBAGENTX_OID(RELAYDREDIRECTLASTDAY), &relaydRedirectIdx, 1, 0, + agentx_redirect)) == NULL || + (relaydRelayIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYINDEX), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayStatus = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYSTATUS), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayName = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYNAME), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayCnt = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYCNT), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayAvg = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYAVG), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayLast = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYLAST), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayAvgHour = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYAVGHOUR), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayLastHour = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYLASTHOUR), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayAvgDay = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYAVGDAY), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRelayLastDay = subagentx_object(sar, + SUBAGENTX_OID(RELAYDRELAYLASTDAY), &relaydRelayIdx, 1, 0, + agentx_relay)) == NULL || + (relaydRouterIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDROUTERINDEX), &relaydRouterIdx, 1, 0, + agentx_router)) == NULL || + (relaydRouterTableIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDROUTERTABLEINDEX), &relaydRouterIdx, 1, 0, + agentx_router)) == NULL || + (relaydRouterStatus = subagentx_object(sar, + SUBAGENTX_OID(RELAYDROUTERSTATUS), &relaydRouterIdx, 1, 0, + agentx_router)) == NULL || + (relaydRouterName = subagentx_object(sar, + SUBAGENTX_OID(RELAYDROUTERNAME), &relaydRouterIdx, 1, 0, + agentx_router)) == NULL || + (relaydRouterLabel = subagentx_object(sar, + SUBAGENTX_OID(RELAYDROUTERLABEL), &relaydRouterIdx, 1, 0, + agentx_router)) == NULL || + (relaydRouterRtable = subagentx_object(sar, + SUBAGENTX_OID(RELAYDROUTERRTABLE), &relaydRouterIdx, 1, 0, + agentx_router)) == NULL || + (relaydNetRouteIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDNETROUTEINDEX), &relaydNetRouteIdx, 1, 0, + agentx_netroute)) == NULL || + (relaydNetRouteAddr = subagentx_object(sar, + SUBAGENTX_OID(RELAYDNETROUTEADDR), &relaydNetRouteIdx, 1, 0, + agentx_netroute)) == NULL || + (relaydNetRouteAddrType = subagentx_object(sar, + SUBAGENTX_OID(RELAYDNETROUTEADDRTYPE), &relaydNetRouteIdx, 1, 0, + agentx_netroute)) == NULL || + (relaydNetRoutePrefixLen = subagentx_object(sar, + SUBAGENTX_OID(RELAYDNETROUTEPREFIXLEN), &relaydNetRouteIdx, 1, 0, + agentx_netroute)) == NULL || + (relaydNetRouteRouterIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDNETROUTEROUTERINDEX), &relaydNetRouteIdx, 1, 0, + agentx_netroute)) == NULL || + (relaydHostIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTINDEX), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostParentIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTPARENTINDEX), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostTableIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTTABLEINDEX), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostName = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTNAME), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostAddress = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTADDRESS), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostAddressType = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTADDRESSTYPE), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostStatus = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTSTATUS), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostCheckCnt = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTCHECKCNT), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostUpCnt = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTUPCNT), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydHostErrno = subagentx_object(sar, + SUBAGENTX_OID(RELAYDHOSTERRNO), &relaydHostIdx, 1, 0, + agentx_host)) == NULL || + (relaydSessionIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONINDEX), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionRelayIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONRELAYINDEX), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionInAddr = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONINADDR), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionInAddrType = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONINADDRTYPE), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionOutAddr = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONOUTADDR), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionOutAddrType = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONOUTADDRTYPE), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionPortIn = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONPORTIN), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionPortOut = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONPORTOUT), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionAge = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONAGE), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionIdle = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONIDLE), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionStatus = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONSTATUS), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydSessionPid = subagentx_object(sar, + SUBAGENTX_OID(RELAYDSESSIONPID), session_idxs, 2, 0, + agentx_session)) == NULL || + (relaydTableIndex = subagentx_object(sar, + SUBAGENTX_OID(RELAYDTABLEINDEX), &relaydTableIdx, 1, 0, + agentx_table)) == NULL || + (relaydTableName = subagentx_object(sar, + SUBAGENTX_OID(RELAYDTABLENAME), &relaydTableIdx, 1, 0, + agentx_table)) == NULL || + (relaydTableStatus = subagentx_object(sar, + SUBAGENTX_OID(RELAYDTABLESTATUS), &relaydTableIdx, 1, 0, + agentx_table)) == NULL) + fatal("%s: agentx object alloc", __func__); +} + +void +agentx_needsock(struct subagentx *usa, void *cookie, int fd) +{ + proc_compose(env->sc_ps, PROC_PARENT, IMSG_AGENTXSOCK, NULL, 0); +} + +void +agentx_setsock(struct relayd *lenv, enum privsep_procid id) +{ + struct sockaddr_un sun; + int s = -1; + + if ((s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) + goto done; + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_UNIX; + if (strlcpy(sun.sun_path, lenv->sc_conf.agentx_path, + sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) + fatalx("invalid socket path"); + + if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) { + close(s); + s = -1; + } + done: + proc_compose_imsg(lenv->sc_ps, id, -1, IMSG_AGENTXSOCK, -1, s, NULL, 0); +} + +int +agentx_getsock(struct imsg *imsg) +{ + struct timeval tv = AGENTX_RECONNECT_TIMEOUT; + + if (imsg->fd == -1) + goto retry; + + event_del(&(env->sc_agentxev)); + event_set(&(env->sc_agentxev), imsg->fd, EV_READ | EV_PERSIST, + agentx_sock, env); + event_add(&(env->sc_agentxev), NULL); + + subagentx_connect(sa, imsg->fd); + + return 0; + retry: + evtimer_set(&env->sc_agentxev, agentx_sock, env); + evtimer_add(&env->sc_agentxev, &tv); + return 0; +} + +void +agentx_sock(int fd, short event, void *arg) +{ + if (event & EV_TIMEOUT) { + proc_compose(env->sc_ps, PROC_PARENT, IMSG_AGENTXSOCK, NULL, 0); + return; + } + if (event & EV_WRITE) { + event_del(&(env->sc_agentxev)); + event_set(&(env->sc_agentxev), fd, EV_READ | EV_PERSIST, + agentx_sock, NULL); + event_add(&(env->sc_agentxev), NULL); + subagentx_write(sa); + } + if (event & EV_READ) + subagentx_read(sa); + return; +} + +void * +sstodata(struct sockaddr_storage *ss) +{ + if (ss->ss_family == AF_INET) + return &((struct sockaddr_in *)ss)->sin_addr; + if (ss->ss_family == AF_INET6) + return &((struct sockaddr_in6 *)ss)->sin6_addr; + return NULL; +} + +size_t +sstolen(struct sockaddr_storage *ss) +{ + if (ss->ss_family == AF_INET) + return sizeof(((struct sockaddr_in *)ss)->sin_addr); + if (ss->ss_family == AF_INET6) + return sizeof(((struct sockaddr_in6 *)ss)->sin6_addr); + return 0; +} + +struct rdr * +agentx_rdr_byidx(uint32_t instanceidx, enum subagentx_request_type type) +{ + struct rdr *rdr; + + TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { + if (rdr->conf.id == instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET || + type == SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE) + return rdr; + else + return TAILQ_NEXT(rdr, entry); + } else if (rdr->conf.id > instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET) + return NULL; + return rdr; + } + } + + return NULL; +} + + +void +agentx_redirect(struct subagentx_varbind *sav) +{ + struct rdr *rdr; + + rdr = agentx_rdr_byidx(subagentx_varbind_get_index_integer(sav, + relaydRedirectIdx), subagentx_varbind_request(sav)); + if (rdr == NULL) { + subagentx_varbind_notfound(sav); + return; + } + subagentx_varbind_set_index_integer(sav, relaydRedirectIdx, + rdr->conf.id); + if (subagentx_varbind_get_object(sav) == relaydRedirectIndex) + subagentx_varbind_integer(sav, rdr->conf.id); + else if (subagentx_varbind_get_object(sav) == relaydRedirectStatus) { + if (rdr->conf.flags & F_DISABLE) + subagentx_varbind_integer(sav, 1); + else if (rdr->conf.flags & F_DOWN) + subagentx_varbind_integer(sav, 2); + else if (rdr->conf.flags & F_BACKUP) + subagentx_varbind_integer(sav, 3); + else + subagentx_varbind_integer(sav, 0); + } else if (subagentx_varbind_get_object(sav) == relaydRedirectName) + subagentx_varbind_string(sav, rdr->conf.name); + else if (subagentx_varbind_get_object(sav) == relaydRedirectCnt) + subagentx_varbind_counter64(sav, rdr->stats.cnt); + else if (subagentx_varbind_get_object(sav) == relaydRedirectAvg) + subagentx_varbind_gauge32(sav, rdr->stats.avg); + else if (subagentx_varbind_get_object(sav) == relaydRedirectLast) + subagentx_varbind_gauge32(sav, rdr->stats.last); + else if (subagentx_varbind_get_object(sav) == relaydRedirectAvgHour) + subagentx_varbind_gauge32(sav, rdr->stats.avg_hour); + else if (subagentx_varbind_get_object(sav) == relaydRedirectLastHour) + subagentx_varbind_gauge32(sav, rdr->stats.last_hour); + else if (subagentx_varbind_get_object(sav) == relaydRedirectAvgDay) + subagentx_varbind_gauge32(sav, rdr->stats.avg_day); + else if (subagentx_varbind_get_object(sav) == relaydRedirectLastDay) + subagentx_varbind_gauge32(sav, rdr->stats.last_day); +} + +struct relay * +agentx_relay_byidx(uint32_t instanceidx, enum subagentx_request_type type) +{ + struct relay *rly; + + TAILQ_FOREACH(rly, env->sc_relays, rl_entry) { + if (rly->rl_conf.id == instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET || + type == SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE) + return rly; + else + return TAILQ_NEXT(rly, rl_entry); + } else if (rly->rl_conf.id > instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET) + return NULL; + return rly; + } + } + + return NULL; +} + +void +agentx_relay(struct subagentx_varbind *sav) +{ + struct relay *rly; + uint64_t value = 0; + int i, nrelay = env->sc_conf.prefork_relay; + + rly = agentx_relay_byidx(subagentx_varbind_get_index_integer(sav, + relaydRelayIdx), subagentx_varbind_request(sav)); + if (rly == NULL) { + subagentx_varbind_notfound(sav); + return; + } + subagentx_varbind_set_index_integer(sav, relaydRelayIdx, + rly->rl_conf.id); + if (subagentx_varbind_get_object(sav) == relaydRelayIndex) + subagentx_varbind_integer(sav, rly->rl_conf.id); + else if (subagentx_varbind_get_object(sav) == relaydRelayStatus) { + if (rly->rl_up == HOST_UP) + subagentx_varbind_integer(sav, 1); + else + subagentx_varbind_integer(sav, 0); + } else if (subagentx_varbind_get_object(sav) == relaydRelayName) + subagentx_varbind_string(sav, rly->rl_conf.name); + else if (subagentx_varbind_get_object(sav) == relaydRelayCnt) { + for (i = 0; i < nrelay; i++) + value += rly->rl_stats[i].cnt; + subagentx_varbind_counter64(sav, value); + } else if (subagentx_varbind_get_object(sav) == relaydRelayAvg) { + for (i = 0; i < nrelay; i++) + value += rly->rl_stats[i].avg; + subagentx_varbind_gauge32(sav, (uint32_t)value); + } else if (subagentx_varbind_get_object(sav) == relaydRelayLast) { + for (i = 0; i < nrelay; i++) + value += rly->rl_stats[i].last; + subagentx_varbind_gauge32(sav, (uint32_t)value); + } else if (subagentx_varbind_get_object(sav) == relaydRelayAvgHour) { + for (i = 0; i < nrelay; i++) + value += rly->rl_stats[i].avg_hour; + subagentx_varbind_gauge32(sav, (uint32_t)value); + } else if (subagentx_varbind_get_object(sav) == relaydRelayLastHour) { + for (i = 0; i < nrelay; i++) + value += rly->rl_stats[i].last_hour; + subagentx_varbind_gauge32(sav, (uint32_t)value); + } else if (subagentx_varbind_get_object(sav) == relaydRelayAvgDay) { + for (i = 0; i < nrelay; i++) + value += rly->rl_stats[i].avg_day; + subagentx_varbind_gauge32(sav, (uint32_t)value); + } else if (subagentx_varbind_get_object(sav) == relaydRelayLastDay) { + for (i = 0; i < nrelay; i++) + value += rly->rl_stats[i].last_day; + subagentx_varbind_gauge32(sav, (uint32_t)value); + } +} + +struct router * +agentx_router_byidx(uint32_t instanceidx, enum subagentx_request_type type) +{ + struct router *router; + + TAILQ_FOREACH(router, env->sc_rts, rt_entry) { + if (router->rt_conf.id == instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET || + type == SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE) + return router; + else + return TAILQ_NEXT(router, rt_entry); + } else if (router->rt_conf.id > instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET) + return NULL; + return router; + } + } + + return NULL; +} + +void +agentx_router(struct subagentx_varbind *sav) +{ + struct router *router; + + router = agentx_router_byidx(subagentx_varbind_get_index_integer(sav, + relaydRouterIdx), subagentx_varbind_request(sav)); + if (router == NULL) { + subagentx_varbind_notfound(sav); + return; + } + subagentx_varbind_set_index_integer(sav, relaydRouterIdx, + router->rt_conf.id); + if (subagentx_varbind_get_object(sav) == relaydRouterIndex) + subagentx_varbind_integer(sav, router->rt_conf.id); + else if (subagentx_varbind_get_object(sav) == relaydRouterTableIndex) + subagentx_varbind_integer(sav, router->rt_conf.gwtable); + else if (subagentx_varbind_get_object(sav) == relaydRouterStatus) { + if (router->rt_conf.flags & F_DISABLE) + subagentx_varbind_integer(sav, 1); + else + subagentx_varbind_integer(sav, 0); + } else if (subagentx_varbind_get_object(sav) == relaydRouterName) + subagentx_varbind_string(sav, router->rt_conf.name); + else if (subagentx_varbind_get_object(sav) == relaydRouterLabel) + subagentx_varbind_string(sav, router->rt_conf.label); + else if (subagentx_varbind_get_object(sav) == relaydRouterRtable) + subagentx_varbind_integer(sav, router->rt_conf.rtable); +} + +struct netroute * +agentx_netroute_byidx(uint32_t instanceidx, enum subagentx_request_type type) +{ + struct netroute *nr; + + TAILQ_FOREACH(nr, env->sc_routes, nr_route) { + if (nr->nr_conf.id == instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET || + type == SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE) + return nr; + else + return TAILQ_NEXT(nr, nr_entry); + } else if (nr->nr_conf.id > instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET) + return NULL; + return nr; + } + } + + return NULL; +} + +void +agentx_netroute(struct subagentx_varbind *sav) +{ + struct netroute *nr; + + nr = agentx_netroute_byidx(subagentx_varbind_get_index_integer(sav, + relaydNetRouteIdx), subagentx_varbind_request(sav)); + if (nr == NULL) { + subagentx_varbind_notfound(sav); + return; + } + subagentx_varbind_set_index_integer(sav, relaydNetRouteIdx, + nr->nr_conf.id); + if (subagentx_varbind_get_object(sav) == relaydNetRouteIndex) + subagentx_varbind_integer(sav, nr->nr_conf.id); + else if (subagentx_varbind_get_object(sav) == relaydNetRouteAddr) + subagentx_varbind_nstring(sav, sstodata(&nr->nr_conf.ss), + sstolen(&nr->nr_conf.ss)); + else if (subagentx_varbind_get_object(sav) == relaydNetRouteAddrType) { + if (nr->nr_conf.ss.ss_family == AF_INET) + subagentx_varbind_integer(sav, 1); + else if (nr->nr_conf.ss.ss_family == AF_INET6) + subagentx_varbind_integer(sav, 2); + } else if (subagentx_varbind_get_object(sav) == relaydNetRoutePrefixLen) + subagentx_varbind_integer(sav, nr->nr_conf.prefixlen); + else if (subagentx_varbind_get_object(sav) == relaydNetRouteRouterIndex) + subagentx_varbind_integer(sav, nr->nr_conf.routerid); +} + +struct host * +agentx_host_byidx(uint32_t instanceidx, enum subagentx_request_type type) +{ + struct host *host; + + TAILQ_FOREACH(host, &(env->sc_hosts), globalentry) { + if (host->conf.id == instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET || + type == SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE) + return host; + else + return TAILQ_NEXT(host, globalentry); + } else if (host->conf.id > instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET) + return NULL; + return host; + } + } + + return NULL; +} + +void +agentx_host(struct subagentx_varbind *sav) +{ + struct host *host; + + host = agentx_host_byidx(subagentx_varbind_get_index_integer(sav, + relaydHostIdx), subagentx_varbind_request(sav)); + if (host == NULL) { + subagentx_varbind_notfound(sav); + return; + } + subagentx_varbind_set_index_integer(sav, relaydHostIdx, + host->conf.id); + if (subagentx_varbind_get_object(sav) == relaydHostIndex) + subagentx_varbind_integer(sav, host->conf.id); + else if (subagentx_varbind_get_object(sav) == relaydHostParentIndex) + subagentx_varbind_integer(sav, host->conf.parentid); + else if (subagentx_varbind_get_object(sav) == relaydHostTableIndex) + subagentx_varbind_integer(sav, host->conf.tableid); + else if (subagentx_varbind_get_object(sav) == relaydHostName) + subagentx_varbind_string(sav, host->conf.name); + else if (subagentx_varbind_get_object(sav) == relaydHostAddress) + subagentx_varbind_nstring(sav, sstodata(&host->conf.ss), + sstolen(&host->conf.ss)); + else if (subagentx_varbind_get_object(sav) == relaydHostAddressType) { + if (host->conf.ss.ss_family == AF_INET) + subagentx_varbind_integer(sav, 1); + else if (host->conf.ss.ss_family == AF_INET6) + subagentx_varbind_integer(sav, 2); + } else if (subagentx_varbind_get_object(sav) == relaydHostStatus) { + if (host->flags & F_DISABLE) + subagentx_varbind_integer(sav, 1); + else if (host->up == HOST_UP) + subagentx_varbind_integer(sav, 0); + else if (host->up == HOST_DOWN) + subagentx_varbind_integer(sav, 2); + else + subagentx_varbind_integer(sav, 3); + } else if (subagentx_varbind_get_object(sav) == relaydHostCheckCnt) + subagentx_varbind_counter64(sav, host->check_cnt); + else if (subagentx_varbind_get_object(sav) == relaydHostUpCnt) + subagentx_varbind_counter64(sav, host->up_cnt); + else if (subagentx_varbind_get_object(sav) == relaydHostErrno) + subagentx_varbind_integer(sav, host->he); +} + +/* + * Every session is spawned in one of multiple processes. + * However, there is no central session id registration, so not every session + * is shown here + */ +struct rsession * +agentx_session_byidx(uint32_t sessidx, uint32_t relayidx, + enum subagentx_request_type type) +{ + struct rsession *session; + + TAILQ_FOREACH(session, &(env->sc_sessions), se_entry) { + if (session->se_id == sessidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET) { + if (relayidx != session->se_relayid) + return NULL; + return session; + } + if (type == SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE) { + if (relayidx <= session->se_relayid) + return session; + return TAILQ_NEXT(session, se_entry); + } + if (relayidx < session->se_relayid) + return session; + return TAILQ_NEXT(session, se_entry); + } else if (session->se_id > sessidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET) + return NULL; + return session; + } + } + + return NULL; +} + +void +agentx_session(struct subagentx_varbind *sav) +{ + struct timeval tv, now; + struct rsession *session; + + session = agentx_session_byidx(subagentx_varbind_get_index_integer(sav, + relaydSessionIdx), subagentx_varbind_get_index_integer(sav, + relaydSessionRelayIdx), subagentx_varbind_request(sav)); + if (session == NULL) { + subagentx_varbind_notfound(sav); + return; + } + + subagentx_varbind_set_index_integer(sav, relaydSessionIdx, + session->se_id); + subagentx_varbind_set_index_integer(sav, relaydSessionRelayIdx, + session->se_relayid); + if (subagentx_varbind_get_object(sav) == relaydSessionIndex) + subagentx_varbind_integer(sav, session->se_id); + else if (subagentx_varbind_get_object(sav) == relaydSessionRelayIndex) + subagentx_varbind_integer(sav, session->se_relayid); + else if (subagentx_varbind_get_object(sav) == relaydSessionInAddr) + subagentx_varbind_nstring(sav, sstodata(&(session->se_in.ss)), + sstolen(&(session->se_in.ss))); + else if (subagentx_varbind_get_object(sav) == relaydSessionInAddrType) { + if (session->se_in.ss.ss_family == AF_INET) + subagentx_varbind_integer(sav, 1); + else if (session->se_in.ss.ss_family == AF_INET6) + subagentx_varbind_integer(sav, 2); + } else if (subagentx_varbind_get_object(sav) == relaydSessionOutAddr) + subagentx_varbind_nstring(sav, sstodata(&(session->se_out.ss)), + sstolen(&(session->se_out.ss))); + else if (subagentx_varbind_get_object(sav) == relaydSessionOutAddrType) { + if (session->se_out.ss.ss_family == AF_INET) + subagentx_varbind_integer(sav, 1); + else if (session->se_out.ss.ss_family == AF_INET6) + subagentx_varbind_integer(sav, 2); + else + subagentx_varbind_integer(sav, 0); + } else if (subagentx_varbind_get_object(sav) == relaydSessionPortIn) + subagentx_varbind_integer(sav, session->se_in.port); + else if (subagentx_varbind_get_object(sav) == relaydSessionPortOut) + subagentx_varbind_integer(sav, session->se_out.port); + else if (subagentx_varbind_get_object(sav) == relaydSessionAge) { + getmonotime(&now); + timersub(&now, &session->se_tv_start, &tv); + subagentx_varbind_timeticks(sav, + tv.tv_sec * 100 + tv.tv_usec / 10000); + } else if (subagentx_varbind_get_object(sav) == relaydSessionIdle) { + getmonotime(&now); + timersub(&now, &session->se_tv_last, &tv); + subagentx_varbind_timeticks(sav, + tv.tv_sec * 100 + tv.tv_usec / 10000); + } else if (subagentx_varbind_get_object(sav) == relaydSessionStatus) { + if (session->se_done) + subagentx_varbind_integer(sav, 1); + else + subagentx_varbind_integer(sav, 0); + } else if (subagentx_varbind_get_object(sav) == relaydSessionPid) + subagentx_varbind_integer(sav, session->se_pid); +} + +struct table * +agentx_table_byidx(uint32_t instanceidx, enum subagentx_request_type type) +{ + struct table *table; + + TAILQ_FOREACH(table, env->sc_tables, entry) { + if (table->conf.id == instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET || + type == SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE) + return table; + else + return TAILQ_NEXT(table, entry); + } else if (table->conf.id > instanceidx) { + if (type == SUBAGENTX_REQUEST_TYPE_GET) + return NULL; + return table; + } + } + + return NULL; +} + +void +agentx_table(struct subagentx_varbind *sav) +{ + struct table *table; + + table = agentx_table_byidx(subagentx_varbind_get_index_integer(sav, + relaydTableIdx), subagentx_varbind_request(sav)); + if (table == NULL) { + subagentx_varbind_notfound(sav); + return; + } + subagentx_varbind_set_index_integer(sav, relaydTableIdx, + table->conf.id); + if (subagentx_varbind_get_object(sav) == relaydTableIndex) + subagentx_varbind_integer(sav, table->conf.id); + else if (subagentx_varbind_get_object(sav) == relaydTableName) + subagentx_varbind_string(sav, table->conf.name); + else if (subagentx_varbind_get_object(sav) == relaydTableStatus) { + if (TAILQ_EMPTY(&table->hosts)) + subagentx_varbind_integer(sav, 1); + else if (table->conf.flags & F_DISABLE) + subagentx_varbind_integer(sav, 2); + else + subagentx_varbind_integer(sav, 0); + } + +} +#if 0 + +int +snmp_element(const char *oidstr, enum snmp_type type, void *buf, int64_t val, + struct agentx_pdu *pdu) +{ + u_int32_t d; + u_int64_t l; + struct snmp_oid oid; + + DPRINTF("%s: oid %s type %d buf %p val %lld", __func__, + oidstr, type, buf, val); + + if (snmp_string2oid(oidstr, &oid) == -1) + return -1; + + switch (type) { + case SNMP_GAUGE32: + case SNMP_NSAPADDR: + case SNMP_INTEGER32: + case SNMP_UINTEGER32: + d = (u_int32_t)val; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_INTEGER, + &d, sizeof(d)) == -1) + return -1; + break; + + case SNMP_COUNTER32: + d = (u_int32_t)val; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_COUNTER32, + &d, sizeof(d)) == -1) + return -1; + break; + + case SNMP_TIMETICKS: + d = (u_int32_t)val; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_TIME_TICKS, + &d, sizeof(d)) == -1) + return -1; + break; + + case SNMP_COUNTER64: + l = (u_int64_t)val; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_COUNTER64, + &l, sizeof(l)) == -1) + return -1; + break; + + case SNMP_IPADDR: + case SNMP_OPAQUE: + d = (u_int32_t)val; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OPAQUE, + buf, strlen(buf)) == -1) + return -1; + break; + + case SNMP_OBJECT: { + struct snmp_oid oid1; + + if (snmp_string2oid(buf, &oid1) == -1) + return -1; + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OBJECT_IDENTIFIER, + &oid1, sizeof(oid1)) == -1) + return -1; + } + + case SNMP_BITSTRING: + case SNMP_OCTETSTRING: + if (snmp_agentx_varbind(pdu, &oid, AGENTX_OCTET_STRING, + buf, strlen(buf)) == -1) + return -1; + break; + + case SNMP_NULL: + /* no data beyond the OID itself */ + if (snmp_agentx_varbind(pdu, &oid, AGENTX_NULL, + NULL, 0) == -1) + return -1; + } + + return 0; +} + +/* + * SNMP traps for relayd + */ + +void +snmp_hosttrap(struct relayd *env, struct table *table, struct host *host) +{ + struct agentx_pdu *pdu; + + if (snmp_agentx == NULL || env->sc_snmp == -1) + return; + + /* + * OPENBSD-RELAYD-MIB host status trap + * XXX The trap format needs some tweaks and other OIDs + */ + + if ((pdu = snmp_agentx_notify_pdu(&hosttrapoid)) == NULL) + return; + + SNMP_ELEMENT(".1.0", SNMP_NULL, NULL, 0, pdu); + SNMP_ELEMENT(".1.1.0", SNMP_OCTETSTRING, host->conf.name, 0, pdu); + SNMP_ELEMENT(".1.2.0", SNMP_INTEGER32, NULL, host->up, pdu); + SNMP_ELEMENT(".1.3.0", SNMP_INTEGER32, NULL, host->last_up, pdu); + SNMP_ELEMENT(".1.4.0", SNMP_INTEGER32, NULL, host->up_cnt, pdu); + SNMP_ELEMENT(".1.5.0", SNMP_INTEGER32, NULL, host->check_cnt, pdu); + SNMP_ELEMENT(".1.6.0", SNMP_OCTETSTRING, table->conf.name, 0, pdu); + SNMP_ELEMENT(".1.7.0", SNMP_INTEGER32, NULL, table->up, pdu); + if (!host->conf.retry) + goto done; + SNMP_ELEMENT(".1.8.0", SNMP_INTEGER32, NULL, host->conf.retry, pdu); + SNMP_ELEMENT(".1.9.0", SNMP_INTEGER32, NULL, host->retry_cnt, pdu); + + done: + snmp_agentx_send(snmp_agentx, pdu); + snmp_event_add(env, EV_WRITE); +} + +int +snmp_string2oid(const char *oidstr, struct snmp_oid *o) +{ + char *sp, *p, str[BUFSIZ]; + const char *errstr; + + if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) + return -1; + bzero(o, sizeof(*o)); + + for (p = sp = str; p != NULL; sp = p) { + if ((p = strpbrk(p, ".-")) != NULL) + *p++ = '\0'; + o->o_id[o->o_n++] = strtonum(sp, 0, UINT_MAX, &errstr); + if (errstr || o->o_n > SNMP_MAX_OID_LEN) + return -1; + } + + return 0; +} +#endif diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y index 114c5d7aec9..ce219245731 100644 --- a/usr.sbin/relayd/parse.y +++ b/usr.sbin/relayd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.245 2020/05/14 17:27:38 pvk Exp $ */ +/* $OpenBSD: parse.y,v 1.246 2020/09/14 11:30:25 martijn Exp $ */ /* * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org> @@ -56,7 +56,7 @@ #include "relayd.h" #include "http.h" -#include "snmp.h" +#include "subagentx.h" TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); static struct file { @@ -168,21 +168,21 @@ typedef struct { %} -%token APPEND BACKLOG BACKUP BINARY BUFFER CA CACHE SET CHECK CIPHERS CODE -%token COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT PASS BLOCK EXTERNAL FILENAME -%token FORWARD FROM HASH HEADER HEADERLEN HOST HTTP ICMP INCLUDE INET INET6 -%token INTERFACE INTERVAL IP KEYPAIR LABEL LISTEN VALUE LOADBALANCE LOG LOOKUP -%token METHOD MODE NAT NO DESTINATION NODELAY NOTHING ON PARENT PATH PFTAG PORT -%token PREFORK PRIORITY PROTO QUERYSTR REAL REDIRECT RELAY REMOVE REQUEST -%token RESPONSE RETRY QUICK RETURN ROUNDROBIN ROUTE SACK SCRIPT SEND SESSION -%token SNMP SOCKET SPLICE SSL STICKYADDR STYLE TABLE TAG TAGGED TCP TIMEOUT TLS -%token TO ROUTER RTLABEL TRANSPARENT TRAP URL WITH TTL RTABLE +%token AGENTX APPEND BACKLOG BACKUP BINARY BUFFER CA CACHE SET CHECK CIPHERS +%token CODE COOKIE DEMOTE DIGEST DISABLE ERROR EXPECT PASS BLOCK EXTERNAL +%token FILENAME FORWARD FROM HASH HEADER HEADERLEN HOST HTTP ICMP INCLUDE INET +%token INET6 INTERFACE INTERVAL IP KEYPAIR LABEL LISTEN VALUE LOADBALANCE LOG +%token LOOKUP METHOD MODE NAT NO DESTINATION NODELAY NOTHING ON PARENT PATH +%token PFTAG PORT PREFORK PRIORITY PROTO QUERYSTR REAL REDIRECT RELAY REMOVE +%token REQUEST RESPONSE RETRY QUICK RETURN ROUNDROBIN ROUTE SACK SCRIPT SEND +%token SESSION SNMP SOCKET SPLICE SSL STICKYADDR STYLE TABLE TAG TAGGED TCP +%token TIMEOUT TLS TO ROUTER RTLABEL TRANSPARENT TRAP URL WITH TTL RTABLE %token MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDHE -%token EDH TICKETS CONNECTION CONNECTIONS ERRORS STATE CHANGES CHECKS +%token EDH TICKETS CONNECTION CONNECTIONS CONTEXT ERRORS STATE CHANGES CHECKS %token WEBSOCKETS %token <v.string> STRING %token <v.number> NUMBER -%type <v.string> hostname interface table value optstring +%type <v.string> context hostname interface table value optstring path %type <v.number> http_type loglevel quick trap %type <v.number> dstmode flag forwardmode retry %type <v.number> opttls opttlsclient @@ -429,29 +429,62 @@ main : INTERVAL NUMBER { } conf->sc_conf.prefork_relay = $2; } + | AGENTX context path { + conf->sc_conf.flags |= F_AGENTX; + if ($2 != NULL) { + if (strlcpy(conf->sc_conf.agentx_context, $2, + sizeof(conf->sc_conf.agentx_context)) >= + sizeof(conf->sc_conf.agentx_context)) { + yyerror("agentx context too long"); + free($2); + free($3); + YYERROR; + } + free($2); + } else + conf->sc_conf.agentx_context[0] = '\0'; + if ($3 != NULL) { + if (strlcpy(conf->sc_conf.agentx_path, $3, + sizeof(conf->sc_conf.agentx_path)) >= + sizeof(conf->sc_conf.agentx_path)) { + yyerror("agentx path too long"); + free($3); + YYERROR; + } + free($3); + } else + (void)strlcpy(conf->sc_conf.agentx_path, + SUBAGENTX_AGENTX_MASTER, + sizeof(conf->sc_conf.agentx_path)); + } | SNMP trap optstring { - conf->sc_conf.flags |= F_SNMP; - if ($2) - conf->sc_conf.flags |= F_SNMP_TRAPONLY; + log_warnx("The snmp keyword is deprecated, please use agentx"); + conf->sc_conf.flags |= F_AGENTX; if ($3) { - if (strlcpy(conf->sc_conf.snmp_path, - $3, sizeof(conf->sc_conf.snmp_path)) >= - sizeof(conf->sc_conf.snmp_path)) { - yyerror("snmp path truncated"); + if (strlcpy(conf->sc_conf.agentx_path, + $3, sizeof(conf->sc_conf.agentx_path)) >= + sizeof(conf->sc_conf.agentx_path)) { + yyerror("agentx path truncated"); free($3); YYERROR; } free($3); } else - (void)strlcpy(conf->sc_conf.snmp_path, - AGENTX_SOCKET, - sizeof(conf->sc_conf.snmp_path)); + (void)strlcpy(conf->sc_conf.agentx_path, + "/var/run/agentx.sock", + sizeof(conf->sc_conf.agentx_path)); } | SOCKET STRING { conf->sc_ps->ps_csock.cs_name = $2; } ; +path : /* nothing */ { $$ = NULL; } + | PATH STRING { $$ = $2; } + +context : /* nothing */ { $$ = NULL; } + | CONTEXT STRING { $$ = $2; } + trap : /* nothing */ { $$ = 0; } | TRAP { $$ = 1; } @@ -2376,6 +2409,7 @@ lookup(char *s) { /* this has to be sorted always */ static const struct keywords keywords[] = { + { "agentx", AGENTX }, { "append", APPEND }, { "backlog", BACKLOG }, { "backup", BACKUP }, @@ -2391,6 +2425,7 @@ lookup(char *s) { "ciphers", CIPHERS }, { "code", CODE }, { "connection", CONNECTION }, + { "context", CONTEXT }, { "cookie", COOKIE }, { "demote", DEMOTE }, { "destination", DESTINATION }, diff --git a/usr.sbin/relayd/pfe.c b/usr.sbin/relayd/pfe.c index 38999e70cd0..3a97b749c4b 100644 --- a/usr.sbin/relayd/pfe.c +++ b/usr.sbin/relayd/pfe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfe.c,v 1.89 2017/05/28 10:39:15 benno Exp $ */ +/* $OpenBSD: pfe.c,v 1.90 2020/09/14 11:30:25 martijn Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -160,7 +160,10 @@ pfe_dispatch_hce(int fd, struct privsep_proc *p, struct imsg *imsg) log_debug("%s: state %d for host %u %s", __func__, st.up, host->conf.id, host->conf.name); +/* XXX Readd hosttrap code later */ +#if 0 snmp_hosttrap(env, table, host); +#endif /* * Do not change the table state when the host @@ -226,7 +229,7 @@ pfe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_CFG_DONE: config_getcfg(env, imsg); init_tables(env); - snmp_init(env, PROC_PARENT); + agentx_init(env); break; case IMSG_CTL_START: pfe_setup_events(); @@ -235,8 +238,8 @@ pfe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_CTL_RESET: config_getreset(env, imsg); break; - case IMSG_SNMPSOCK: - snmp_getsock(env, imsg); + case IMSG_AGENTXSOCK: + agentx_getsock(imsg); break; default: return (-1); diff --git a/usr.sbin/relayd/relayd.c b/usr.sbin/relayd/relayd.c index d431e70e42b..9390b2e4606 100644 --- a/usr.sbin/relayd/relayd.c +++ b/usr.sbin/relayd/relayd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.c,v 1.183 2020/08/19 14:23:26 mestre Exp $ */ +/* $OpenBSD: relayd.c,v 1.184 2020/09/14 11:30:25 martijn Exp $ */ /* * Copyright (c) 2007 - 2016 Reyk Floeter <reyk@openbsd.org> @@ -185,7 +185,6 @@ main(int argc, char *argv[]) TAILQ_INIT(&env->sc_hosts); TAILQ_INIT(&env->sc_sessions); env->sc_rtable = getrtable(); - env->sc_snmp = -1; /* initialize the TLS session id to a random key for all relay procs */ arc4random_buf(env->sc_conf.tls_sid, sizeof(env->sc_conf.tls_sid)); @@ -430,8 +429,8 @@ parent_dispatch_pfe(int fd, struct privsep_proc *p, struct imsg *imsg) case IMSG_CFG_DONE: parent_configure_done(env); break; - case IMSG_SNMPSOCK: - (void)snmp_setsock(env, p->p_id); + case IMSG_AGENTXSOCK: + (void)agentx_setsock(env, p->p_id); break; default: return (-1); diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5 index d78ac17118f..491fc216b71 100644 --- a/usr.sbin/relayd/relayd.conf.5 +++ b/usr.sbin/relayd/relayd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: relayd.conf.5,v 1.198 2020/07/01 06:47:18 martijn Exp $ +.\" $OpenBSD: relayd.conf.5,v 1.199 2020/09/14 11:30:25 martijn Exp $ .\" .\" Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org> .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 1 2020 $ +.Dd $Mdocdate: September 14 2020 $ .Dt RELAYD.CONF 5 .Os .Sh NAME @@ -118,6 +118,19 @@ table <webhosts> { .Sh GLOBAL CONFIGURATION Here are the settings that can be set globally: .Bl -tag -width Ds +.It Ic agentx Oo Ic context Ar context Oc Oo Ic path Ar path Oc +Export +.Xr relayd 8 +statistics via an agentx compatible +.Pq snmp +daemon by connecting to +.Ar path . +If +.Ar path +is omitted it will default to +.Pa /var/agentx/master . +.Ar Context +is the SNMPv3 context and can usually be omitted. .It Ic interval Ar number Set the interval in seconds at which the hosts will be checked. The default interval is 10 seconds. @@ -153,18 +166,6 @@ to a relay. .Xr relayd 8 runs 3 relay processes by default and every process will handle all configured relays. -.It Ic snmp Oo Ic trap Oc Op Qq Ar path -Send an SNMP trap when the state of a host changes. -.Xr relayd 8 -will try to connect to an AgentX compatible snmpd daemon -over the AgentX SNMP socket specified by -.Ar path -and request it send a trap to the registered trap receivers. -If -.Ar path -is not specified, a default path of -.Pa /var/run/agentx.sock -will be used. .It Ic socket Qo Ar path Qc Create a control socket at .Ar path . diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h index 393a13c55a5..354c4e7d01f 100644 --- a/usr.sbin/relayd/relayd.h +++ b/usr.sbin/relayd/relayd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: relayd.h,v 1.261 2020/05/14 17:27:39 pvk Exp $ */ +/* $OpenBSD: relayd.h,v 1.262 2020/09/14 11:30:25 martijn Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org> @@ -26,6 +26,7 @@ #include <sys/queue.h> #include <sys/tree.h> #include <sys/time.h> +#include <sys/un.h> #include <net/if.h> #include <net/pfvar.h> @@ -97,7 +98,7 @@ #define ICMP_BUF_SIZE 64 #define ICMP_RCVBUF_SIZE 262144 -#define SNMP_RECONNECT_TIMEOUT { 3, 0 } /* sec, usec */ +#define AGENTX_RECONNECT_TIMEOUT { 3, 0 } /* sec, usec */ #define PROC_PARENT_SOCK_FILENO 3 #define PROC_MAX_INSTANCES 32 @@ -389,7 +390,7 @@ union hashkey { #define F_DEMOTED 0x00008000 #define F_UDP 0x00010000 #define F_RETURN 0x00020000 -#define F_SNMP 0x00040000 +#define F_AGENTX 0x00040000 #define F_NEEDPF 0x00080000 #define F_PORT 0x00100000 #define F_TLSCLIENT 0x00200000 @@ -399,7 +400,7 @@ union hashkey { #define F_SCRIPT 0x02000000 #define F_TLSINSPECT 0x04000000 #define F_HASHKEY 0x08000000 -#define F_SNMP_TRAPONLY 0x10000000 +#define F_AGENTX_TRAPONLY 0x10000000 #define F_BITS \ "\10\01DISABLE\02BACKUP\03USED\04DOWN\05ADD\06DEL\07CHANGED" \ @@ -407,7 +408,7 @@ union hashkey { "\14TLS\15NAT_LOOKUP\16DEMOTE\17LOOKUP_PATH\20DEMOTED\21UDP" \ "\22RETURN\23TRAP\24NEEDPF\25PORT\26TLS_CLIENT\27NEEDRT" \ "\30MATCH\31DIVERT\32SCRIPT\33TLS_INSPECT\34HASHKEY" \ - "\35SNMP_TRAPONLY" + "\35AGENTX_TRAPONLY" enum forwardmode { FWD_NORMAL = 0, @@ -986,7 +987,7 @@ enum imsg_type { IMSG_DEMOTE, IMSG_STATISTICS, IMSG_SCRIPT, - IMSG_SNMPSOCK, + IMSG_AGENTXSOCK, IMSG_BINDANY, IMSG_RTMSG, /* from pfe to parent */ IMSG_CFG_TABLE, /* configuration from parent */ @@ -1073,7 +1074,8 @@ struct privsep_fd { struct relayd_config { char tls_sid[SSL_MAX_SID_CTX_LENGTH]; - char snmp_path[PATH_MAX]; + char agentx_path[sizeof(((struct sockaddr_un *)NULL)->sun_path)]; + char agentx_context[32]; struct timeval interval; struct timeval timeout; struct timeval statinterval; @@ -1121,9 +1123,7 @@ struct relayd { struct event sc_statev; - int sc_snmp; - struct event sc_snmpto; - struct event sc_snmpev; + struct event sc_agentxev; int sc_has_icmp; int sc_has_icmp6; @@ -1393,10 +1393,10 @@ const char *tag_id2name(u_int16_t); void tag_unref(u_int16_t); void tag_ref(u_int16_t); -/* snmp.c */ -void snmp_init(struct relayd *, enum privsep_procid); -void snmp_setsock(struct relayd *, enum privsep_procid); -int snmp_getsock(struct relayd *, struct imsg *); +/* agentx_control.c */ +void agentx_init(struct relayd *); +void agentx_setsock(struct relayd *, enum privsep_procid); +int agentx_getsock(struct imsg *); void snmp_hosttrap(struct relayd *, struct table *, struct host *); /* shuffle.c */ diff --git a/usr.sbin/relayd/snmp.c b/usr.sbin/relayd/snmp.c deleted file mode 100644 index ef9c757c81a..00000000000 --- a/usr.sbin/relayd/snmp.c +++ /dev/null @@ -1,1745 +0,0 @@ -/* $OpenBSD: snmp.c,v 1.30 2019/10/03 12:03:49 martijn Exp $ */ - -/* - * Copyright (c) 2008 - 2014 Reyk Floeter <reyk@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/queue.h> -#include <sys/time.h> -#include <sys/un.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 "relayd.h" -#include "snmp.h" - -#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 - -#define OIDIDX_relaydInfo 9 - -#define RELAYD_MIB "1.3.6.1.4.1.30155.3" -#define SNMP_ELEMENT(x...) do { \ - if (snmp_element(RELAYD_MIB x) == -1) \ - goto done; \ -} while (0) - -static struct snmp_oid hosttrapoid = { - { 1, 3, 6, 1, 4, 1, 30155, 3, 1, 0 }, - 10 -}; - -static struct snmp_oid relaydinfooid = { - { 1, 3, 6, 1, 4, 1, 30155, 3, 2 }, - 9 -}; - -static struct agentx_handle *snmp_agentx = NULL; -enum privsep_procid snmp_procid; - -void snmp_sock(int, short, void *); -int snmp_element(const char *, enum snmp_type, void *, int64_t, - struct agentx_pdu *); -int snmp_string2oid(const char *, struct snmp_oid *); -char *snmp_oid2string(struct snmp_oid *, char *, size_t); -void snmp_event_add(struct relayd *, int); -void snmp_agentx_process(struct agentx_handle *, struct agentx_pdu *, - void *); -int snmp_register(struct relayd *); -int snmp_unregister(struct relayd *); - -void *sstodata(struct sockaddr_storage *); -size_t sstolen(struct sockaddr_storage *); - -struct host * - snmp_host_byidx(struct relayd *, u_int *, u_int *, u_int, u_int); -struct table * - snmp_table_byidx(struct relayd *, u_int *, u_int *, u_int, u_int); -struct router * - snmp_router_byidx(struct relayd *, u_int *, u_int *, u_int, u_int); -struct relay * - snmp_relay_byidx(struct relayd *, u_int *, u_int *, u_int, u_int); -struct rsession * - snmp_session_byidx(struct relayd *, u_int *, u_int *, u_int, u_int); -struct rdr * - snmp_rdr_byidx(struct relayd *, u_int *, u_int *, u_int, u_int); -struct netroute * - snmp_netroute_byidx(struct relayd *, u_int *, u_int *, u_int, u_int); - -int snmp_redirect(struct relayd *, struct snmp_oid *, struct agentx_pdu *, - int, uint32_t, uint32_t, u_int); -int snmp_relay(struct relayd *, struct snmp_oid *, struct agentx_pdu *, - int, uint32_t, uint32_t, u_int); -int snmp_router(struct relayd *, struct snmp_oid *, struct agentx_pdu *, - int, uint32_t, uint32_t, u_int); -int snmp_netroute(struct relayd *, struct snmp_oid *, struct agentx_pdu *, - int, uint32_t, uint32_t, u_int); -int snmp_host(struct relayd *, struct snmp_oid *, struct agentx_pdu *, - int, uint32_t, uint32_t, u_int); -int snmp_session(struct relayd *, struct snmp_oid *, struct agentx_pdu *, - int, uint32_t, uint32_t, u_int); -int snmp_table(struct relayd *, struct snmp_oid *, struct agentx_pdu *, - int, uint32_t, uint32_t, u_int); - -void -snmp_init(struct relayd *env, enum privsep_procid id) -{ - if (event_initialized(&env->sc_snmpev)) - event_del(&env->sc_snmpev); - if (event_initialized(&env->sc_snmpto)) - event_del(&env->sc_snmpto); - if (env->sc_snmp != -1) { - if (snmp_agentx) { - snmp_unregister(env); - snmp_agentx_close(snmp_agentx, AGENTX_CLOSE_OTHER); - snmp_agentx = NULL; - } - close(env->sc_snmp); - env->sc_snmp = -1; - } - - if ((env->sc_conf.flags & F_SNMP) == 0) - return; - - snmp_procid = id; - - proc_compose(env->sc_ps, snmp_procid, IMSG_SNMPSOCK, NULL, 0); -} - -void -snmp_setsock(struct relayd *env, enum privsep_procid id) -{ - struct sockaddr_un sun; - int s = -1; - - if ((s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) - goto done; - - bzero(&sun, sizeof(sun)); - sun.sun_family = AF_UNIX; - if (strlcpy(sun.sun_path, env->sc_conf.snmp_path, - sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) - fatalx("invalid socket path"); - - if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) { - close(s); - s = -1; - } - done: - proc_compose_imsg(env->sc_ps, id, -1, IMSG_SNMPSOCK, -1, s, NULL, 0); -} - -int -snmp_getsock(struct relayd *env, struct imsg *imsg) -{ - struct timeval tv = SNMP_RECONNECT_TIMEOUT; - struct agentx_pdu *pdu; - - if (imsg->fd == -1) - goto retry; - - env->sc_snmp = imsg->fd; - - log_debug("%s: got new snmp socket %d", __func__, imsg->fd); - - if ((snmp_agentx = snmp_agentx_alloc(env->sc_snmp)) == NULL) - fatal("%s: agentx alloc", __func__); - if ((pdu = snmp_agentx_open_pdu(snmp_agentx, "relayd", NULL)) == NULL) - fatal("%s: agentx pdu", __func__); - (void)snmp_agentx_send(snmp_agentx, pdu); - - snmp_event_add(env, EV_WRITE); - - return (0); - retry: - evtimer_set(&env->sc_snmpto, snmp_sock, env); - evtimer_add(&env->sc_snmpto, &tv); - return (0); -} - -void -snmp_event_add(struct relayd *env, int wflag) -{ - event_del(&env->sc_snmpev); - event_set(&env->sc_snmpev, env->sc_snmp, EV_READ|wflag, snmp_sock, env); - event_add(&env->sc_snmpev, NULL); -} - -void -snmp_sock(int fd, short event, void *arg) -{ - struct relayd *env = arg; - struct agentx_pdu *pdu; - int evflags = 0; - - if (event & EV_TIMEOUT) { - goto reopen; - } - if (event & EV_WRITE) { - if (snmp_agentx_send(snmp_agentx, NULL) == -1) { - if (errno != EAGAIN) - goto close; - - /* short write */ - evflags |= EV_WRITE; - } - } - if (event & EV_READ) { - if ((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) { - log_warn("agentx socket error"); - goto close; - } - - /* short read */ - goto out; - } - - snmp_agentx_process(snmp_agentx, pdu, env); - } -out: - snmp_event_add(env, evflags); - return; - - close: - log_debug("%s: snmp socket closed %d", __func__, env->sc_snmp); - snmp_agentx_free(snmp_agentx); - env->sc_snmp = -1; - snmp_agentx = NULL; - reopen: - proc_compose(env->sc_ps, snmp_procid, IMSG_SNMPSOCK, NULL, 0); - return; -} - -void -snmp_agentx_process(struct agentx_handle *h, struct agentx_pdu *pdu, void *arg) -{ - struct agentx_getbulk_repeaters repeaters; - struct agentx_search_range sr; - struct snmp_oid oid; - struct agentx_close_request_data close_hdr; - struct relayd *env = arg; - struct agentx_pdu *resp; - u_int erridx = 0; - int getnext = 0, repetitions; - int nonrepeaters, maxrepetitions; - - 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); - env->sc_snmp = -1; - snmp_agentx = NULL; - proc_compose(env->sc_ps, snmp_procid, IMSG_SNMPSOCK, NULL, 0); - 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_relaydInfo + 1) - infoendidx = sr.end.o_id[OIDIDX_relaydInfo]; - else - infoendidx = UINT32_MAX; - if (sr.end.o_n >= OIDIDX_relaydInfo + 1 + 1) - infoentryendidx = - sr.end.o_id[OIDIDX_relaydInfo + 1]; - else - infoentryendidx = UINT32_MAX; - if (sr.end.o_n >= OIDIDX_relaydInfo + 2 + 1) - infoentryidxendidx = - sr.end.o_id[OIDIDX_relaydInfo + 2]; - else - infoentryidxendidx = UINT32_MAX; - - bcopy(&sr.start, &oid, sizeof(oid)); - - if (snmp_oid_cmp(&relaydinfooid, &oid) == -1) { - /* - * If the requested OID is not part of the registered - * MIB, return "no such object", per RFC - */ - if (pdu->hdr->type == AGENTX_GET) { - 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; - } - bcopy(&relaydinfooid, &oid, sizeof(oid)); - } - if (oid.o_n == 9) { - oid.o_id[9] = 1; - oid.o_n++; - } - if (oid.o_n != OIDIDX_relaydInfo + 2 + 1) { - /* GET requests require the exact OID */ - if (pdu->hdr->type == AGENTX_GET) - goto nosuchinstance; - - if (oid.o_n == OIDIDX_relaydInfo + 1) { - oid.o_id[OIDIDX_relaydInfo + 1] = 0; - oid.o_n = OIDIDX_relaydInfo + 1 + 1; - } - if (oid.o_n == OIDIDX_relaydInfo + 1 + 1) { - oid.o_id[OIDIDX_relaydInfo + 2] = 0; - oid.o_n = OIDIDX_relaydInfo + 2 + 1; - } - if (oid.o_n > OIDIDX_relaydInfo + 2 + 1) - oid.o_n = OIDIDX_relaydInfo + 2 + 1; - } - - /* - * 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_relaydInfo]) { - case 1: - log_warnx("%s: redirects", __func__); - if (infoendidx < 1) - break; - if (snmp_redirect(env, &oid, resp, getnext, - infoentryendidx, infoentryidxendidx, - sr.include) == 0) - break; - if (!getnext) - goto nosuchinstance; - - oid.o_id[OIDIDX_relaydInfo] = 2; - oid.o_id[OIDIDX_relaydInfo + 1] = 0; - oid.o_id[OIDIDX_relaydInfo + 2] = 0; - /* FALLTHROUGH */ - case 2: - log_warnx("%s: relays", __func__); - if (infoendidx < 2) - break; - if (snmp_relay(env, &oid, resp, getnext, - infoentryendidx, infoentryidxendidx, - sr.include) == 0) - break; - if (!getnext) - goto nosuchinstance; - - oid.o_id[OIDIDX_relaydInfo] = 3; - oid.o_id[OIDIDX_relaydInfo + 1] = 0; - oid.o_id[OIDIDX_relaydInfo + 2] = 0; - /* FALLTHROUGH */ - case 3: - log_warnx("%s: routers", __func__); - if (infoendidx < 3) - break; - if (snmp_router(env, &oid, resp, getnext, - infoentryendidx, infoentryidxendidx, - sr.include) == 0) - break; - if (!getnext) - goto nosuchinstance; - - oid.o_id[OIDIDX_relaydInfo] = 4; - oid.o_id[OIDIDX_relaydInfo + 1] = 0; - oid.o_id[OIDIDX_relaydInfo + 2] = 0; - /* FALLTHROUGH */ - case 4: - log_warnx("%s: relaydNetRoutes", __func__); - if (infoendidx < 4) - break; - if (snmp_netroute(env, &oid, resp, getnext, - infoentryendidx, infoentryidxendidx, - sr.include) == 0) - break; - if (!getnext) - goto nosuchinstance; - - oid.o_id[OIDIDX_relaydInfo] = 5; - oid.o_id[OIDIDX_relaydInfo + 1] = 0; - oid.o_id[OIDIDX_relaydInfo + 2] = 0; - /* FALLTHROUGH */ - case 5: - log_warnx("%s: hosts", __func__); - if (infoendidx < 5) - break; - if (snmp_host(env, &oid, resp, getnext, - infoentryendidx, infoentryidxendidx, - sr.include) == 0) - break; - if (!getnext) - goto nosuchinstance; - - oid.o_id[OIDIDX_relaydInfo] = 6; - oid.o_id[OIDIDX_relaydInfo + 1] = 0; - oid.o_id[OIDIDX_relaydInfo + 2] = 0; - /* FALLTHROUGH */ - case 6: - log_warnx("%s: sessions", __func__); - if (infoendidx < 6) - break; - if (snmp_session(env, &oid, resp, getnext, - infoentryendidx, infoentryidxendidx, - sr.include) == 0) - break; - if (!getnext) - goto nosuchinstance; - - oid.o_id[OIDIDX_relaydInfo] = 7; - oid.o_id[OIDIDX_relaydInfo + 1] = 0; - oid.o_id[OIDIDX_relaydInfo + 2] = 0; - /* FALLTHROUGH */ - case 7: - log_warnx("%s: tables", __func__); - if (infoendidx < 7) - break; - if (snmp_table(env, &oid, resp, getnext, - infoentryendidx, infoentryidxendidx, - sr.include) == 0) - break; - if (!getnext) - goto nosuchinstance; - - if (snmp_agentx_varbind(resp, &oid, - AGENTX_END_OF_MIB_VIEW, NULL, 0) == -1) { - log_warn("%s: unable to generate" - " response", __func__); - snmp_agentx_pdu_free(resp); - resp = NULL; - } - goto reply; - default: - nosuchinstance: - log_warnx("unknown index %i", - oid.o_id[OIDIDX_relaydInfo]); - 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); - snmp_event_add(env, EV_WRITE); - } - - 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; - /* Open AgentX socket; register MIB if not trap-only */ - if (!(env->sc_conf.flags & F_SNMP_TRAPONLY)) - if (snmp_register(env) == -1) { - log_warn("failed to register MIB"); - break; - } - 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; -} - -int -snmp_register(struct relayd *env) -{ - struct agentx_pdu *pdu; - - if ((pdu = snmp_agentx_register_pdu(&relaydinfooid, 3, 0, 0)) == NULL) - return (-1); - - if (snmp_agentx_send(snmp_agentx, pdu) == -1) - return (-1); - - snmp_event_add(env, EV_WRITE); - return (0); -} - -int -snmp_unregister(struct relayd *env) -{ - struct agentx_pdu *pdu; - - if ((pdu = snmp_agentx_unregister_pdu(&relaydinfooid, 0, 0)) == NULL) - return (-1); - - if (snmp_agentx_send(snmp_agentx, pdu) == -1) - return (-1); - - snmp_event_add(env, EV_WRITE); - return (0); -} - -int -snmp_element(const char *oidstr, enum snmp_type type, void *buf, int64_t val, - struct agentx_pdu *pdu) -{ - u_int32_t d; - u_int64_t l; - struct snmp_oid oid; - - DPRINTF("%s: oid %s type %d buf %p val %lld", __func__, - oidstr, type, buf, val); - - if (snmp_string2oid(oidstr, &oid) == -1) - return (-1); - - switch (type) { - case SNMP_GAUGE32: - case SNMP_NSAPADDR: - case SNMP_INTEGER32: - case SNMP_UINTEGER32: - d = (u_int32_t)val; - if (snmp_agentx_varbind(pdu, &oid, AGENTX_INTEGER, - &d, sizeof(d)) == -1) - return (-1); - break; - - case SNMP_COUNTER32: - d = (u_int32_t)val; - if (snmp_agentx_varbind(pdu, &oid, AGENTX_COUNTER32, - &d, sizeof(d)) == -1) - return (-1); - break; - - case SNMP_TIMETICKS: - d = (u_int32_t)val; - if (snmp_agentx_varbind(pdu, &oid, AGENTX_TIME_TICKS, - &d, sizeof(d)) == -1) - return (-1); - break; - - case SNMP_COUNTER64: - l = (u_int64_t)val; - if (snmp_agentx_varbind(pdu, &oid, AGENTX_COUNTER64, - &l, sizeof(l)) == -1) - return (-1); - break; - - case SNMP_IPADDR: - case SNMP_OPAQUE: - d = (u_int32_t)val; - if (snmp_agentx_varbind(pdu, &oid, AGENTX_OPAQUE, - buf, strlen(buf)) == -1) - return (-1); - break; - - case SNMP_OBJECT: { - struct snmp_oid oid1; - - if (snmp_string2oid(buf, &oid1) == -1) - return (-1); - if (snmp_agentx_varbind(pdu, &oid, AGENTX_OBJECT_IDENTIFIER, - &oid1, sizeof(oid1)) == -1) - return (-1); - } - - case SNMP_BITSTRING: - case SNMP_OCTETSTRING: - if (snmp_agentx_varbind(pdu, &oid, AGENTX_OCTET_STRING, - buf, strlen(buf)) == -1) - return (-1); - break; - - case SNMP_NULL: - /* no data beyond the OID itself */ - if (snmp_agentx_varbind(pdu, &oid, AGENTX_NULL, - NULL, 0) == -1) - return (-1); - } - - return (0); -} - -/* - * SNMP traps for relayd - */ - -void -snmp_hosttrap(struct relayd *env, struct table *table, struct host *host) -{ - struct agentx_pdu *pdu; - - if (snmp_agentx == NULL || env->sc_snmp == -1) - return; - - /* - * OPENBSD-RELAYD-MIB host status trap - * XXX The trap format needs some tweaks and other OIDs - */ - - if ((pdu = snmp_agentx_notify_pdu(&hosttrapoid)) == NULL) - return; - - SNMP_ELEMENT(".1.0", SNMP_NULL, NULL, 0, pdu); - SNMP_ELEMENT(".1.1.0", SNMP_OCTETSTRING, host->conf.name, 0, pdu); - SNMP_ELEMENT(".1.2.0", SNMP_INTEGER32, NULL, host->up, pdu); - SNMP_ELEMENT(".1.3.0", SNMP_INTEGER32, NULL, host->last_up, pdu); - SNMP_ELEMENT(".1.4.0", SNMP_INTEGER32, NULL, host->up_cnt, pdu); - SNMP_ELEMENT(".1.5.0", SNMP_INTEGER32, NULL, host->check_cnt, pdu); - SNMP_ELEMENT(".1.6.0", SNMP_OCTETSTRING, table->conf.name, 0, pdu); - SNMP_ELEMENT(".1.7.0", SNMP_INTEGER32, NULL, table->up, pdu); - if (!host->conf.retry) - goto done; - SNMP_ELEMENT(".1.8.0", SNMP_INTEGER32, NULL, host->conf.retry, pdu); - SNMP_ELEMENT(".1.9.0", SNMP_INTEGER32, NULL, host->retry_cnt, pdu); - - done: - snmp_agentx_send(snmp_agentx, pdu); - snmp_event_add(env, EV_WRITE); -} - -int -snmp_string2oid(const char *oidstr, struct snmp_oid *o) -{ - char *sp, *p, str[BUFSIZ]; - const char *errstr; - - if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) - return (-1); - bzero(o, sizeof(*o)); - - for (p = sp = str; p != NULL; sp = p) { - if ((p = strpbrk(p, ".-")) != NULL) - *p++ = '\0'; - o->o_id[o->o_n++] = strtonum(sp, 0, UINT_MAX, &errstr); - if (errstr || o->o_n > SNMP_MAX_OID_LEN) - return (-1); - } - - return (0); -} - -void * -sstodata(struct sockaddr_storage *ss) -{ - if (ss->ss_family == AF_INET) - return (&((struct sockaddr_in *)ss)->sin_addr); - if (ss->ss_family == AF_INET6) - return (&((struct sockaddr_in6 *)ss)->sin6_addr); - return (NULL); -} - -size_t -sstolen(struct sockaddr_storage *ss) -{ - if (ss->ss_family == AF_INET) - return (((struct sockaddr_in *)ss)->sin_len); - if (ss->ss_family == AF_INET6) - return (((struct sockaddr_in6 *)ss)->sin6_len); - return (0); -} - -struct rdr * -snmp_rdr_byidx(struct relayd *env, u_int *instanceidx, u_int *objectidx, - u_int getnext, u_int include) -{ - struct rdr *rdr; - - if (*objectidx > RDR_MAX_SUBIDX) - return (NULL); - if (*objectidx == 0) { - if (!getnext) - return (NULL); - *objectidx = 1; - } - - restart: - TAILQ_FOREACH(rdr, env->sc_rdrs, entry) { - if (rdr->conf.id >= *instanceidx) { - if (getnext) { - /* Lexographical ordering */ - - /* 1) try the next instance index */ - if (rdr->conf.id == *instanceidx && !include) - rdr = TAILQ_NEXT(rdr, entry); - if (rdr) { - *instanceidx = rdr->conf.id; - return (rdr); - } - - /* 2) try the next object index */ - if (*objectidx < RDR_MAX_SUBIDX) { - *objectidx += 1; - *instanceidx = 1; - include = 1; - goto restart; - } - - /* 3) no further OIDs under this prefix */ - return (NULL); - } - - if (rdr->conf.id == *instanceidx) - return (rdr); - - return (NULL); - } - } - - return (NULL); -} - -struct relay * -snmp_relay_byidx(struct relayd *env, u_int *instanceidx, u_int *objectidx, - u_int getnext, u_int include) -{ - struct relay *rly; - - if (*objectidx > RELAY_MAX_SUBIDX) - return (NULL); - if (*objectidx == 0) { - if (!getnext) - return (NULL); - *objectidx = 1; - } - - restart: - TAILQ_FOREACH(rly, env->sc_relays, rl_entry) { - if (rly->rl_conf.id >= *instanceidx) { - if (getnext) { - /* Lexographical ordering */ - - /* 1) try the next instance index */ - if (rly->rl_conf.id == *instanceidx && !include) - rly = TAILQ_NEXT(rly, rl_entry); - if (rly) { - *instanceidx = rly->rl_conf.id; - return (rly); - } - - /* 2) try the next object index */ - if (*objectidx < RELAY_MAX_SUBIDX) { - *objectidx += 1; - *instanceidx = 1; - include = 1; - goto restart; - } - - /* 3) no further OIDs under this prefix */ - return (rly); - } - - if (rly->rl_conf.id == *instanceidx) - return (rly); - - return (NULL); - } - } - - return (NULL); -} - -struct router * -snmp_router_byidx(struct relayd *env, u_int *instanceidx, u_int *objectidx, - u_int getnext, u_int include) -{ - struct router *router; - - if (*objectidx > ROUTER_MAX_SUBIDX) - return (NULL); - if (*objectidx == 0) { - if (!getnext) - return (NULL); - *objectidx = 1; - } - - restart: - TAILQ_FOREACH(router, env->sc_rts, rt_entry) { - if (router->rt_conf.id >= *instanceidx) { - if (getnext) { - /* Lexographical ordering */ - - /* 1) try the next instance index */ - if ((router->rt_conf.id == *instanceidx) && - !include) - router = TAILQ_NEXT(router, rt_entry); - if (router) { - *instanceidx = router->rt_conf.id; - return (router); - } - - /* 2) try the next object index */ - if (*objectidx < ROUTER_MAX_SUBIDX) { - *objectidx += 1; - *instanceidx = 1; - include = 1; - goto restart; - } - - /* 3) no further OIDs under this prefix */ - return (NULL); - } - - if (router->rt_conf.id == *instanceidx) - return (router); - - return (NULL); - } - } - - return (NULL); -} - -struct netroute * -snmp_netroute_byidx(struct relayd *env, u_int *instanceidx, u_int *objectidx, - u_int getnext, u_int include) -{ - struct netroute *nr; - - if (*objectidx > NETROUTE_MAX_SUBIDX) - return (NULL); - if (*objectidx == 0) { - if (!getnext) - return (NULL); - *objectidx = 1; - } - - restart: - TAILQ_FOREACH(nr, env->sc_routes, nr_route) { - if (nr->nr_conf.id >= *instanceidx) { - if (getnext) { - /* Lexographical ordering */ - - /* 1) try the next instance index */ - if (nr->nr_conf.id == *instanceidx && !include) - nr = TAILQ_NEXT(nr, nr_route); - if (nr) { - *instanceidx = nr->nr_conf.id; - return (nr); - } - - /* 2) try the next object index */ - if (*objectidx < NETROUTE_MAX_SUBIDX) { - *objectidx += 1; - *instanceidx = 1; - include = 1; - goto restart; - } - - /* 3) no further OIDs under this prefix */ - return (NULL); - } - - if (nr->nr_conf.id == *instanceidx) - return (nr); - - return (NULL); - } - } - - return (NULL); -} - -struct host * -snmp_host_byidx(struct relayd *env, u_int *instanceidx, u_int *objectidx, - u_int getnext, u_int include) -{ - struct host *host; - - if (*objectidx > HOST_MAX_SUBIDX) - return (NULL); - if (*objectidx == 0) { - if (!getnext) - return (NULL); - *objectidx = 1; - } - - restart: - TAILQ_FOREACH(host, &env->sc_hosts, globalentry) { - if (host->conf.id >= *instanceidx) { - if (getnext) { - /* Lexographical ordering */ - - /* 1) try the next instance index */ - if (host->conf.id == *instanceidx && !include) - host = TAILQ_NEXT(host, globalentry); - if (host) { - *instanceidx = host->conf.id; - return (host); - } - - /* 2) try the next object index */ - if (*objectidx < HOST_MAX_SUBIDX) { - *objectidx += 1; - *instanceidx = 1; - include = 1; - goto restart; - } - - /* 3) no further OIDs under this prefix */ - return (NULL); - } - - if (host->conf.id == *instanceidx) - return (host); - - return (NULL); - } - } - - return (NULL); -} - -struct rsession * -snmp_session_byidx(struct relayd *env, u_int *instanceidx, u_int *objectidx, - u_int getnext, u_int include) -{ - struct rsession *session; - - if (*objectidx > SESSION_MAX_SUBIDX) - return (NULL); - if (*objectidx == 0) { - if (!getnext) - return (NULL); - *objectidx = 1; - } - - restart: - TAILQ_FOREACH(session, &env->sc_sessions, se_entry) { - if (session->se_id >= *instanceidx) { - if (getnext) { - /* Lexographical ordering */ - - /* 1) try the next instance index */ - if (session->se_id == *instanceidx && !include) - session = TAILQ_NEXT(session, se_entry); - if (session) { - *instanceidx = session->se_id; - return (session); - } - - /* 2) try the next object index */ - if (*objectidx < SESSION_MAX_SUBIDX) { - *objectidx += 1; - *instanceidx = 1; - include = 1; - goto restart; - } - - /* 3) no further OIDs under this prefix */ - return (NULL); - } - - if (session->se_id == *instanceidx) - return (session); - - return (NULL); - } - } - - return (NULL); -} - -struct table * -snmp_table_byidx(struct relayd *env, u_int *instanceidx, u_int *objectidx, - u_int getnext, u_int include) -{ - struct table *table; - - if (*objectidx > TABLE_MAX_SUBIDX) - return (NULL); - if (*objectidx == 0) { - if (!getnext) - return (NULL); - *objectidx = 1; - } - - restart: - TAILQ_FOREACH(table, env->sc_tables, entry) { - if (table->conf.id >= *instanceidx) { - if (getnext) { - /* Lexographical ordering */ - - /* 1) try the next instance index */ - if (table->conf.id == *instanceidx && !include) - table = TAILQ_NEXT(table, entry); - if (table) { - *instanceidx = table->conf.id; - return (table); - } - - /* 2) try the next object index */ - if (*objectidx < TABLE_MAX_SUBIDX) { - *objectidx += 1; - *instanceidx = 1; - include = 1; - goto restart; - } - - /* 3) no further OIDs under this prefix */ - return (NULL); - } - - if (table->conf.id == *instanceidx) - return (table); - - return (NULL); - } - } - - return (NULL); -} - -int -snmp_redirect(struct relayd *env, struct snmp_oid *oid, - struct agentx_pdu *resp, int getnext, uint32_t einstanceidx, - uint32_t eobjectidx, u_int include) -{ - struct rdr *rdr; - u_int instanceidx, objectidx; - u_int32_t status; - - instanceidx = oid->o_id[OIDIDX_relaydInfo + 2]; - objectidx = oid->o_id[OIDIDX_relaydInfo + 1]; - rdr = snmp_rdr_byidx(env, &instanceidx, &objectidx, getnext, include); - if (rdr == NULL) - return (-1); - - if (instanceidx >= einstanceidx || objectidx >= eobjectidx) - return (0); - - oid->o_id[OIDIDX_relaydInfo + 1] = objectidx; - oid->o_id[OIDIDX_relaydInfo + 2] = instanceidx; - - switch (objectidx) { - case 1: /* index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &rdr->conf.id, - sizeof(rdr->conf.id)) == -1) - return (-1); - break; - case 2: /* status */ - if (rdr->conf.flags & F_DISABLE) - status = 1; - else if (rdr->conf.flags & F_DOWN) - status = 2; - else if (rdr->conf.flags & F_BACKUP) - status = 3; - else - status = 0; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &status, - sizeof(status)) == -1) - return (-1); - break; - case 3: /* name */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, rdr->conf.name, - strlen(rdr->conf.name)) == -1) - return (-1); - break; - case 4: /* count */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_COUNTER64, &rdr->stats.cnt, - sizeof(rdr->stats.cnt)) == -1) - return (-1); - break; - case 5: /* average */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &rdr->stats.avg, - sizeof(rdr->stats.avg)) == -1) - return (-1); - break; - case 6: /* last */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &rdr->stats.last, - sizeof(rdr->stats.last)) == -1) - return (-1); - break; - case 7: /* average hour */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &rdr->stats.avg_hour, - sizeof(rdr->stats.avg_hour)) == -1) - return (-1); - break; - case 8: /* last hour */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &rdr->stats.last_hour, - sizeof(rdr->stats.last_hour)) == -1) - return (-1); - break; - case 9: /* average day */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &rdr->stats.avg_day, - sizeof(rdr->stats.avg_day)) == -1) - return (-1); - break; - case 10: /* last day */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &rdr->stats.last_day, - sizeof(rdr->stats.last_day)) == -1) - return (-1); - break; - default: - fatalx("unhandled host element id"); - } - - return (0); -} - -int -snmp_relay(struct relayd *env, struct snmp_oid *oid, struct agentx_pdu *resp, - int getnext, uint32_t einstanceidx, uint32_t eobjectidx, u_int include) -{ - struct relay *rly; - u_int instanceidx, objectidx; - u_int32_t status, value = 0; - u_int64_t value64 = 0; - int i, nrelay = env->sc_conf.prefork_relay; - - instanceidx = oid->o_id[OIDIDX_relaydInfo + 2]; - objectidx = oid->o_id[OIDIDX_relaydInfo + 1]; - rly = snmp_relay_byidx(env, &instanceidx, &objectidx, getnext, - include); - if (rly == NULL) - return (-1); - - if (instanceidx >= einstanceidx || objectidx >= eobjectidx) - return (0); - - oid->o_id[OIDIDX_relaydInfo + 1] = objectidx; - oid->o_id[OIDIDX_relaydInfo + 2] = instanceidx; - - switch (objectidx) { - case 1: /* index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &rly->rl_conf.id, - sizeof(rly->rl_conf.id)) == -1) - return (-1); - break; - case 2: /* status */ - if (rly->rl_up == HOST_UP) - status = 0; /* active */ - else - status = 1; /* disabled */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &status, - sizeof(status)) == -1) - return (-1); - break; - case 3: /* name */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, &rly->rl_conf.name, - strlen(rly->rl_conf.name)) == -1) - return (-1); - break; - case 4: /* count */ - for (i = 0; i < nrelay; i++) - value64 += rly->rl_stats[i].cnt; - if (snmp_agentx_varbind(resp, oid, - AGENTX_COUNTER64, &value64, - sizeof(value64)) == -1) - return (-1); - break; - case 5: /* average */ - for (i = 0; i < nrelay; i++) - value += rly->rl_stats[i].avg; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &value, - sizeof(value)) == -1) - return (-1); - break; - case 6: /* last */ - for (i = 0; i < nrelay; i++) - value += rly->rl_stats[i].last; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &value, - sizeof(value)) == -1) - return (-1); - break; - case 7: /* average hour */ - for (i = 0; i < nrelay; i++) - value += rly->rl_stats[i].avg_hour; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &value, - sizeof(value)) == -1) - return (-1); - break; - case 8: /* last hour */ - for (i = 0; i < nrelay; i++) - value += rly->rl_stats[i].last_hour; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &value, - sizeof(value)) == -1) - return (-1); - break; - case 9: /* average day */ - for (i = 0; i < nrelay; i++) - value += rly->rl_stats[i].avg_day; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &value, - sizeof(value)) == -1) - return (-1); - break; - case 10: /* last day */ - for (i = 0; i < nrelay; i++) - value += rly->rl_stats[i].last_day; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &value, - sizeof(value)) == -1) - return (-1); - break; - default: - fatalx("unhandled host element id"); - } - - return (0); -} - -int -snmp_router(struct relayd *env, struct snmp_oid *oid, struct agentx_pdu *resp, - int getnext, uint32_t einstanceidx, uint32_t eobjectidx, u_int include) -{ - struct router *router; - u_int instanceidx, objectidx; - u_int32_t status; - - instanceidx = oid->o_id[OIDIDX_relaydInfo + 2]; - objectidx = oid->o_id[OIDIDX_relaydInfo + 1]; - router = snmp_router_byidx(env, &instanceidx, &objectidx, getnext, - include); - if (router == NULL) - return (-1); - - if (instanceidx >= einstanceidx || objectidx >= eobjectidx) - return (0); - - oid->o_id[OIDIDX_relaydInfo + 1] = objectidx; - oid->o_id[OIDIDX_relaydInfo + 2] = instanceidx; - - switch (objectidx) { - case 1: /* index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &router->rt_conf.id, - sizeof(router->rt_conf.id)) == -1) - return (-1); - break; - case 2: /* table index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &router->rt_conf.gwtable, - sizeof(router->rt_conf.gwtable)) == -1) - return (-1); - break; - case 3: /* status */ - if (router->rt_conf.flags & F_DISABLE) - status = 1; /* disabled */ - else - status = 0; /* active */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &status, - sizeof(status)) == -1) - return (-1); - break; - case 4: /* name */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, router->rt_conf.name, - strlen(router->rt_conf.name)) == -1) - return (-1); - break; - case 5: /* pf label */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, router->rt_conf.label, - strlen(router->rt_conf.label)) == -1) - return (-1); - break; - case 6: /* rtable */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &router->rt_conf.rtable, - sizeof(router->rt_conf.rtable)) == -1) - return (-1); - break; - default: - fatalx("unhandled host element id"); - } - - return (0); -} - -int -snmp_netroute(struct relayd *env, struct snmp_oid *oid, - struct agentx_pdu *resp, int getnext, uint32_t einstanceidx, - uint32_t eobjectidx, u_int include) -{ - struct netroute *nr; - u_int32_t addrtype; - u_int instanceidx, objectidx; - - instanceidx = oid->o_id[OIDIDX_relaydInfo + 2]; - objectidx = oid->o_id[OIDIDX_relaydInfo + 1]; - nr = snmp_netroute_byidx(env, &instanceidx, &objectidx, getnext, - include); - if (nr == NULL) - return (-1); - - if (instanceidx >= einstanceidx || objectidx >= eobjectidx) - return (0); - - oid->o_id[OIDIDX_relaydInfo + 1] = objectidx; - oid->o_id[OIDIDX_relaydInfo + 2] = instanceidx; - - switch (objectidx) { - case 1: /* index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &nr->nr_conf.id, - sizeof(nr->nr_conf.id)) == -1) - return (-1); - break; - case 2: /* address */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, sstodata(&nr->nr_conf.ss), - sstolen(&nr->nr_conf.ss)) == -1) - return (-1); - break; - case 3: /* address type */ - if (nr->nr_conf.ss.ss_family == AF_INET) - addrtype = 1; - else if (nr->nr_conf.ss.ss_family == AF_INET6) - addrtype = 2; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &addrtype, - sizeof(addrtype)) == -1) - return (-1); - break; - case 4: /* prefix length */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &nr->nr_conf.prefixlen, - sizeof(nr->nr_conf.prefixlen)) == -1) - return (-1); - break; - case 5: /* router index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &nr->nr_conf.routerid, - sizeof(nr->nr_conf.routerid)) == -1) - return (-1); - break; - default: - fatalx("unhandled host element id"); - } - - return (0); -} - -int -snmp_host(struct relayd *env, struct snmp_oid *oid, struct agentx_pdu *resp, - int getnext, uint32_t einstanceidx, uint32_t eobjectidx, u_int include) -{ - struct host *host; - u_int32_t addrtype, count, error, status; - u_int instanceidx, objectidx; - - instanceidx = oid->o_id[OIDIDX_relaydInfo + 2]; - objectidx = oid->o_id[OIDIDX_relaydInfo + 1]; - host = snmp_host_byidx(env, &instanceidx, &objectidx, getnext, - include); - if (host == NULL) - return (-1); - - if (instanceidx >= einstanceidx || objectidx >= eobjectidx) - return (0); - - oid->o_id[OIDIDX_relaydInfo + 1] = objectidx; - oid->o_id[OIDIDX_relaydInfo + 2] = instanceidx; - - switch (objectidx) { - case 1: /* index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &host->conf.id, - sizeof(host->conf.id)) == -1) - return (-1); - break; - case 2: /* parent index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &host->conf.parentid, - sizeof(host->conf.parentid)) == -1) - return (-1); - break; - case 3: /* table index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &host->conf.tableid, - sizeof(host->conf.tableid)) == -1) - return (-1); - break; - case 4: /* name */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, host->conf.name, - strlen(host->conf.name)) == -1) - return (-1); - break; - case 5: /* address */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, sstodata(&host->conf.ss), - sstolen(&host->conf.ss)) == -1) - return (-1); - break; - case 6: /* address type */ - if (host->conf.ss.ss_family == AF_INET) - addrtype = 1; - else if (host->conf.ss.ss_family == AF_INET6) - addrtype = 2; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &addrtype, - sizeof(addrtype)) == -1) - return (-1); - break; - case 7: /* status */ - if (host->flags & F_DISABLE) - status = 1; - else if (host->up == HOST_UP) - status = 0; - else if (host->up == HOST_DOWN) - status = 2; - else - status = 3; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &status, - sizeof(status)) == -1) - return (-1); - break; - case 8: /* check count */ - count = host->check_cnt; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &count, - sizeof(count)) == -1) - return (-1); - break; - case 9: /* up count */ - count = host->up_cnt; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &count, - sizeof(count)) == -1) - return (-1); - break; - case 10: /* errno */ - error = host->he; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &error, - sizeof(errno)) == -1) - return (-1); - break; - default: - fatalx("unhandled host element id"); - } - - return (0); -} - -int -snmp_session(struct relayd *env, struct snmp_oid *oid, struct agentx_pdu *resp, - int getnext, uint32_t einstanceidx, uint32_t eobjectidx, u_int include) -{ - struct timeval tv, now; - time_t ticks; - struct rsession *session; - u_int instanceidx, objectidx; - u_int32_t status, pid, port, addrtype; - - instanceidx = oid->o_id[OIDIDX_relaydInfo + 2]; - objectidx = oid->o_id[OIDIDX_relaydInfo + 1]; - session = snmp_session_byidx(env, &instanceidx, &objectidx, getnext, - include); - if (session == NULL) - return (-1); - - if (instanceidx >= einstanceidx || objectidx >= eobjectidx) - return (0); - - oid->o_id[OIDIDX_relaydInfo + 1] = objectidx; - oid->o_id[OIDIDX_relaydInfo + 2] = instanceidx; - - switch (objectidx) { - case 1: /* index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &session->se_id, - sizeof(session->se_id)) == -1) - return (-1); - break; - case 2: /* relay index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &session->se_relayid, - sizeof(session->se_relayid)) == -1) - return (-1); - break; - case 3: /* in address */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, sstodata(&session->se_in.ss), - sstolen(&session->se_in.ss)) == -1) - return (-1); - break; - case 4: /* in address type */ - if (session->se_in.ss.ss_family == AF_INET) - addrtype = 1; - else if (session->se_in.ss.ss_family == AF_INET6) - addrtype = 2; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &addrtype, - sizeof(addrtype)) == -1) - return (-1); - break; - case 5: /* out address */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, sstodata(&session->se_out.ss), - sstolen(&session->se_out.ss)) == -1) - return (-1); - break; - case 6: /* out address type */ - if (session->se_out.ss.ss_family == AF_INET) - addrtype = 1; - else if (session->se_out.ss.ss_family == AF_INET6) - addrtype = 2; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &addrtype, - sizeof(addrtype)) == -1) - return (-1); - break; - case 7: /* port out */ - port = session->se_out.port; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &port, - sizeof(port)) == -1) - return (-1); - break; - case 8: /* port in */ - port = session->se_in.port; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &port, - sizeof(port)) == -1) - return (-1); - break; - case 9: /* age */ - getmonotime(&now); - timerclear(&tv); - timersub(&now, &session->se_tv_start, &tv); - ticks = tv.tv_sec * 100 + tv.tv_usec / 10000; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &ticks, - sizeof(ticks)) == -1) - return (-1); - break; - case 10: /* idle time */ - getmonotime(&now); - timerclear(&tv); - timersub(&now, &session->se_tv_last, &tv); - ticks = tv.tv_sec * 100 + tv.tv_usec / 10000; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &ticks, - sizeof(ticks)) == -1) - return (-1); - break; - case 11: /* status */ - if (session->se_done) - status = 1; - else - status = 0; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &status, - sizeof(status)) == -1) - return (-1); - break; - case 12: /* session pid */ - pid = (u_int32_t)session->se_pid; - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &pid, - sizeof(pid)) == -1) - return (-1); - break; - default: - fatalx("unhandled table element id"); - } - - return (0); -} - -int -snmp_table(struct relayd *env, struct snmp_oid *oid, struct agentx_pdu *resp, - int getnext, uint32_t einstanceidx, uint32_t eobjectidx, u_int include) -{ - struct table *table; - u_int instanceidx, objectidx; - u_int32_t status; - - instanceidx = oid->o_id[OIDIDX_relaydInfo + 2]; - objectidx = oid->o_id[OIDIDX_relaydInfo + 1]; - table = snmp_table_byidx(env, &instanceidx, &objectidx, getnext, - include); - if (table == NULL) - return (-1); - - if (instanceidx >= einstanceidx || objectidx >= eobjectidx) - return (0); - - oid->o_id[OIDIDX_relaydInfo + 1] = objectidx; - oid->o_id[OIDIDX_relaydInfo + 2] = instanceidx; - - switch (objectidx) { - case 1: /* index */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &table->conf.id, - sizeof(table->conf.id)) == -1) - return (-1); - break; - case 2: /* name */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_OCTET_STRING, &table->conf.name, - strlen(table->conf.name)) == -1) - return (-1); - break; - case 3: /* status */ - if (TAILQ_EMPTY(&table->hosts)) - status = 1; /* empty */ - else if (table->conf.flags & F_DISABLE) - status = 2; /* disabled */ - else - status = 0; /* active */ - if (snmp_agentx_varbind(resp, oid, - AGENTX_INTEGER, &status, - sizeof(status)) == -1) - return (-1); - break; - default: - fatalx("unhandled table element id"); - } - - return (0); -} diff --git a/usr.sbin/relayd/snmp.h b/usr.sbin/relayd/snmp.h deleted file mode 100644 index dadf1539fd5..00000000000 --- a/usr.sbin/relayd/snmp.h +++ /dev/null @@ -1,452 +0,0 @@ -/* $OpenBSD: snmp.h,v 1.1 2020/06/30 16:59:38 martijn Exp $ */ - -/* - * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef SNMPD_SNMP_H -#define SNMPD_SNMP_H - -#include <sys/types.h> -#include <endian.h> - -/* - * SNMP IMSG interface - */ - -#define SNMP_MAX_OID_STRLEN 128 /* max size of the OID _string_ */ -#define SNMP_SOCKET "/var/run/snmpd.sock" -#define AGENTX_SOCKET "/var/run/agentx.sock" -#define SNMP_RESTRICTED_SOCKET "/var/run/snmpd.rsock" - -enum snmp_type { - SNMP_IPADDR = 0, - SNMP_COUNTER32 = 1, - SNMP_GAUGE32 = 2, - SNMP_UNSIGNED32 = 2, - SNMP_TIMETICKS = 3, - SNMP_OPAQUE = 4, - SNMP_NSAPADDR = 5, - SNMP_COUNTER64 = 6, - SNMP_UINTEGER32 = 7, - - SNMP_INTEGER32 = 100, - SNMP_BITSTRING = 101, - SNMP_OCTETSTRING = 102, - SNMP_NULL = 103, - SNMP_OBJECT = 104 -}; - -enum snmp_imsg_ctl { - IMSG_SNMP_DUMMY = 1000, /* something that works everywhere */ - IMSG_SNMP_ELEMENT, - IMSG_SNMP_END, - IMSG_SNMP_LOCK, /* enable restricted mode */ - IMSG_SNMP_AGENTX -}; - -struct snmp_imsg_hdr { - u_int32_t imsg_type; - u_int16_t imsg_len; - u_int16_t imsg_flags; - u_int32_t imsg_peerid; - u_int32_t imsg_pid; -}; - -struct snmp_imsg { - char snmp_oid[SNMP_MAX_OID_STRLEN]; - u_int8_t snmp_type; - u_int16_t snmp_len; -}; - -/* - * SNMP BER types - */ - -enum snmp_version { - SNMP_V1 = 0, - SNMP_V2 = 1, /* SNMPv2c */ - SNMP_V3 = 3 -}; - -enum snmp_context { - SNMP_C_GETREQ = 0, - SNMP_C_GETNEXTREQ = 1, - SNMP_C_GETRESP = 2, - SNMP_C_SETREQ = 3, - SNMP_C_TRAP = 4, - - /* SNMPv2 */ - SNMP_C_GETBULKREQ = 5, - SNMP_C_INFORMREQ = 6, - SNMP_C_TRAPV2 = 7, - SNMP_C_REPORT = 8 -}; - -enum snmp_application { - SNMP_T_IPADDR = 0, - SNMP_T_COUNTER32 = 1, - SNMP_T_GAUGE32 = 2, - SNMP_T_UNSIGNED32 = 2, - SNMP_T_TIMETICKS = 3, - SNMP_T_OPAQUE = 4, - SNMP_T_NSAPADDR = 5, - SNMP_T_COUNTER64 = 6, - SNMP_T_UINTEGER32 = 7 -}; - -enum snmp_generic_trap { - SNMP_TRAP_COLDSTART = 0, - SNMP_TRAP_WARMSTART = 1, - SNMP_TRAP_LINKDOWN = 2, - SNMP_TRAP_LINKUP = 3, - SNMP_TRAP_AUTHFAILURE = 4, - SNMP_TRAP_EGPNEIGHLOSS = 5, - SNMP_TRAP_ENTERPRISE = 6 -}; - -enum snmp_error { - SNMP_ERROR_NONE = 0, - SNMP_ERROR_TOOBIG = 1, - SNMP_ERROR_NOSUCHNAME = 2, - SNMP_ERROR_BADVALUE = 3, - SNMP_ERROR_READONLY = 4, - SNMP_ERROR_GENERR = 5, - - /* SNMPv2 */ - SNMP_ERROR_NOACCESS = 6, - SNMP_ERROR_WRONGTYPE = 7, - SNMP_ERROR_WRONGLENGTH = 8, - SNMP_ERROR_WRONGENC = 9, - SNMP_ERROR_WRONGVALUE = 10, - SNMP_ERROR_NOCREATION = 11, - SNMP_ERROR_INCONVALUE = 12, - SNMP_ERROR_RESUNAVAIL = 13, /* EGAIN */ - SNMP_ERROR_COMMITFAILED = 14, - SNMP_ERROR_UNDOFAILED = 15, - SNMP_ERROR_AUTHERROR = 16, - SNMP_ERROR_NOTWRITABLE = 17, - SNMP_ERROR_INCONNAME = 18 -}; - -enum snmp_security_model { - SNMP_SEC_ANY = 0, - SNMP_SEC_SNMPv1 = 1, - SNMP_SEC_SNMPv2c = 2, - SNMP_SEC_USM = 3, - SNMP_SEC_TSM = 4 -}; - -#define SNMP_MSGFLAG_AUTH 0x01 -#define SNMP_MSGFLAG_PRIV 0x02 -#define SNMP_MSGFLAG_SECMASK (SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV) -#define SNMP_MSGFLAG_REPORT 0x04 - -#define SNMP_MAX_TIMEWINDOW 150 /* RFC3414 */ - -#define SNMP_MIN_OID_LEN 2 /* OBJECT */ -#define SNMP_MAX_OID_LEN 32 /* OBJECT */ - -struct snmp_oid { - u_int32_t o_id[SNMP_MAX_OID_LEN + 1]; - size_t o_n; -}; - -/* AgentX protocol, as outlined in RFC 2741 */ - -/* version */ -#define AGENTX_VERSION 1 - -/* type */ -#define AGENTX_OPEN 1 -#define AGENTX_CLOSE 2 -#define AGENTX_REGISTER 3 -#define AGENTX_UNREGISTER 4 -#define AGENTX_GET 5 -#define AGENTX_GET_NEXT 6 -#define AGENTX_GET_BULK 7 -#define AGENTX_TEST_SET 8 -#define AGENTX_COMMIT_SET 9 -#define AGENTX_UNDO_SET 10 -#define AGENTX_CLEANUP_SET 11 -#define AGENTX_NOTIFY 12 -#define AGENTX_PING 13 -#define AGENTX_INDEX_ALLOCATE 14 -#define AGENTX_INDEX_DEALLOCATE 15 -#define AGENTX_ADD_AGENT_CAPS 16 -#define AGENTX_REMOVE_AGENT_CAPS 17 -#define AGENTX_RESPONSE 18 - -/* error return codes */ -#define AGENTX_ERR_NONE 0 -#define AGENTX_ERR_OPEN_FAILED 256 -#define AGENTX_ERR_NOT_OPEN 257 -#define AGENTX_ERR_INDEX_WRONG_TYPE 258 -#define AGENTX_ERR_INDEX_ALREADY_ALLOCATED 259 -#define AGENTX_ERR_INDEX_NONE_AVAILABLE 260 -#define AGENTX_ERR_INDEX_NOT_ALLOCATED 261 -#define AGENTX_ERR_UNSUPPORTED_CONTEXT 262 -#define AGENTX_ERR_DUPLICATE_REGISTRATION 263 -#define AGENTX_ERR_UNKNOWN_REGISTRATION 264 -#define AGENTX_ERR_UNKNOWN_AGENT_CAPS 265 -#define AGENTX_ERR_PARSE_ERROR 266 -#define AGENTX_ERR_REQUEST_DENIED 267 -#define AGENTX_ERR_PROCESSING_ERROR 268 - -/* flags */ -#define AGENTX_INSTANCE_REGISTRATION 0x01 -#define AGENTX_NEW_INDEX 0x02 -#define AGENTX_ANY_INDEX 0x04 -#define AGENTX_NON_DEFAULT_CONTEXT 0x08 -#define AGENTX_NETWORK_BYTE_ORDER 0x10 -#define AGENTX_FLAGS_MASK 0x1f - -/* encoded data types */ -#define AGENTX_INTEGER 2 -#define AGENTX_OCTET_STRING 4 -#define AGENTX_NULL 5 -#define AGENTX_OBJECT_IDENTIFIER 6 -#define AGENTX_IP_ADDRESS 64 -#define AGENTX_COUNTER32 65 -#define AGENTX_GAUGE32 66 -#define AGENTX_TIME_TICKS 67 -#define AGENTX_OPAQUE 68 -#define AGENTX_COUNTER64 70 -#define AGENTX_NO_SUCH_OBJECT 128 -#define AGENTX_NO_SUCH_INSTANCE 129 -#define AGENTX_END_OF_MIB_VIEW 130 - -/* for registered MIB overlap */ -#define AGENTX_REGISTER_PRIO_DEFAULT 127 - -/* reasons for request of close */ -#define AGENTX_CLOSE_OTHER 1 -#define AGENTX_CLOSE_PARSE_ERROR 2 -#define AGENTX_CLOSE_PROTOCOL_ERROR 3 -#define AGENTX_CLOSE_TIMEOUTS 4 -#define AGENTX_CLOSE_SHUTDOWN 5 -#define AGENTX_CLOSE_BY_MANAGER 6 - -#define AGENTX_DEFAULT_TIMEOUT 3 - -#define MIN_OID_LEN 2 /* OBJECT */ -#define MAX_OID_LEN 32 /* OBJECT */ - -/* - * Protocol header prefixed to all messages - */ -struct agentx_hdr { - uint8_t version; - uint8_t type; - uint8_t flags; - uint8_t reserved; - uint32_t sessionid; /* chosen by agent */ - uint32_t transactid; /* chosen by subagent */ - uint32_t packetid; /* per-request id */ - uint32_t length; -} __packed; - -/* - * Prefixed to a series of 4-byte values indicating the OID - */ -struct agentx_oid_hdr { - uint8_t n_subid; /* # of oid elements (named in RFC) */ - uint8_t prefix; /* if not 0, OID is 1.3.6.1.<prefix> */ - uint8_t include; /* is OID included in search range */ - uint8_t reserved; /* always 0 */ -} __packed; - -struct agentx_response_data { - uint32_t sysuptime; /* uptime of SNMP context */ - uint16_t error; /* status of request */ - uint16_t index; /* index of failed variable binding */ -} __packed; - -struct agentx_open_timeout { - uint8_t timeout; - uint8_t reserved[3]; -} __packed; - -struct agentx_register_hdr { - uint8_t timeout; - uint8_t priority; - uint8_t subrange; - uint8_t reserved; -} __packed; - -struct agentx_unregister_hdr { - uint8_t reserved1; - uint8_t priority; - uint8_t subrange; - uint8_t reserved2; -} __packed; - -struct agentx_null_oid { - uint8_t padding[4]; -} __packed; - -#define AGENTX_NULL_OID { 0, 0, 0, 0 } - -struct agentx_varbind_hdr { - uint16_t type; - uint16_t reserved; -} __packed; - -struct agentx_response { - struct agentx_hdr hdr; - struct agentx_response_data data; -} __packed; - -struct agentx_close_request_data { - uint8_t reason; - uint8_t padding[3]; -} __packed; - -struct agentx_close_request { - struct agentx_hdr hdr; - struct agentx_close_request_data data; -} __packed; - -struct agentx_getbulk_repeaters { - uint16_t nonrepeaters; - uint16_t maxrepetitions; -} __packed; - -struct agentx_pdu { - uint8_t *buffer; - uint8_t *ptr; - uint8_t *ioptr; - size_t buflen; - size_t datalen; - struct agentx_hdr *hdr; - - char *context; - uint32_t contextlen; - - void *cookie; - struct agentx_pdu *request; /* request this is a response to */ - TAILQ_ENTRY(agentx_pdu) entry; -}; -TAILQ_HEAD(agentx_pdulist, agentx_pdu); - -struct agentx_handle { - int fd; - uint32_t sessionid; - uint32_t transactid; - uint32_t packetid; - int timeout; /* in seconds */ - int error; - int erridx; - - struct agentx_pdulist w; - struct agentx_pdulist inflight; - - struct agentx_pdu *r; -}; - -struct agentx_search_range { - struct snmp_oid start; - struct snmp_oid end; - int include; /* is start oid included in search range */ -}; - -struct agentx_handle * - snmp_agentx_alloc(int); -struct agentx_handle * - snmp_agentx_open(const char *, char *, struct snmp_oid *); -struct agentx_handle * - snmp_agentx_fdopen(int, char *, struct snmp_oid *); -int snmp_agentx_response(struct agentx_handle *, struct agentx_pdu *); -int snmp_agentx_open_response(struct agentx_handle *, struct agentx_pdu *); -struct agentx_pdu * - snmp_agentx_open_pdu(struct agentx_handle *, char *descr, - struct snmp_oid *); -struct agentx_pdu * - snmp_agentx_close_pdu(struct agentx_handle *, uint8_t); -int snmp_agentx_close(struct agentx_handle *, uint8_t); -void snmp_agentx_free(struct agentx_handle *); -int snmp_agentx_ping(struct agentx_handle *); -struct agentx_pdu * - snmp_agentx_ping_pdu(void); -struct agentx_pdu * - snmp_agentx_notify_pdu(struct snmp_oid *); -struct agentx_pdu * - snmp_agentx_request(struct agentx_handle *, struct agentx_pdu *); -int snmp_agentx_varbind(struct agentx_pdu *, struct snmp_oid *, int, - void *, int); -int snmp_agentx_send(struct agentx_handle *, struct agentx_pdu *); -int snmp_agentx_enqueue(struct agentx_handle *, struct agentx_pdu *); -int snmp_agentx_flush(struct agentx_handle *); -struct agentx_pdu * - snmp_agentx_recv(struct agentx_handle *); -struct agentx_pdu * - snmp_agentx_response_pdu(int, int, int); -struct agentx_pdu * - snmp_agentx_register_pdu(struct snmp_oid *, int, int, int); -struct agentx_pdu * - snmp_agentx_unregister_pdu(struct snmp_oid *, int, int); -struct agentx_pdu * - snmp_agentx_get_pdu(struct snmp_oid *, int); -struct agentx_pdu * - snmp_agentx_getnext_pdu(struct snmp_oid *, int); -char *snmp_agentx_read_octetstr(struct agentx_pdu *, int *); -int snmp_agentx_read_oid(struct agentx_pdu *, struct snmp_oid *); -int snmp_agentx_read_searchrange(struct agentx_pdu *, - struct agentx_search_range *); -int snmp_agentx_read_raw(struct agentx_pdu *, void *, int); -int snmp_agentx_copy_raw(struct agentx_pdu *, void *, int); -char *snmp_agentx_type2name(int); -int snmp_agentx_read_int(struct agentx_pdu *, uint32_t *); -int snmp_agentx_read_int64(struct agentx_pdu *, uint64_t *); -int snmp_agentx_raw(struct agentx_pdu *, void *, int); -int snmp_agentx_read_vbhdr(struct agentx_pdu *, struct - agentx_varbind_hdr *); -struct agentx_pdu *snmp_agentx_pdu_alloc(void); -void snmp_agentx_pdu_free(struct agentx_pdu *); -char *snmp_oid2string(struct snmp_oid *, char *, size_t); -int snmp_oid_cmp(struct snmp_oid *, struct snmp_oid *); -void snmp_oid_increment(struct snmp_oid *); - -#if BYTE_ORDER == BIG_ENDIAN - -static __inline int -snmp_agentx_byteorder_native(struct agentx_hdr *h) -{ - return ((h->flags & AGENTX_NETWORK_BYTE_ORDER) != 0); -} - -#define AGENTX_LOCAL_BYTE_ORDER_FLAG AGENTX_NETWORK_BYTE_ORDER -#define snmp_agentx_int_byteswap(_i) htole32(_i) -#define snmp_agentx_int16_byteswap(_i) htole16(_i) -#define snmp_agentx_int64_byteswap(_i) htole64(_i) - -#elif BYTE_ORDER == LITTLE_ENDIAN - -static __inline int -snmp_agentx_byteorder_native(struct agentx_hdr *h) -{ - return ((h->flags & AGENTX_NETWORK_BYTE_ORDER) == 0); -} - -#define AGENTX_LOCAL_BYTE_ORDER_FLAG 0 -#define snmp_agentx_int_byteswap(_i) htobe32(_i) -#define snmp_agentx_int16_byteswap(_i) htobe16(_i) -#define snmp_agentx_int64_byteswap(_i) htobe64(_i) - -#else -#error "Unknown host byte order" -#endif - -#endif /* SNMPD_SNMP_H */ diff --git a/usr.sbin/relayd/subagentx.c b/usr.sbin/relayd/subagentx.c new file mode 100644 index 00000000000..335eab2fb4f --- /dev/null +++ b/usr.sbin/relayd/subagentx.c @@ -0,0 +1,3994 @@ +/* + * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <netinet/in.h> + +#include <errno.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <time.h> +#include <unistd.h> + +#include "subagentx_internal.h" +#include "subagentx.h" + +enum subagentx_index_type { + SAI_TYPE_NEW, + SAI_TYPE_ANY, + SAI_TYPE_VALUE, + SAI_TYPE_DYNAMIC +}; + +#define SUBAGENTX_CONTEXT_CTX(sac) (sac->sac_name_default ? NULL : \ + &(sac->sac_name)) + +struct subagentx_agentcaps { + struct subagentx_context *saa_sac; + struct agentx_oid saa_oid; + struct agentx_ostring saa_descr; + enum subagentx_cstate saa_cstate; + enum subagentx_dstate saa_dstate; + TAILQ_ENTRY(subagentx_agentcaps) saa_sac_agentcaps; +}; + +struct subagentx_region { + struct subagentx_context *sar_sac; + struct agentx_oid sar_oid; + uint8_t sar_timeout; + uint8_t sar_priority; + enum subagentx_cstate sar_cstate; + enum subagentx_dstate sar_dstate; + TAILQ_HEAD(, subagentx_index) sar_indices; + TAILQ_HEAD(, subagentx_object) sar_objects; + TAILQ_ENTRY(subagentx_region) sar_sac_regions; +}; + +struct subagentx_index { + struct subagentx_region *sai_sar; + enum subagentx_index_type sai_type; + struct agentx_varbind sai_vb; + struct subagentx_object **sai_object; + size_t sai_objectlen; + size_t sai_objectsize; + enum subagentx_cstate sai_cstate; + enum subagentx_dstate sai_dstate; + TAILQ_ENTRY(subagentx_index) sai_sar_indices; +}; + +struct subagentx_object { + struct subagentx_region *sao_sar; + struct agentx_oid sao_oid; + struct subagentx_index *sao_index[SUBAGENTX_OID_INDEX_MAX_LEN]; + size_t sao_indexlen; + int sao_implied; + uint8_t sao_timeout; + /* Prevent freeing object while in use by get and set requesets */ + uint32_t sao_lock; + void (*sao_get)(struct subagentx_varbind *); + enum subagentx_cstate sao_cstate; + enum subagentx_dstate sao_dstate; + RB_ENTRY(subagentx_object) sao_sac_objects; + TAILQ_ENTRY(subagentx_object) sao_sar_objects; +}; + +struct subagentx_varbind { + struct subagentx_get *sav_sag; + struct subagentx_object *sav_sao; + struct subagentx_varbind_index { + struct subagentx_index *sav_sai; + union agentx_data sav_idata; + uint8_t sav_idatacomplete; + } sav_index[SUBAGENTX_OID_INDEX_MAX_LEN]; + size_t sav_indexlen; + int sav_initialized; + int sav_include; + struct agentx_varbind sav_vb; + struct agentx_oid sav_start; + struct agentx_oid sav_end; + enum agentx_pdu_error sav_error; +}; + +#define SUBAGENTX_GET_CTX(sag) (sag->sag_context_default ? NULL : \ + &(sag->sag_context)) +struct subagentx_request { + uint32_t sar_packetid; + int (*sar_cb)(struct agentx_pdu *, void *); + void *sar_cookie; + RB_ENTRY(subagentx_request) sar_sa_requests; +}; + +static void subagentx_start(struct subagentx *); +static void subagentx_finalize(struct subagentx *, int); +static void subagentx_wantwritenow(struct subagentx *, int); +void (*subagentx_wantwrite)(struct subagentx *, int) = + subagentx_wantwritenow; +static void subagentx_reset(struct subagentx *); +static void subagentx_free_finalize(struct subagentx *); +static int subagentx_session_start(struct subagentx_session *); +static int subagentx_session_finalize(struct agentx_pdu *, void *); +static int subagentx_session_close(struct subagentx_session *, + enum agentx_close_reason); +static int subagentx_session_close_finalize(struct agentx_pdu *, void *); +static void subagentx_session_free_finalize(struct subagentx_session *); +static void subagentx_session_reset(struct subagentx_session *); +static void subagentx_context_start(struct subagentx_context *); +static void subagentx_context_free_finalize(struct subagentx_context *); +static void subagentx_context_reset(struct subagentx_context *); +static int subagentx_agentcaps_start(struct subagentx_agentcaps *); +static int subagentx_agentcaps_finalize(struct agentx_pdu *, void *); +static int subagentx_agentcaps_close(struct subagentx_agentcaps *); +static int subagentx_agentcaps_close_finalize(struct agentx_pdu *, void *); +static void subagentx_agentcaps_free_finalize(struct subagentx_agentcaps *); +static void subagentx_agentcaps_reset(struct subagentx_agentcaps *); +static int subagentx_region_start(struct subagentx_region *); +static int subagentx_region_finalize(struct agentx_pdu *, void *); +static int subagentx_region_close(struct subagentx_region *); +static int subagentx_region_close_finalize(struct agentx_pdu *, void *); +static void subagentx_region_free_finalize(struct subagentx_region *); +static void subagentx_region_reset(struct subagentx_region *); +static struct subagentx_index *subagentx_index(struct subagentx_region *, + struct agentx_varbind *, enum subagentx_index_type); +static int subagentx_index_start(struct subagentx_index *); +static int subagentx_index_finalize(struct agentx_pdu *, void *); +static void subagentx_index_free_finalize(struct subagentx_index *); +static void subagentx_index_reset(struct subagentx_index *); +static int subagentx_index_close(struct subagentx_index *); +static int subagentx_index_close_finalize(struct agentx_pdu *, void *); +static int subagentx_object_start(struct subagentx_object *); +static int subagentx_object_finalize(struct agentx_pdu *, void *); +static int subagentx_object_lock(struct subagentx_object *); +static void subagentx_object_unlock(struct subagentx_object *); +static int subagentx_object_close(struct subagentx_object *); +static int subagentx_object_close_finalize(struct agentx_pdu *, void *); +static void subagentx_object_free_finalize(struct subagentx_object *); +static void subagentx_object_reset(struct subagentx_object *); +static int subagentx_object_cmp(struct subagentx_object *, + struct subagentx_object *); +static void subagentx_get_start(struct subagentx_context *, + struct agentx_pdu *); +static void subagentx_get_finalize(struct subagentx_get *); +static void subagentx_get_free(struct subagentx_get *); +static void subagentx_varbind_start(struct subagentx_varbind *); +static void subagentx_varbind_finalize(struct subagentx_varbind *); +static void subagentx_varbind_nosuchobject(struct subagentx_varbind *); +static void subagentx_varbind_nosuchinstance(struct subagentx_varbind *); +static void subagentx_varbind_endofmibview(struct subagentx_varbind *); +static void subagentx_varbind_error_type(struct subagentx_varbind *, + enum agentx_pdu_error, int); +static int subagentx_request(struct subagentx *, uint32_t, + int (*)(struct agentx_pdu *, void *), void *); +static int subagentx_request_cmp(struct subagentx_request *, + struct subagentx_request *); +static int subagentx_strcat(char **, const char *); + +RB_PROTOTYPE_STATIC(sa_requests, subagentx_request, sar_sa_requests, + subagentx_request_cmp) +RB_PROTOTYPE_STATIC(sac_objects, subagentx_object, sao_sac_objects, + subagentx_object_cmp) + +struct subagentx * +subagentx(void (*nofd)(struct subagentx *, void *, int), void *cookie) +{ + struct subagentx *sa; + + if ((sa = calloc(1, sizeof(*sa))) == NULL) + return NULL; + + sa->sa_nofd = nofd; + sa->sa_cookie = cookie; + sa->sa_fd = -1; + sa->sa_cstate = SA_CSTATE_CLOSE; + sa->sa_dstate = SA_DSTATE_OPEN; + TAILQ_INIT(&(sa->sa_sessions)); + TAILQ_INIT(&(sa->sa_getreqs)); + RB_INIT(&(sa->sa_requests)); + + subagentx_start(sa); + + return sa; +} + +/* + * subagentx_finalize is not a suitable name for a public API, + * but use it internally for consistency + */ +void +subagentx_connect(struct subagentx *sa, int fd) +{ + subagentx_finalize(sa, fd); +} + +static void +subagentx_start(struct subagentx *sa) +{ +#ifdef AGENTX_DEBUG + if (sa->sa_cstate != SA_CSTATE_CLOSE || + sa->sa_dstate != SA_DSTATE_OPEN) + subagentx_log_sa_fatalx(sa, "%s: unexpected connect", __func__); +#endif + sa->sa_cstate = SA_CSTATE_WAITOPEN; + sa->sa_nofd(sa, sa->sa_cookie, 0); +} + +static void +subagentx_finalize(struct subagentx *sa, int fd) +{ + struct subagentx_session *sas; + + if (sa->sa_cstate != SA_CSTATE_WAITOPEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sa_fatalx(sa, "%s: subagentx unexpected connect", + __func__); +#else + subagentx_log_sa_warnx(sa, + "%s: subagentx unexpected connect: ignoring", __func__); + return; +#endif + } + if ((sa->sa_ax = agentx_new(fd)) == NULL) { + subagentx_log_sa_warn(sa, "failed to initialize"); + close(fd); + subagentx_reset(sa); + return; + } + + subagentx_log_sa_info(sa, "new connection: %d", fd); + + sa->sa_fd = fd; + sa->sa_cstate = SA_CSTATE_OPEN; + + TAILQ_FOREACH(sas, &(sa->sa_sessions), sas_sa_sessions) { + if (subagentx_session_start(sas) == -1) + break; + } +} + +static void +subagentx_wantwritenow(struct subagentx *sa, int fd) +{ + subagentx_write(sa); +} + +static void +subagentx_reset(struct subagentx *sa) +{ + struct subagentx_session *sas, *tsas; + struct subagentx_request *sar; + struct subagentx_get *sag; + + agentx_free(sa->sa_ax); + sa->sa_ax = NULL; + sa->sa_fd = -1; + + sa->sa_cstate = SA_CSTATE_CLOSE; + + while ((sar = RB_MIN(sa_requests, &(sa->sa_requests))) != NULL) { + RB_REMOVE(sa_requests, &(sa->sa_requests), sar); + free(sar); + } + TAILQ_FOREACH_SAFE(sas, &(sa->sa_sessions), sas_sa_sessions, tsas) + subagentx_session_reset(sas); + while (!TAILQ_EMPTY(&(sa->sa_getreqs))) { + sag = TAILQ_FIRST(&(sa->sa_getreqs)); + sag->sag_sac = NULL; + TAILQ_REMOVE(&(sa->sa_getreqs), sag, sag_sa_getreqs); + } + + if (sa->sa_dstate == SA_DSTATE_CLOSE) { + subagentx_free_finalize(sa); + return; + } + + subagentx_start(sa); +} + +void +subagentx_free(struct subagentx *sa) +{ + struct subagentx_session *sas, *tsas; + + if (sa == NULL) + return; + + if (sa->sa_dstate == SA_DSTATE_CLOSE) { +/* Malloc throws abort on invalid pointers as well */ + subagentx_log_sa_fatalx(sa, "%s: double free", __func__); + } + sa->sa_dstate = SA_DSTATE_CLOSE; + + if (!TAILQ_EMPTY(&(sa->sa_sessions))) { + TAILQ_FOREACH_SAFE(sas, &(sa->sa_sessions), sas_sa_sessions, + tsas) { + if (sas->sas_dstate != SA_DSTATE_CLOSE) + subagentx_session_free(sas); + } + } else + subagentx_free_finalize(sa); +} + +static void +subagentx_free_finalize(struct subagentx *sa) +{ +#ifdef AGENTX_DEBUG + if (sa->sa_dstate != SA_DSTATE_CLOSE) + subagentx_log_sa_fatalx(sa, "%s: subagentx not closing", + __func__); + if (!TAILQ_EMPTY(&(sa->sa_sessions))) + subagentx_log_sa_fatalx(sa, "%s: subagentx still has sessions", + __func__); + if (!RB_EMPTY(&(sa->sa_requests))) + subagentx_log_sa_fatalx(sa, + "%s: subagentx still has pending requests", __func__); +#endif + + agentx_free(sa->sa_ax); + sa->sa_nofd(sa, sa->sa_cookie, 1); + free(sa); +} + +struct subagentx_session * +subagentx_session(struct subagentx *sa, uint32_t oid[], + size_t oidlen, const char *descr, uint8_t timeout) +{ + struct subagentx_session *sas; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sa_fatalx(sa, "%s: oidlen > %d", __func__, + SUBAGENTX_OID_MAX_LEN); +#else + errno = EINVAL; + return NULL; +#endif + } + if ((sas = calloc(1, sizeof(*sas))) == NULL) + return NULL; + + sas->sas_sa = sa; + sas->sas_timeout = timeout; + for (i = 0; i < oidlen; i++) + sas->sas_oid.aoi_id[i] = oid[i]; + sas->sas_oid.aoi_idlen = oidlen; + sas->sas_descr.aos_string = (unsigned char *)strdup(descr); + if (sas->sas_descr.aos_string == NULL) { + free(sas); + return NULL; + } + sas->sas_descr.aos_slen = strlen(descr); + sas->sas_cstate = SA_CSTATE_CLOSE; + sas->sas_dstate = SA_DSTATE_OPEN; + TAILQ_INIT(&(sas->sas_contexts)); + TAILQ_INSERT_HEAD(&(sa->sa_sessions), sas, sas_sa_sessions); + + if (sa->sa_cstate == SA_CSTATE_OPEN) + (void) subagentx_session_start(sas); + + return sas; +} + +static int +subagentx_session_start(struct subagentx_session *sas) +{ + struct subagentx *sa = sas->sas_sa; + uint32_t packetid; + +#ifdef AGENTX_DEBUG + if (sa->sa_cstate != SA_CSTATE_OPEN || + sas->sas_cstate != SA_CSTATE_CLOSE || + sas->sas_dstate != SA_DSTATE_OPEN) + subagentx_log_sa_fatalx(sa, "%s: unexpected session open", + __func__); +#endif + packetid = agentx_open(sa->sa_ax, sas->sas_timeout, &(sas->sas_oid), + &(sas->sas_descr)); + if (packetid == 0) { + subagentx_log_sa_warn(sa, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_OPEN)); + subagentx_reset(sa); + return -1; + } + sas->sas_packetid = packetid; + subagentx_log_sa_info(sa, "opening session"); + sas->sas_cstate = SA_CSTATE_WAITOPEN; + return subagentx_request(sa, packetid, subagentx_session_finalize, sas); +} + +static int +subagentx_session_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_session *sas = cookie; + struct subagentx *sa = sas->sas_sa; + struct subagentx_context *sac; + +#ifdef AGENTX_DEBUG + if (sas->sas_cstate != SA_CSTATE_WAITOPEN) + subagentx_log_sa_fatalx(sa, "%s: not expecting new session", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { + subagentx_log_sa_warnx(sa, "failed to open session: %s", + agentx_error2string(pdu->ap_payload.ap_response.ap_error)); + subagentx_reset(sa); + return -1; + } + + sas->sas_id = pdu->ap_header.aph_sessionid; + sas->sas_cstate = SA_CSTATE_OPEN; + + if (sas->sas_dstate == SA_DSTATE_CLOSE) { + subagentx_session_close(sas, AGENTX_CLOSE_SHUTDOWN); + return 0; + } + + subagentx_log_sas_info(sas, "open"); + + TAILQ_FOREACH(sac, &(sas->sas_contexts), sac_sas_contexts) + subagentx_context_start(sac); + return 0; +} + +static int +subagentx_session_close(struct subagentx_session *sas, + enum agentx_close_reason reason) +{ + struct subagentx *sa = sas->sas_sa; + uint32_t packetid; + +#ifdef AGENTX_DEBUG + if (sas->sas_cstate != SA_CSTATE_OPEN) + subagentx_log_sa_fatalx(sa, "%s: unexpected session close", + __func__); +#endif + if ((packetid = agentx_close(sa->sa_ax, sas->sas_id, reason)) == 0) { + subagentx_log_sas_warn(sas, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_CLOSE)); + subagentx_reset(sa); + return -1; + } + + subagentx_log_sas_info(sas, "closing session: %s", + agentx_closereason2string(reason)); + + sas->sas_cstate = SA_CSTATE_WAITCLOSE; + return subagentx_request(sa, packetid, subagentx_session_close_finalize, + sas); +} + +static int +subagentx_session_close_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_session *sas = cookie; + struct subagentx *sa = sas->sas_sa; + struct subagentx_context *sac, *tsac; + +#ifdef AGENTX_DEBUG + if (sas->sas_cstate != SA_CSTATE_WAITCLOSE) + subagentx_log_sas_fatalx(sas, "%s: not expecting session close", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { + subagentx_log_sas_warnx(sas, "failed to close session: %s", + agentx_error2string(pdu->ap_payload.ap_response.ap_error)); + subagentx_reset(sa); + return -1; + } + + sas->sas_cstate = SA_CSTATE_CLOSE; + + subagentx_log_sas_info(sas, "closed"); + + TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac) + subagentx_context_reset(sac); + + if (sas->sas_dstate == SA_DSTATE_CLOSE) + subagentx_session_free_finalize(sas); + else { + if (sa->sa_cstate == SA_CSTATE_OPEN) + if (subagentx_session_start(sas) == -1) + return -1; + } + return 0; +} + +void +subagentx_session_free(struct subagentx_session *sas) +{ + struct subagentx_context *sac, *tsac; + + if (sas == NULL) + return; + + if (sas->sas_dstate == SA_DSTATE_CLOSE) + subagentx_log_sas_fatalx(sas, "%s: double free", __func__); + + sas->sas_dstate = SA_DSTATE_CLOSE; + + if (sas->sas_cstate == SA_CSTATE_OPEN) + (void) subagentx_session_close(sas, AGENTX_CLOSE_SHUTDOWN); + + TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac) { + if (sac->sac_dstate != SA_DSTATE_CLOSE) + subagentx_context_free(sac); + } + + if (sas->sas_cstate == SA_CSTATE_CLOSE) + subagentx_session_free_finalize(sas); +} + +static void +subagentx_session_free_finalize(struct subagentx_session *sas) +{ + struct subagentx *sa = sas->sas_sa; + +#ifdef AGENTX_DEBUG + if (sas->sas_cstate != SA_CSTATE_CLOSE) + subagentx_log_sas_fatalx(sas, "%s: free without closing", + __func__); + if (!TAILQ_EMPTY(&(sas->sas_contexts))) + subagentx_log_sas_fatalx(sas, + "%s: subagentx still has contexts", __func__); +#endif + + TAILQ_REMOVE(&(sa->sa_sessions), sas, sas_sa_sessions); + free(sas->sas_descr.aos_string); + free(sas); + + if (TAILQ_EMPTY(&(sa->sa_sessions)) && sa->sa_dstate == SA_DSTATE_CLOSE) + subagentx_free_finalize(sa); +} + +static void +subagentx_session_reset(struct subagentx_session *sas) +{ + struct subagentx_context *sac, *tsac; + + sas->sas_cstate = SA_CSTATE_CLOSE; + + TAILQ_FOREACH_SAFE(sac, &(sas->sas_contexts), sac_sas_contexts, tsac) + subagentx_context_reset(sac); + + if (sas->sas_dstate == SA_DSTATE_CLOSE) + subagentx_session_free_finalize(sas); +} + +struct subagentx_context * +subagentx_context(struct subagentx_session *sas, const char *name) +{ + struct subagentx_context *sac; + + if (sas->sas_dstate == SA_DSTATE_CLOSE) + subagentx_log_sas_fatalx(sas, "%s: use after free", __func__); + + if ((sac = calloc(1, sizeof(*sac))) == NULL) + return NULL; + + sac->sac_sas = sas; + sac->sac_name_default = (name == NULL); + if (name != NULL) { + sac->sac_name.aos_string = (unsigned char *)strdup(name); + if (sac->sac_name.aos_string == NULL) { + free(sac); + return NULL; + } + sac->sac_name.aos_slen = strlen(name); + } + sac->sac_cstate = sas->sas_cstate == SA_CSTATE_OPEN ? + SA_CSTATE_OPEN : SA_CSTATE_CLOSE; + sac->sac_dstate = SA_DSTATE_OPEN; + TAILQ_INIT(&(sac->sac_agentcaps)); + TAILQ_INIT(&(sac->sac_regions)); + + TAILQ_INSERT_HEAD(&(sas->sas_contexts), sac, sac_sas_contexts); + + return sac; +} + +static void +subagentx_context_start(struct subagentx_context *sac) +{ + struct subagentx_agentcaps *saa; + struct subagentx_region *sar; + +#ifdef AGENTX_DEBUG + if (sac->sac_cstate != SA_CSTATE_CLOSE) + subagentx_log_sac_fatalx(sac, "%s: unexpected context start", + __func__); +#endif + sac->sac_cstate = SA_CSTATE_OPEN; + + TAILQ_FOREACH(saa, &(sac->sac_agentcaps), saa_sac_agentcaps) { + if (subagentx_agentcaps_start(saa) == -1) + return; + } + TAILQ_FOREACH(sar, &(sac->sac_regions), sar_sac_regions) { + if (subagentx_region_start(sar) == -1) + return; + } +} + +uint32_t +subagentx_context_uptime(struct subagentx_context *sac) +{ + struct timespec cur, res; + + if (sac->sac_sysuptimespec.tv_sec == 0 && + sac->sac_sysuptimespec.tv_nsec == 0) + return 0; + + (void) clock_gettime(CLOCK_MONOTONIC, &cur); + + timespecsub(&cur, &(sac->sac_sysuptimespec), &res); + + return sac->sac_sysuptime + + (uint32_t) ((res.tv_sec * 100) + (res.tv_nsec / 10000000)); +} + +struct subagentx_object * +subagentx_context_object_find(struct subagentx_context *sac, + const uint32_t oid[], size_t oidlen, int active, int instance) +{ + struct subagentx_object *sao, sao_search; + size_t i; + + for (i = 0; i < oidlen; i++) + sao_search.sao_oid.aoi_id[i] = oid[i]; + sao_search.sao_oid.aoi_idlen = oidlen; + + sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search); + while (sao == NULL && !instance && sao_search.sao_oid.aoi_idlen > 0) { + sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search); + sao_search.sao_oid.aoi_idlen--; + } + if (active && sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN) + return NULL; + return sao; +} + +struct subagentx_object * +subagentx_context_object_nfind(struct subagentx_context *sac, + const uint32_t oid[], size_t oidlen, int active, int inclusive) +{ + struct subagentx_object *sao, sao_search; + size_t i; + + for (i = 0; i < oidlen; i++) + sao_search.sao_oid.aoi_id[i] = oid[i]; + sao_search.sao_oid.aoi_idlen = oidlen; + + sao = RB_NFIND(sac_objects, &(sac->sac_objects), &sao_search); + if (!inclusive && sao != NULL && + agentx_oid_cmp(&(sao_search.sao_oid), &(sao->sao_oid)) <= 0) { + sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao); + } + + while (active && sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN) + sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao); + return sao; +} + +void +subagentx_context_free(struct subagentx_context *sac) +{ + struct subagentx_agentcaps *saa, *tsaa; + struct subagentx_region *sar, *tsar; + + if (sac == NULL) + return; + +#ifdef AGENTX_DEBUG + if (sac->sac_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sac, "%s: double free", __func__); +#endif + sac->sac_dstate = SA_DSTATE_CLOSE; + + TAILQ_FOREACH_SAFE(saa, &(sac->sac_agentcaps), saa_sac_agentcaps, + tsaa) { + if (saa->saa_dstate != SA_DSTATE_CLOSE) + subagentx_agentcaps_free(saa); + } + TAILQ_FOREACH_SAFE(sar, &(sac->sac_regions), sar_sac_regions, tsar) { + if (sar->sar_dstate != SA_DSTATE_CLOSE) + subagentx_region_free(sar); + } +} + +static void +subagentx_context_free_finalize(struct subagentx_context *sac) +{ + struct subagentx_session *sas = sac->sac_sas; + +#ifdef AGENTX_DEBUG + if (sac->sac_dstate != SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sac, "%s: unexpected context free", + __func__); +#endif + if (!TAILQ_EMPTY(&(sac->sac_regions)) || + !TAILQ_EMPTY(&(sac->sac_agentcaps))) + return; + TAILQ_REMOVE(&(sas->sas_contexts), sac, sac_sas_contexts); + free(sac->sac_name.aos_string); + free(sac); +} + +static void +subagentx_context_reset(struct subagentx_context *sac) +{ + struct subagentx_agentcaps *saa, *tsaa; + struct subagentx_region *sar, *tsar; + + sac->sac_cstate = SA_CSTATE_CLOSE; + sac->sac_sysuptimespec.tv_sec = 0; + sac->sac_sysuptimespec.tv_nsec = 0; + + TAILQ_FOREACH_SAFE(saa, &(sac->sac_agentcaps), saa_sac_agentcaps, tsaa) + subagentx_agentcaps_reset(saa); + TAILQ_FOREACH_SAFE(sar, &(sac->sac_regions), sar_sac_regions, tsar) + subagentx_region_reset(sar); + + if (sac->sac_dstate == SA_DSTATE_CLOSE) + subagentx_context_free_finalize(sac); +} + +struct subagentx_agentcaps * +subagentx_agentcaps(struct subagentx_context *sac, uint32_t oid[], + size_t oidlen, const char *descr) +{ + struct subagentx_agentcaps *saa; + size_t i; + + if (sac->sac_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sac, "%s: use after free", __func__); + + if ((saa = calloc(1, sizeof(*saa))) == NULL) + return NULL; + + saa->saa_sac = sac; + for (i = 0; i < oidlen; i++) + saa->saa_oid.aoi_id[i] = oid[i]; + saa->saa_oid.aoi_idlen = oidlen; + saa->saa_descr.aos_string = (unsigned char *)strdup(descr); + if (saa->saa_descr.aos_string == NULL) { + free(saa); + return NULL; + } + saa->saa_descr.aos_slen = strlen(descr); + saa->saa_cstate = SA_CSTATE_CLOSE; + saa->saa_dstate = SA_DSTATE_OPEN; + + TAILQ_INSERT_TAIL(&(sac->sac_agentcaps), saa, saa_sac_agentcaps); + + if (sac->sac_cstate == SA_CSTATE_OPEN) + subagentx_agentcaps_start(saa); + + return saa; +} + +static int +subagentx_agentcaps_start(struct subagentx_agentcaps *saa) +{ + struct subagentx_context *sac = saa->saa_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + uint32_t packetid; + +#ifdef AGENTX_DEBUG + if (sac->sac_cstate != SA_CSTATE_OPEN || + saa->saa_cstate != SA_CSTATE_CLOSE || + saa->saa_dstate != SA_DSTATE_OPEN) + subagentx_log_sac_fatalx(sac, + "%s: unexpected region registration", __func__); +#endif + + packetid = agentx_addagentcaps(sa->sa_ax, sas->sas_id, + SUBAGENTX_CONTEXT_CTX(sac), &(saa->saa_oid), &(saa->saa_descr)); + if (packetid == 0) { + subagentx_log_sac_warn(sac, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_ADDAGENTCAPS)); + subagentx_reset(sa); + return -1; + } + subagentx_log_sac_info(sac, "agentcaps %s: opening", + agentx_oid2string(&(saa->saa_oid))); + saa->saa_cstate = SA_CSTATE_WAITOPEN; + return subagentx_request(sa, packetid, subagentx_agentcaps_finalize, + saa); +} + +static int +subagentx_agentcaps_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_agentcaps *saa = cookie; + struct subagentx_context *sac = saa->saa_sac; + +#ifdef AGENTX_DEBUG + if (saa->saa_cstate != SA_CSTATE_WAITOPEN) + subagentx_log_sac_fatalx(sac, + "%s: not expecting agentcaps open", __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { + /* Agentcaps failing is nothing too serious */ + subagentx_log_sac_warn(sac, "agentcaps %s: %s", + agentx_oid2string(&(saa->saa_oid)), + agentx_error2string(pdu->ap_payload.ap_response.ap_error)); + saa->saa_cstate = SA_CSTATE_CLOSE; + return 0; + } + + saa->saa_cstate = SA_CSTATE_OPEN; + + subagentx_log_sac_info(sac, "agentcaps %s: open", + agentx_oid2string(&(saa->saa_oid))); + + if (saa->saa_dstate == SA_DSTATE_CLOSE) + subagentx_agentcaps_close(saa); + + return 0; +} + +static int +subagentx_agentcaps_close(struct subagentx_agentcaps *saa) +{ + struct subagentx_context *sac = saa->saa_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + uint32_t packetid; + +#ifdef AGENTX_DEBUG + if (saa->saa_cstate != SA_CSTATE_OPEN) + subagentx_log_sac_fatalx(sac, "%s: unexpected agentcaps close", + __func__); +#endif + + saa->saa_cstate = SA_CSTATE_WAITCLOSE; + if (sas->sas_cstate == SA_CSTATE_WAITCLOSE) + return 0; + + packetid = agentx_removeagentcaps(sa->sa_ax, sas->sas_id, + SUBAGENTX_CONTEXT_CTX(sac), &(saa->saa_oid)); + if (packetid == 0) { + subagentx_log_sac_warn(sac, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_REMOVEAGENTCAPS)); + subagentx_reset(sa); + return -1; + } + subagentx_log_sac_info(sac, "agentcaps %s: closing", + agentx_oid2string(&(saa->saa_oid))); + return subagentx_request(sa, packetid, + subagentx_agentcaps_close_finalize, saa); +} + +static int +subagentx_agentcaps_close_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_agentcaps *saa = cookie; + struct subagentx_context *sac = saa->saa_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + +#ifdef AGENTX_DEBUG + if (saa->saa_cstate != SA_CSTATE_WAITCLOSE) + subagentx_log_sac_fatalx(sac, "%s: unexpected agentcaps close", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { + subagentx_log_sac_warnx(sac, "agentcaps %s: %s", + agentx_oid2string(&(saa->saa_oid)), + agentx_error2string(pdu->ap_payload.ap_response.ap_error)); + subagentx_reset(sa); + return -1; + } + + saa->saa_cstate = SA_CSTATE_CLOSE; + + subagentx_log_sac_info(sac, "agentcaps %s: closed", + agentx_oid2string(&(saa->saa_oid))); + + if (saa->saa_dstate == SA_DSTATE_CLOSE) { + subagentx_agentcaps_free_finalize(saa); + return 0; + } else { + if (sac->sac_cstate == SA_CSTATE_OPEN) { + if (subagentx_agentcaps_start(saa) == -1) + return -1; + } + } + return 0; +} + +void +subagentx_agentcaps_free(struct subagentx_agentcaps *saa) +{ + if (saa == NULL) + return; + + if (saa->saa_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(saa->saa_sac, "%s: double free", + __func__); + + saa->saa_dstate = SA_DSTATE_CLOSE; + + if (saa->saa_cstate == SA_CSTATE_OPEN) { + if (subagentx_agentcaps_close(saa) == -1) + return; + } + + if (saa->saa_cstate == SA_CSTATE_CLOSE) + subagentx_agentcaps_free_finalize(saa); +} + +static void +subagentx_agentcaps_free_finalize(struct subagentx_agentcaps *saa) +{ + struct subagentx_context *sac = saa->saa_sac; + +#ifdef AGENTX_DEBUG + if (saa->saa_dstate != SA_DSTATE_CLOSE || + saa->saa_cstate != SA_CSTATE_CLOSE) + subagentx_log_sac_fatalx(sac, "%s: unexpected free", __func__); +#endif + + TAILQ_REMOVE(&(sac->sac_agentcaps), saa, saa_sac_agentcaps); + free(saa->saa_descr.aos_string); + free(saa); + + if (sac->sac_dstate == SA_DSTATE_CLOSE) + subagentx_context_free_finalize(sac); +} + +static void +subagentx_agentcaps_reset(struct subagentx_agentcaps *saa) +{ + saa->saa_cstate = SA_CSTATE_CLOSE; + + if (saa->saa_dstate == SA_DSTATE_CLOSE) + subagentx_agentcaps_free_finalize(saa); +} + +struct subagentx_region * +subagentx_region(struct subagentx_context *sac, uint32_t oid[], + size_t oidlen, uint8_t timeout) +{ + struct subagentx_region *sar; + struct agentx_oid tmpoid; + size_t i; + + if (sac->sac_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sac, "%s: use after free", __func__); + if (oidlen < 1) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sac, "%s: oidlen == 0", __func__); +#else + errno = EINVAL; + return NULL; +#endif + } + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sac, "%s: oidlen > %d", __func__, + SUBAGENTX_OID_MAX_LEN); +#else + errno = EINVAL; + return NULL; +#endif + } + + for (i = 0; i < oidlen; i++) + tmpoid.aoi_id[i] = oid[i]; + tmpoid.aoi_idlen = oidlen; + TAILQ_FOREACH(sar, &(sac->sac_regions), sar_sac_regions) { + if (agentx_oid_cmp(&(sar->sar_oid), &tmpoid) == 0) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sac, + "%s: duplicate region registration", __func__); +#else + errno = EINVAL; + return NULL; +#endif + } + } + + if ((sar = calloc(1, sizeof(*sar))) == NULL) + return NULL; + + sar->sar_sac = sac; + sar->sar_timeout = timeout; + sar->sar_priority = AGENTX_PRIORITY_DEFAULT; + bcopy(&tmpoid, &(sar->sar_oid), sizeof(sar->sar_oid)); + sar->sar_cstate = SA_CSTATE_CLOSE; + sar->sar_dstate = SA_DSTATE_OPEN; + TAILQ_INIT(&(sar->sar_indices)); + TAILQ_INIT(&(sar->sar_objects)); + + TAILQ_INSERT_HEAD(&(sac->sac_regions), sar, sar_sac_regions); + + if (sac->sac_cstate == SA_CSTATE_OPEN) + (void) subagentx_region_start(sar); + + return sar; +} + +static int +subagentx_region_start(struct subagentx_region *sar) +{ + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + uint32_t packetid; + +#ifdef AGENTX_DEBUG + if (sac->sac_cstate != SA_CSTATE_OPEN || + sar->sar_cstate != SA_CSTATE_CLOSE || + sar->sar_dstate != SA_DSTATE_OPEN) + subagentx_log_sac_fatalx(sac, + "%s: unexpected region registration", __func__); +#endif + + packetid = agentx_register(sa->sa_ax, 0, sas->sas_id, + SUBAGENTX_CONTEXT_CTX(sac), sar->sar_timeout, sar->sar_priority, + 0, &(sar->sar_oid), 0); + if (packetid == 0) { + subagentx_log_sac_warn(sac, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_REGISTER)); + subagentx_reset(sa); + return -1; + } + subagentx_log_sac_info(sac, "region %s: opening", + agentx_oid2string(&(sar->sar_oid))); + sar->sar_cstate = SA_CSTATE_WAITOPEN; + return subagentx_request(sa, packetid, subagentx_region_finalize, sar); +} + +static int +subagentx_region_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_region *sar = cookie; + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + struct subagentx_index *sai; + struct subagentx_object *sao; + +#ifdef AGENTX_DEBUG + if (sar->sar_cstate != SA_CSTATE_WAITOPEN) + subagentx_log_sac_fatalx(sac, "%s: not expecting region open", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error == AGENTX_PDU_ERROR_NOERROR) { + sar->sar_cstate = SA_CSTATE_OPEN; + subagentx_log_sac_info(sac, "region %s: open", + agentx_oid2string(&(sar->sar_oid))); + } else if (pdu->ap_payload.ap_response.ap_error == + AGENTX_PDU_ERROR_DUPLICATEREGISTRATION) { + sar->sar_cstate = SA_CSTATE_CLOSE; + /* Try at lower priority: first come first serve */ + if ((++sar->sar_priority) != 0) { + subagentx_log_sac_warnx(sac, "region %s: duplicate, " + "reducing priority", + agentx_oid2string(&(sar->sar_oid))); + return subagentx_region_start(sar); + } + subagentx_log_sac_info(sac, "region %s: duplicate, can't " + "reduce priority, ignoring", + agentx_oid2string(&(sar->sar_oid))); + } else if (pdu->ap_payload.ap_response.ap_error == + AGENTX_PDU_ERROR_REQUESTDENIED) { + sar->sar_cstate = SA_CSTATE_CLOSE; + subagentx_log_sac_warnx(sac, "region %s: %s", + agentx_oid2string(&(sar->sar_oid)), + agentx_error2string(pdu->ap_payload.ap_response.ap_error)); + /* + * If we can't register a region, related objects are useless. + * But no need to retry. + */ + return 0; + } else { + subagentx_log_sac_info(sac, "region %s: %s", + agentx_oid2string(&(sar->sar_oid)), + agentx_error2string(pdu->ap_payload.ap_response.ap_error)); + subagentx_reset(sa); + return -1; + } + + if (sar->sar_dstate == SA_DSTATE_CLOSE) { + if (subagentx_region_close(sar) == -1) + return -1; + } else { + TAILQ_FOREACH(sai, &(sar->sar_indices), sai_sar_indices) { + if (subagentx_index_start(sai) == -1) + return -1; + } + TAILQ_FOREACH(sao, &(sar->sar_objects), sao_sar_objects) { + if (subagentx_object_start(sao) == -1) + return -1; + } + } + return 0; +} + +static int +subagentx_region_close(struct subagentx_region *sar) +{ + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + uint32_t packetid; + +#ifdef AGENTX_DEBUG + if (sar->sar_cstate != SA_CSTATE_OPEN) + subagentx_log_sac_fatalx(sac, "%s: unexpected region close", + __func__); +#endif + + sar->sar_cstate = SA_CSTATE_WAITCLOSE; + if (sas->sas_cstate == SA_CSTATE_WAITCLOSE) + return 0; + + packetid = agentx_unregister(sa->sa_ax, sas->sas_id, + SUBAGENTX_CONTEXT_CTX(sac), sar->sar_priority, 0, &(sar->sar_oid), + 0); + if (packetid == 0) { + subagentx_log_sac_warn(sac, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_UNREGISTER)); + subagentx_reset(sa); + return -1; + } + subagentx_log_sac_info(sac, "region %s: closing", + agentx_oid2string(&(sar->sar_oid))); + return subagentx_request(sa, packetid, subagentx_region_close_finalize, + sar); +} + +static int +subagentx_region_close_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_region *sar = cookie; + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + +#ifdef AGENTX_DEBUG + if (sar->sar_cstate != SA_CSTATE_WAITCLOSE) + subagentx_log_sac_fatalx(sac, "%s: unexpected region close", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { + subagentx_log_sac_warnx(sac, "closing %s: %s", + agentx_oid2string(&(sar->sar_oid)), + agentx_error2string(pdu->ap_payload.ap_response.ap_error)); + subagentx_reset(sa); + return -1; + } + + sar->sar_priority = AGENTX_PRIORITY_DEFAULT; + sar->sar_cstate = SA_CSTATE_CLOSE; + + subagentx_log_sac_info(sac, "region %s: closed", + agentx_oid2string(&(sar->sar_oid))); + + if (sar->sar_dstate == SA_DSTATE_CLOSE) { + subagentx_region_free_finalize(sar); + return 0; + } else { + if (sac->sac_cstate == SA_CSTATE_OPEN) { + if (subagentx_region_start(sar) == -1) + return -1; + } + } + return 0; +} + +void +subagentx_region_free(struct subagentx_region *sar) +{ + struct subagentx_index *sai, *tsai; + struct subagentx_object *sao, *tsao; + + if (sar == NULL) + return; + + if (sar->sar_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sar->sar_sac, "%s: double free", + __func__); + + sar->sar_dstate = SA_DSTATE_CLOSE; + + TAILQ_FOREACH_SAFE(sai, &(sar->sar_indices), sai_sar_indices, tsai) { + if (sai->sai_dstate != SA_DSTATE_CLOSE) + subagentx_index_free(sai); + } + + TAILQ_FOREACH_SAFE(sao, &(sar->sar_objects), sao_sar_objects, tsao) { + if (sao->sao_dstate != SA_DSTATE_CLOSE) + subagentx_object_free(sao); + } + + if (sar->sar_cstate == SA_CSTATE_OPEN) { + if (subagentx_region_close(sar) == -1) + return; + } + + if (sar->sar_cstate == SA_CSTATE_CLOSE) + subagentx_region_free_finalize(sar); +} + +static void +subagentx_region_free_finalize(struct subagentx_region *sar) +{ + struct subagentx_context *sac = sar->sar_sac; + +#ifdef AGENTX_DEBUG + if (sar->sar_dstate != SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sac, "%s: unexpected free", __func__); +#endif + + if (!TAILQ_EMPTY(&(sar->sar_indices)) || + !TAILQ_EMPTY(&(sar->sar_objects))) + return; + + if (sar->sar_cstate != SA_CSTATE_CLOSE) + return; + + TAILQ_REMOVE(&(sac->sac_regions), sar, sar_sac_regions); + free(sar); + + if (sac->sac_dstate == SA_DSTATE_CLOSE) + subagentx_context_free_finalize(sac); +} + +static void +subagentx_region_reset(struct subagentx_region *sar) +{ + struct subagentx_index *sai, *tsai; + struct subagentx_object *sao, *tsao; + + sar->sar_cstate = SA_CSTATE_CLOSE; + sar->sar_priority = AGENTX_PRIORITY_DEFAULT; + + TAILQ_FOREACH_SAFE(sai, &(sar->sar_indices), sai_sar_indices, tsai) + subagentx_index_reset(sai); + TAILQ_FOREACH_SAFE(sao, &(sar->sar_objects), sao_sar_objects, tsao) + subagentx_object_reset(sao); + + if (sar->sar_dstate == SA_DSTATE_CLOSE) + subagentx_region_free_finalize(sar); +} + +struct subagentx_index * +subagentx_index_integer_new(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_INTEGER; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_uint32 = 0; + + return subagentx_index(sar, &vb, SAI_TYPE_NEW); +} + +struct subagentx_index * +subagentx_index_integer_any(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_INTEGER; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_uint32 = 0; + + return subagentx_index(sar, &vb, SAI_TYPE_ANY); +} + +struct subagentx_index * +subagentx_index_integer_value(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen, uint32_t value) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_INTEGER; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_uint32 = value; + + return subagentx_index(sar, &vb, SAI_TYPE_VALUE); +} + +struct subagentx_index * +subagentx_index_integer_dynamic(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_INTEGER; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + + return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); +} + +struct subagentx_index * +subagentx_index_string_dynamic(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_ostring.aos_slen = 0; + vb.avb_data.avb_ostring.aos_string = NULL; + + return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); +} + +struct subagentx_index * +subagentx_index_nstring_dynamic(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen, size_t vlen) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + if (vlen == 0 || vlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid string " + "length: %zu\n", __func__, vlen); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid string " + "length: %zu\n", __func__, vlen); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_ostring.aos_slen = vlen; + vb.avb_data.avb_ostring.aos_string = NULL; + + return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); +} + +struct subagentx_index * +subagentx_index_oid_dynamic(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_OID; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_oid.aoi_idlen = 0; + + return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); +} + +struct subagentx_index * +subagentx_index_noid_dynamic(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen, size_t vlen) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + if (vlen == 0 || vlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid string " + "length: %zu\n", __func__, vlen); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid string " + "length: %zu\n", __func__, vlen); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_OID; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_oid.aoi_idlen = oidlen; + vb.avb_data.avb_oid.aoi_idlen = vlen; + + return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); +} + +struct subagentx_index * +subagentx_index_ipaddress_dynamic(struct subagentx_region *sar, uint32_t oid[], + size_t oidlen) +{ + struct agentx_varbind vb; + size_t i; + + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + vb.avb_type = AGENTX_DATA_TYPE_IPADDRESS; + for (i = 0; i < oidlen; i++) + vb.avb_oid.aoi_id[i] = oid[i]; + vb.avb_data.avb_ostring.aos_string = NULL; + vb.avb_oid.aoi_idlen = oidlen; + + return subagentx_index(sar, &vb, SAI_TYPE_DYNAMIC); +} + +static struct subagentx_index * +subagentx_index(struct subagentx_region *sar, struct agentx_varbind *vb, + enum subagentx_index_type type) +{ + struct subagentx_index *sai; + + if (sar->sar_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sar->sar_sac, "%s: use after free", + __func__); + if (agentx_oid_cmp(&(sar->sar_oid), &(vb->avb_oid)) != -2) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oid is not child " + "of region %s", __func__, + agentx_oid2string(&(vb->avb_oid))); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oid is not child of " + "region %s", __func__, agentx_oid2string(&(vb->avb_oid))); + errno = EINVAL; + return NULL; +#endif + } + + if ((sai = calloc(1, sizeof(*sai))) == NULL) + return NULL; + + sai->sai_sar = sar; + sai->sai_type = type; + bcopy(vb, &(sai->sai_vb), sizeof(*vb)); + sai->sai_cstate = SA_CSTATE_CLOSE; + sai->sai_dstate = SA_DSTATE_OPEN; + TAILQ_INSERT_HEAD(&(sar->sar_indices), sai, sai_sar_indices); + + if (sar->sar_cstate == SA_CSTATE_OPEN) + subagentx_index_start(sai); + + return sai; +} + +static int +subagentx_index_start(struct subagentx_index *sai) +{ + struct subagentx_region *sar = sai->sai_sar; + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + uint32_t packetid; + int flags = 0; + +#ifdef AGENTX_DEBUG + if (sar->sar_cstate != SA_CSTATE_OPEN || + sai->sai_cstate != SA_CSTATE_CLOSE || + sai->sai_dstate != SA_DSTATE_OPEN) + subagentx_log_sac_fatalx(sac, "%s: unexpected index allocation", + __func__); +#endif + + sai->sai_cstate = SA_CSTATE_WAITOPEN; + + if (sai->sai_type == SAI_TYPE_NEW) + flags = AGENTX_PDU_FLAG_NEW_INDEX; + else if (sai->sai_type == SAI_TYPE_ANY) + flags = AGENTX_PDU_FLAG_ANY_INDEX; + else if (sai->sai_type == SAI_TYPE_DYNAMIC) { + subagentx_index_finalize(NULL, sai); + return 0; + } + + /* We might be able to bundle, but if we fail we'd have to reorganise */ + packetid = agentx_indexallocate(sa->sa_ax, flags, sas->sas_id, + SUBAGENTX_CONTEXT_CTX(sac), &(sai->sai_vb), 1); + if (packetid == 0) { + subagentx_log_sac_warn(sac, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_INDEXDEALLOCATE)); + subagentx_reset(sa); + return -1; + } + if (sai->sai_type == SAI_TYPE_VALUE) + subagentx_log_sac_info(sac, "index %s: allocating '%u'", + agentx_oid2string(&(sai->sai_vb.avb_oid)), + sai->sai_vb.avb_data.avb_uint32); + else if (sai->sai_type == SAI_TYPE_ANY) + subagentx_log_sac_info(sac, "index %s: allocating any index", + agentx_oid2string(&(sai->sai_vb.avb_oid))); + else if (sai->sai_type == SAI_TYPE_NEW) + subagentx_log_sac_info(sac, "index %s: allocating new index", + agentx_oid2string(&(sai->sai_vb.avb_oid))); + + return subagentx_request(sa, packetid, subagentx_index_finalize, sai); +} + +static int +subagentx_index_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_index *sai = cookie; + struct subagentx_region *sar = sai->sai_sar; + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + struct agentx_pdu_response *resp; + size_t i; + +#ifdef AGENTX_DEBUG + if (sai->sai_cstate != SA_CSTATE_WAITOPEN) + subagentx_log_sac_fatalx(sac, + "%s: not expecting index allocate", __func__); +#endif + if (sai->sai_type == SAI_TYPE_DYNAMIC) { + sai->sai_cstate = SA_CSTATE_OPEN; + return 0; + } + + resp = &(pdu->ap_payload.ap_response); + if (resp->ap_error != AGENTX_PDU_ERROR_NOERROR) { + sai->sai_cstate = SA_CSTATE_CLOSE; + subagentx_log_sac_warnx(sac, "index %s: %s", + agentx_oid2string(&(sar->sar_oid)), + agentx_error2string(resp->ap_error)); + return 0; + } + sai->sai_cstate = SA_CSTATE_OPEN; + if (resp->ap_nvarbind != 1) { + subagentx_log_sac_warnx(sac, "index %s: unexpected number of " + "indices", agentx_oid2string(&(sar->sar_oid))); + subagentx_reset(sa); + return -1; + } + if (resp->ap_varbindlist[0].avb_type != sai->sai_vb.avb_type) { + subagentx_log_sac_warnx(sac, "index %s: unexpected index type", + agentx_oid2string(&(sar->sar_oid))); + subagentx_reset(sa); + return -1; + } + if (agentx_oid_cmp(&(resp->ap_varbindlist[0].avb_oid), + &(sai->sai_vb.avb_oid)) != 0) { + subagentx_log_sac_warnx(sac, "index %s: unexpected oid", + agentx_oid2string(&(sar->sar_oid))); + subagentx_reset(sa); + return -1; + } + + switch (sai->sai_vb.avb_type) { + case AGENTX_DATA_TYPE_INTEGER: + if (sai->sai_type == SAI_TYPE_NEW || + sai->sai_type == SAI_TYPE_ANY) + sai->sai_vb.avb_data.avb_uint32 = + resp->ap_varbindlist[0].avb_data.avb_uint32; + else if (sai->sai_vb.avb_data.avb_uint32 != + resp->ap_varbindlist[0].avb_data.avb_uint32) { + subagentx_log_sac_warnx(sac, "index %s: unexpected " + "index value", agentx_oid2string(&(sar->sar_oid))); + subagentx_reset(sa); + return -1; + } + subagentx_log_sac_info(sac, "index %s: allocated '%u'", + agentx_oid2string(&(sai->sai_vb.avb_oid)), + sai->sai_vb.avb_data.avb_uint32); + break; + default: + subagentx_log_sac_fatalx(sac, "%s: Unsupported index type", + __func__); + } + + if (sai->sai_dstate == SA_DSTATE_CLOSE) + return subagentx_index_close(sai); + + /* TODO Make use of range_subid register */ + for (i = 0; i < sai->sai_objectlen; i++) { + if (sai->sai_object[i]->sao_dstate == SA_DSTATE_OPEN) { + if (subagentx_object_start(sai->sai_object[i]) == -1) + return -1; + } + } + return 0; +} + +void +subagentx_index_free(struct subagentx_index *sai) +{ + size_t i; + + if (sai == NULL) + return; + + if (sai->sai_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sai->sai_sar->sar_sac, + "%s: double free", __func__); + + sai->sai_dstate = SA_DSTATE_CLOSE; + + /* TODO Do a range_subid unregister before freeing */ + for (i = 0; i < sai->sai_objectlen; i++) { + if (sai->sai_object[i]->sao_dstate != SA_DSTATE_CLOSE) + subagentx_object_free(sai->sai_object[i]); + } + + if (sai->sai_cstate == SA_CSTATE_OPEN) + (void) subagentx_index_close(sai); + else if (sai->sai_cstate == SA_CSTATE_CLOSE) + subagentx_index_free_finalize(sai); +} + +static void +subagentx_index_free_finalize(struct subagentx_index *sai) +{ + struct subagentx_region *sar = sai->sai_sar; + +#ifdef AGENTX_DEBUG + if (sai->sai_dstate != SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sar->sar_sac, "%s: unexpected free", + __func__); + if (sai->sai_cstate != SA_CSTATE_CLOSE) + subagentx_log_sac_fatalx(sar->sar_sac, + "%s: free without deallocating", __func__); +#endif + + if (sai->sai_objectlen != 0) + return; + + TAILQ_REMOVE(&(sar->sar_indices), sai, sai_sar_indices); + agentx_varbind_free(&(sai->sai_vb)); + free(sai->sai_object); + free(sai); + if (sar->sar_dstate == SA_DSTATE_CLOSE) + subagentx_region_free_finalize(sar); +} + +static void +subagentx_index_reset(struct subagentx_index *sai) +{ + sai->sai_cstate = SA_CSTATE_CLOSE; + + if (sai->sai_dstate == SA_DSTATE_CLOSE) + subagentx_index_free_finalize(sai); +} + +static int +subagentx_index_close(struct subagentx_index *sai) +{ + struct subagentx_region *sar = sai->sai_sar; + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + uint32_t packetid; + +#ifdef AGENTX_DEBUG + if (sai->sai_cstate != SA_CSTATE_OPEN) + subagentx_log_sac_fatalx(sac, + "%s: unexpected index deallocation", __func__); +#endif + + sai->sai_cstate = SA_CSTATE_WAITCLOSE; + if (sas->sas_cstate == SA_CSTATE_WAITCLOSE) + return 0; + + /* We might be able to bundle, but if we fail we'd have to reorganise */ + packetid = agentx_indexdeallocate(sa->sa_ax, sas->sas_id, + SUBAGENTX_CONTEXT_CTX(sac), &(sai->sai_vb), 1); + if (packetid == 0) { + subagentx_log_sac_warn(sac, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_INDEXDEALLOCATE)); + subagentx_reset(sa); + return -1; + } + subagentx_log_sac_info(sac, "index %s: deallocating", + agentx_oid2string(&(sai->sai_vb.avb_oid))); + return subagentx_request(sa, packetid, subagentx_index_close_finalize, + sai); +} + +static int +subagentx_index_close_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_index *sai = cookie; + struct subagentx_region *sar = sai->sai_sar; + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + struct agentx_pdu_response *resp = &(pdu->ap_payload.ap_response); + +#ifdef AGENTX_DEBUG + if (sai->sai_cstate != SA_CSTATE_WAITCLOSE) + subagentx_log_sac_fatalx(sac, "%s: unexpected indexdeallocate", + __func__); +#endif + + if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { + subagentx_log_sac_warnx(sac, + "index %s: couldn't deallocate: %s", + agentx_oid2string(&(sai->sai_vb.avb_oid)), + agentx_error2string(resp->ap_error)); + subagentx_reset(sa); + return -1; + } + + if (resp->ap_nvarbind != 1) { + subagentx_log_sac_warnx(sac, + "index %s: unexpected number of indices", + agentx_oid2string(&(sar->sar_oid))); + subagentx_reset(sa); + return -1; + } + if (resp->ap_varbindlist[0].avb_type != sai->sai_vb.avb_type) { + subagentx_log_sac_warnx(sac, "index %s: unexpected index type", + agentx_oid2string(&(sar->sar_oid))); + subagentx_reset(sa); + return -1; + } + if (agentx_oid_cmp(&(resp->ap_varbindlist[0].avb_oid), + &(sai->sai_vb.avb_oid)) != 0) { + subagentx_log_sac_warnx(sac, "index %s: unexpected oid", + agentx_oid2string(&(sar->sar_oid))); + subagentx_reset(sa); + return -1; + } + switch (sai->sai_vb.avb_type) { + case AGENTX_DATA_TYPE_INTEGER: + if (sai->sai_vb.avb_data.avb_uint32 != + resp->ap_varbindlist[0].avb_data.avb_uint32) { + subagentx_log_sac_warnx(sac, + "index %s: unexpected index value", + agentx_oid2string(&(sar->sar_oid))); + subagentx_reset(sa); + return -1; + } + break; + default: + subagentx_log_sac_fatalx(sac, "%s: Unsupported index type", + __func__); + } + + sai->sai_cstate = SA_CSTATE_CLOSE; + + subagentx_log_sac_info(sac, "index %s: deallocated", + agentx_oid2string(&(sai->sai_vb.avb_oid))); + + if (sai->sai_dstate == SA_DSTATE_CLOSE) { + subagentx_index_free_finalize(sai); + } else if (sar->sar_cstate == SA_CSTATE_OPEN) { + if (subagentx_index_start(sai) == -1) + return -1; + } + return 0; +} + +struct subagentx_object * +subagentx_object(struct subagentx_region *sar, uint32_t oid[], size_t oidlen, + struct subagentx_index *sai[], size_t sailen, int implied, + void (*get)(struct subagentx_varbind *)) +{ + struct subagentx_object *sao, **tsao, sao_search; + struct subagentx_index *lsai; + int ready = 1; + size_t i, j; + + if (sar->sar_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sar->sar_sac, "%s: use after free", + __func__); + if (oidlen < 1) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen == 0", + __func__); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen == 0", + __func__); + errno = EINVAL; + return NULL; +#endif + } + if (oidlen > SUBAGENTX_OID_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: oidlen > %d", + __func__, SUBAGENTX_OID_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + if (sailen > SUBAGENTX_OID_INDEX_MAX_LEN) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: indexlen > %d", + __func__, SUBAGENTX_OID_INDEX_MAX_LEN); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: indexlen > %d", + __func__, SUBAGENTX_OID_INDEX_MAX_LEN); + errno = EINVAL; + return NULL; +#endif + } + + for (i = 0; i < oidlen; i++) + sao_search.sao_oid.aoi_id[i] = oid[i]; + sao_search.sao_oid.aoi_idlen = oidlen; + + do { + if (RB_FIND(sac_objects, &(sar->sar_sac->sac_objects), + &sao_search) != NULL) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid " + "parent child object relationship", __func__); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid " + "parent child object relationship", __func__); + errno = EINVAL; + return NULL; +#endif + } + sao_search.sao_oid.aoi_idlen--; + } while (sao_search.sao_oid.aoi_idlen > 0); + sao_search.sao_oid.aoi_idlen = oidlen; + sao = RB_NFIND(sac_objects, &(sar->sar_sac->sac_objects), &sao_search); + if (sao != NULL && + agentx_oid_cmp(&(sao->sao_oid), &(sao_search.sao_oid)) == 2) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: invalid parent " + "child object relationship", __func__); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: invalid parent " + "child object relationship", __func__); + errno = EINVAL; + return NULL; +#endif + } + if (implied == 1) { + lsai = sai[sailen - 1]; + if (lsai->sai_vb.avb_type == AGENTX_DATA_TYPE_OCTETSTRING) { + if (lsai->sai_vb.avb_data.avb_ostring.aos_slen != 0) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, + "%s: implied can only be used on strings " + "of dynamic length", __func__); +#else + subagentx_log_sac_warnx(sar->sar_sac, + "%s: implied can only be used on strings " + "of dynamic length", __func__); + errno = EINVAL; + return NULL; +#endif + } + } else if (lsai->sai_vb.avb_type == AGENTX_DATA_TYPE_OID) { + if (lsai->sai_vb.avb_data.avb_oid.aoi_idlen != 0) { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, + "%s: implied can only be used on oids of " + "dynamic length", __func__); +#else + subagentx_log_sac_warnx(sar->sar_sac, + "%s: implied can only be used on oids of " + "dynamic length", __func__); + errno = EINVAL; + return NULL; +#endif + } + } else { +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sar->sar_sac, "%s: implied " + "can only be set on oid and string indices", + __func__); +#else + subagentx_log_sac_warnx(sar->sar_sac, "%s: implied can " + "only be set on oid and string indices", __func__); + errno = EINVAL; + return NULL; +#endif + } + } + + ready = sar->sar_cstate == SA_CSTATE_OPEN; + if ((sao = calloc(1, sizeof(*sao))) == NULL) + return NULL; + sao->sao_sar = sar; + bcopy(&(sao_search.sao_oid), &(sao->sao_oid), sizeof(sao->sao_oid)); + for (i = 0; i < sailen; i++) { + sao->sao_index[i] = sai[i]; + if (sai[i]->sai_objectlen == sai[i]->sai_objectsize) { + tsao = recallocarray(sai[i]->sai_object, + sai[i]->sai_objectlen, sai[i]->sai_objectlen + 1, + sizeof(*sai[i]->sai_object)); + if (tsao == NULL) { + free(sao); + return NULL; + } + sai[i]->sai_object = tsao; + sai[i]->sai_objectsize = sai[i]->sai_objectlen + 1; + } + for (j = 0; j < sai[i]->sai_objectlen; j++) { + if (agentx_oid_cmp(&(sao->sao_oid), + &(sai[i]->sai_object[j]->sao_oid)) < 0) { + memmove(&(sai[i]->sai_object[j + 1]), + &(sai[i]->sai_object[j]), + sizeof(*(sai[i]->sai_object)) * + (sai[i]->sai_objectlen - j)); + break; + } + } + sai[i]->sai_object[j] = sao; + sai[i]->sai_objectlen++; + if (sai[i]->sai_cstate != SA_CSTATE_OPEN) + ready = 0; + } + sao->sao_indexlen = sailen; + sao->sao_implied = implied; + sao->sao_timeout = 0; + sao->sao_lock = 0; + sao->sao_get = get; + sao->sao_cstate = SA_CSTATE_CLOSE; + sao->sao_dstate = SA_DSTATE_OPEN; + + TAILQ_INSERT_TAIL(&(sar->sar_objects), sao, sao_sar_objects); + RB_INSERT(sac_objects, &(sar->sar_sac->sac_objects), sao); + + if (ready) + subagentx_object_start(sao); + + return sao; +} + +static int +subagentx_object_start(struct subagentx_object *sao) +{ + struct subagentx_region *sar = sao->sao_sar; + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + struct agentx_oid oid; + char oids[1024]; + size_t i; + int needregister = 0; + uint32_t packetid; + uint8_t flags = AGENTX_PDU_FLAG_INSTANCE_REGISTRATION; + +#ifdef AGENTX_DEBUG + if (sar->sar_cstate != SA_CSTATE_OPEN || + sao->sao_cstate != SA_CSTATE_CLOSE || + sao->sao_dstate != SA_DSTATE_OPEN) + subagentx_log_sac_fatalx(sac, + "%s: unexpected object registration", __func__); +#endif + + if (sao->sao_timeout != 0) + needregister = 1; + for (i = 0; i < sao->sao_indexlen; i++) { + if (sao->sao_index[i]->sai_cstate != SA_CSTATE_OPEN) + return 0; + if (sao->sao_index[i]->sai_type != SAI_TYPE_DYNAMIC) + needregister = 1; + } + if (!needregister) { + sao->sao_cstate = SA_CSTATE_WAITOPEN; + subagentx_object_finalize(NULL, sao); + return 0; + } + + bcopy(&(sao->sao_oid), &(oid), sizeof(oid)); + for (i = 0; i < sao->sao_indexlen; i++) { + if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { + flags = 0; + break; + } +#ifdef AGENTX_DEBUG + if (sao->sao_index[i]->sai_vb.avb_type != + AGENTX_DATA_TYPE_INTEGER) + subagentx_log_sac_fatalx(sac, + "%s: Unsupported allocated index type", __func__); +#endif + oid.aoi_id[oid.aoi_idlen++] = + sao->sao_index[i]->sai_vb.avb_data.avb_uint32; + } + packetid = agentx_register(sa->sa_ax, flags, sas->sas_id, + SUBAGENTX_CONTEXT_CTX(sac), sao->sao_timeout, + AGENTX_PRIORITY_DEFAULT, 0, &oid, 0); + if (packetid == 0) { + subagentx_log_sac_warn(sac, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_REGISTER)); + subagentx_reset(sa); + return -1; + } + strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids)); + subagentx_log_sac_info(sac, "object %s (%s %s): opening", + oids, flags ? "instance" : "region", agentx_oid2string(&(oid))); + sao->sao_cstate = SA_CSTATE_WAITOPEN; + return subagentx_request(sa, packetid, subagentx_object_finalize, sao); +} + +static int +subagentx_object_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_object *sao = cookie; + struct subagentx_context *sac = sao->sao_sar->sar_sac; + struct agentx_oid oid; + char oids[1024]; + size_t i; + uint8_t flags = 1; + +#ifdef AGENTX_DEBUG + if (sao->sao_cstate != SA_CSTATE_WAITOPEN) + subagentx_log_sac_fatalx(sac, "%s: not expecting object open", + __func__); +#endif + + if (pdu == NULL) { + sao->sao_cstate = SA_CSTATE_OPEN; + return 0; + } + + bcopy(&(sao->sao_oid), &oid, sizeof(oid)); + for (i = 0; i < sao->sao_indexlen; i++) { + if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { + flags = 0; + break; + } +#ifdef AGENTX_DEBUG + if (sao->sao_index[i]->sai_vb.avb_type != + AGENTX_DATA_TYPE_INTEGER) + subagentx_log_sac_fatalx(sac, + "%s: Unsupported allocated index type", __func__); +#endif + + oid.aoi_id[oid.aoi_idlen++] = + sao->sao_index[i]->sai_vb.avb_data.avb_uint32; + } + strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids)); + + /* + * We should only be here for table objects with registered indices. + * If we fail here something is misconfigured and the admin should fix + * it. + */ + if (pdu->ap_payload.ap_response.ap_error != AGENTX_PDU_ERROR_NOERROR) { + sao->sao_cstate = SA_CSTATE_CLOSE; + subagentx_log_sac_info(sac, "object %s (%s %s): %s", + oids, flags ? "instance" : "region", agentx_oid2string(&oid), + agentx_error2string(pdu->ap_payload.ap_response.ap_error)); + if (sao->sao_dstate == SA_DSTATE_CLOSE) + return subagentx_object_close_finalize(NULL, sao); + return 0; + } + sao->sao_cstate = SA_CSTATE_OPEN; + subagentx_log_sac_info(sac, "object %s (%s %s): open", oids, + flags ? "instance" : "region", agentx_oid2string(&oid)); + + if (sao->sao_dstate == SA_DSTATE_CLOSE) + return subagentx_object_close(sao); + + return 0; +} + +static int +subagentx_object_lock(struct subagentx_object *sao) +{ + if (sao->sao_lock == UINT32_MAX) { + subagentx_log_sac_warnx(sao->sao_sar->sar_sac, + "%s: sao_lock == %u", __func__, UINT32_MAX); + return -1; + } + sao->sao_lock++; + return 0; +} + +static void +subagentx_object_unlock(struct subagentx_object *sao) +{ +#ifdef AGENTX_DEBUG + if (sao->sao_lock == 0) + subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, + "%s: sao_lock == 0", __func__); +#endif + sao->sao_lock--; + if (sao->sao_lock == 0 && sao->sao_dstate == SA_DSTATE_CLOSE && + sao->sao_cstate == SA_CSTATE_CLOSE) + subagentx_object_free_finalize(sao); +} + +static int +subagentx_object_close(struct subagentx_object *sao) +{ + struct subagentx_context *sac = sao->sao_sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + struct agentx_oid oid; + char oids[1024]; + size_t i; + int needclose = 0; + uint32_t packetid; + uint8_t flags = 1; + +#ifdef AGENTX_DEBUG + if (sao->sao_cstate != SA_CSTATE_OPEN) + subagentx_log_sac_fatalx(sac, "%s: unexpected object close", + __func__); +#endif + + for (i = 0; i < sao->sao_indexlen; i++) { +#ifdef AGENTX_DEBUG + if (sao->sao_index[i]->sai_cstate != SA_CSTATE_OPEN) + subagentx_log_sac_fatalx(sac, + "%s: Object open while index closed", __func__); +#endif + if (sao->sao_index[i]->sai_type != SAI_TYPE_DYNAMIC) + needclose = 1; + } + sao->sao_cstate = SA_CSTATE_WAITCLOSE; + if (sas->sas_cstate == SA_CSTATE_WAITCLOSE) + return 0; + if (!needclose) { + subagentx_object_close_finalize(NULL, sao); + return 0; + } + + bcopy(&(sao->sao_oid), &(oid), sizeof(oid)); + for (i = 0; i < sao->sao_indexlen; i++) { + if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { + flags = 0; + break; + } +#ifdef AGENTX_DEBUG + if (sao->sao_index[i]->sai_vb.avb_type != + AGENTX_DATA_TYPE_INTEGER) + subagentx_log_sac_fatalx(sac, + "%s: Unsupported allocated index type", __func__); +#endif + oid.aoi_id[oid.aoi_idlen++] = + sao->sao_index[i]->sai_vb.avb_data.avb_uint32; + } + packetid = agentx_unregister(sa->sa_ax, sas->sas_id, + SUBAGENTX_CONTEXT_CTX(sac), AGENTX_PRIORITY_DEFAULT, 0, &oid, 0); + if (packetid == 0) { + subagentx_log_sac_warn(sac, "couldn't generate %s", + agentx_pdutype2string(AGENTX_PDU_TYPE_UNREGISTER)); + subagentx_reset(sa); + return -1; + } + strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids)); + subagentx_log_sac_info(sac, "object %s (%s %s): closing", + oids, flags ? "instance" : "region", agentx_oid2string(&(oid))); + return subagentx_request(sa, packetid, subagentx_object_close_finalize, + sao); +} + +static int +subagentx_object_close_finalize(struct agentx_pdu *pdu, void *cookie) +{ + struct subagentx_object *sao = cookie; + struct subagentx_region *sar = sao->sao_sar; + struct subagentx_context *sac = sar->sar_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + struct agentx_oid oid; + char oids[1024]; + uint8_t flags = 1; + size_t i; + +#ifdef AGENTX_DEBUG + if (sao->sao_cstate != SA_CSTATE_WAITCLOSE) + subagentx_log_sac_fatalx(sac, + "%s: unexpected object unregister", __func__); +#endif + + if (pdu != NULL) { + bcopy(&(sao->sao_oid), &(oid), sizeof(oid)); + for (i = 0; i < sao->sao_indexlen; i++) { + if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { + flags = 0; + break; + } +#ifdef AGENTX_DEBUG + if (sao->sao_index[i]->sai_vb.avb_type != + AGENTX_DATA_TYPE_INTEGER) + subagentx_log_sac_fatalx(sac, + "%s: Unsupported allocated index type", + __func__); +#endif + oid.aoi_id[oid.aoi_idlen++] = + sao->sao_index[i]->sai_vb.avb_data.avb_uint32; + } + strlcpy(oids, agentx_oid2string(&(sao->sao_oid)), sizeof(oids)); + if (pdu->ap_payload.ap_response.ap_error != + AGENTX_PDU_ERROR_NOERROR) { + subagentx_log_sac_warnx(sac, + "closing object %s (%s %s): %s", oids, + flags ? "instance" : "region", + agentx_oid2string(&oid), agentx_error2string( + pdu->ap_payload.ap_response.ap_error)); + subagentx_reset(sa); + return -1; + } + subagentx_log_sac_info(sac, "object %s (%s %s): closed", oids, + flags ? "instance" : "region", agentx_oid2string(&oid)); + } + + if (sao->sao_dstate == SA_DSTATE_CLOSE) + subagentx_object_free_finalize(sao); + else { + if (sar->sar_cstate == SA_CSTATE_OPEN) + if (subagentx_object_start(sao) == -1) + return -1; + } + + return 0; +} + +void +subagentx_object_free(struct subagentx_object *sao) +{ + if (sao == NULL) + return; + + if (sao->sao_dstate == SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, + "%s: double free", __func__); + + sao->sao_dstate = SA_DSTATE_CLOSE; + + if (sao->sao_cstate == SA_CSTATE_OPEN) { + if (subagentx_object_close(sao) == -1) + return; + } + if (sao->sao_cstate == SA_CSTATE_CLOSE) + subagentx_object_free_finalize(sao); +} + +static void +subagentx_object_free_finalize(struct subagentx_object *sao) +{ +#ifdef AGENTX_DEBUG + struct subagentx *sa = sao->sao_sar->sar_sac->sac_sas->sas_sa; +#endif + size_t i, j; + int found; + +#ifdef AGENTX_DEBUG + if (sao->sao_dstate != SA_DSTATE_CLOSE) + subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, + "%s: unexpected free", __func__); +#endif + + if (sao->sao_lock != 0) { +#ifdef AGENTX_DEBUG + if (TAILQ_EMPTY(&(sa->sa_getreqs))) + subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, + "%s: %s sao_lock == %u", __func__, + agentx_oid2string(&(sao->sao_oid)), sao->sao_lock); +#endif + return; + } + + RB_REMOVE(sac_objects, &(sao->sao_sar->sar_sac->sac_objects), sao); + TAILQ_REMOVE(&(sao->sao_sar->sar_objects), sao, sao_sar_objects); + + for (i = 0; i < sao->sao_indexlen; i++) { + found = 0; + for (j = 0; j < sao->sao_index[i]->sai_objectlen; j++) { + if (sao->sao_index[i]->sai_object[j] == sao) + found = 1; + if (found && j + 1 != sao->sao_index[i]->sai_objectlen) + sao->sao_index[i]->sai_object[j] = + sao->sao_index[i]->sai_object[j + 1]; + } +#ifdef AGENTX_DEBUG + if (!found) + subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, + "%s: object not found in index", __func__); +#endif + sao->sao_index[i]->sai_objectlen--; + if (sao->sao_index[i]->sai_dstate == SA_DSTATE_CLOSE) + subagentx_index_free_finalize(sao->sao_index[i]); + } + + free(sao); +} + +static void +subagentx_object_reset(struct subagentx_object *sao) +{ + sao->sao_cstate = SA_CSTATE_CLOSE; + + if (sao->sao_dstate == SA_DSTATE_CLOSE) + subagentx_object_free_finalize(sao); +} + +static int +subagentx_object_cmp(struct subagentx_object *o1, struct subagentx_object *o2) +{ + return agentx_oid_cmp(&(o1->sao_oid), &(o2->sao_oid)); +} + +static int +subagentx_object_implied(struct subagentx_object *sao, + struct subagentx_index *sai) +{ + size_t i = 0; + + for (i = 0; i < sao->sao_indexlen; i++) { + if (sao->sao_index[i] == sai) { + if (sai->sai_vb.avb_data.avb_ostring.aos_slen != 0) + return 1; + else if (i == sao->sao_indexlen - 1) + return sao->sao_implied; + return 0; + } + } +#ifdef AGENTX_DEBUG + subagentx_log_sac_fatalx(sao->sao_sar->sar_sac, "%s: unsupported index", + __func__); +#endif + return 0; +} + +static void +subagentx_get_start(struct subagentx_context *sac, struct agentx_pdu *pdu) +{ + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + struct subagentx_get *sag, tsag; + struct agentx_pdu_searchrangelist *srl; + char *logmsg = NULL; + size_t i, j; + int fail = 0; + + if ((sag = calloc(1, sizeof(*sag))) == NULL) { + tsag.sag_sessionid = pdu->ap_header.aph_sessionid; + tsag.sag_transactionid = pdu->ap_header.aph_transactionid; + tsag.sag_packetid = pdu->ap_header.aph_packetid; + tsag.sag_context_default = sac->sac_name_default; + tsag.sag_fd = sac->sac_sas->sas_sa->sa_fd; + subagentx_log_sag_warn(&tsag, "Couldn't parse request"); + subagentx_reset(sa); + return; + } + + sag->sag_sessionid = pdu->ap_header.aph_sessionid; + sag->sag_transactionid = pdu->ap_header.aph_transactionid; + sag->sag_packetid = pdu->ap_header.aph_packetid; + sag->sag_context_default = sac->sac_name_default; + sag->sag_fd = sac->sac_sas->sas_sa->sa_fd; + if (!sac->sac_name_default) { + sag->sag_context.aos_string = + (unsigned char *)strdup((char *)sac->sac_name.aos_string); + if (sag->sag_context.aos_string == NULL) { + subagentx_log_sag_warn(sag, "Couldn't parse request"); + free(sag); + subagentx_reset(sa); + return; + } + } + sag->sag_context.aos_slen = sac->sac_name.aos_slen; + sag->sag_type = pdu->ap_header.aph_type; + sag->sag_sac = sac; + TAILQ_INSERT_TAIL(&(sa->sa_getreqs), sag, sag_sa_getreqs); + if (sag->sag_type == AGENTX_PDU_TYPE_GET || + sag->sag_type == AGENTX_PDU_TYPE_GETNEXT) { + srl = &(pdu->ap_payload.ap_srl); + sag->sag_nvarbind = srl->ap_nsr; + } else { + sag->sag_nonrep = pdu->ap_payload.ap_getbulk.ap_nonrep; + sag->sag_maxrep = pdu->ap_payload.ap_getbulk.ap_maxrep; + srl = &(pdu->ap_payload.ap_getbulk.ap_srl); + sag->sag_nvarbind = ((srl->ap_nsr - sag->sag_nonrep) * + sag->sag_maxrep) + sag->sag_nonrep; + } + + if ((sag->sag_varbind = calloc(sag->sag_nvarbind, + sizeof(*(sag->sag_varbind)))) == NULL) { + subagentx_log_sag_warn(sag, "Couldn't parse request"); + subagentx_get_free(sag); + subagentx_reset(sa); + return; + } + + /* XXX net-snmp doesn't use getbulk, so untested */ + /* Two loops: varbind after needs to be initialized */ + for (i = 0; i < srl->ap_nsr; i++) { + if (i < sag->sag_nonrep || + sag->sag_type != AGENTX_PDU_TYPE_GETBULK) + j = i; + else if (sag->sag_maxrep == 0) + break; + else + j = (sag->sag_maxrep * i) + sag->sag_nonrep; + bcopy(&(srl->ap_sr[i].asr_start), + &(sag->sag_varbind[j].sav_vb.avb_oid), + sizeof(srl->ap_sr[i].asr_start)); + bcopy(&(srl->ap_sr[i].asr_start), + &(sag->sag_varbind[j].sav_start), + sizeof(srl->ap_sr[i].asr_start)); + bcopy(&(srl->ap_sr[i].asr_stop), + &(sag->sag_varbind[j].sav_end), + sizeof(srl->ap_sr[i].asr_stop)); + sag->sag_varbind[j].sav_initialized = 1; + sag->sag_varbind[j].sav_sag = sag; + sag->sag_varbind[j].sav_include = + srl->ap_sr[i].asr_start.aoi_include; + if (j == 0) + fail |= subagentx_strcat(&logmsg, " {"); + else + fail |= subagentx_strcat(&logmsg, ",{"); + fail |= subagentx_strcat(&logmsg, + agentx_oid2string(&(srl->ap_sr[i].asr_start))); + if (srl->ap_sr[i].asr_start.aoi_include) + fail |= subagentx_strcat(&logmsg, " (inclusive)"); + if (srl->ap_sr[i].asr_stop.aoi_idlen != 0) { + fail |= subagentx_strcat(&logmsg, " - "); + fail |= subagentx_strcat(&logmsg, + agentx_oid2string(&(srl->ap_sr[i].asr_stop))); + } + fail |= subagentx_strcat(&logmsg, "}"); + if (fail) { + subagentx_log_sag_warn(sag, "Couldn't parse request"); + free(logmsg); + subagentx_get_free(sag); + subagentx_reset(sa); + return; + } + } + + subagentx_log_sag_debug(sag, "%s:%s", + agentx_pdutype2string(sag->sag_type), logmsg); + free(logmsg); + + for (i = 0; i < srl->ap_nsr; i++) { + if (i < sag->sag_nonrep || + sag->sag_type != AGENTX_PDU_TYPE_GETBULK) + j = i; + else if (sag->sag_maxrep == 0) + break; + else + j = (sag->sag_maxrep * i) + sag->sag_nonrep; + subagentx_varbind_start(&(sag->sag_varbind[j])); + } +} + +static void +subagentx_get_finalize(struct subagentx_get *sag) +{ + struct subagentx_context *sac = sag->sag_sac; + struct subagentx_session *sas = sac->sac_sas; + struct subagentx *sa = sas->sas_sa; + size_t i, j, nvarbind = 0; + uint16_t error = 0, index = 0; + struct agentx_varbind *vbl; + char *logmsg = NULL; + int fail = 0; + + for (i = 0; i < sag->sag_nvarbind; i++) { + if (sag->sag_varbind[i].sav_initialized) { + if (sag->sag_varbind[i].sav_vb.avb_type == 0) + return; + nvarbind++; + } + } + + if (sag->sag_sac == NULL) { + subagentx_get_free(sag); + return; + } + + if ((vbl = calloc(nvarbind, sizeof(*vbl))) == NULL) { + subagentx_log_sag_warn(sag, "Couldn't parse request"); + subagentx_get_free(sag); + subagentx_reset(sa); + return; + } + for (i = 0, j = 0; i < sag->sag_nvarbind; i++) { + if (sag->sag_varbind[i].sav_initialized) { + memcpy(&(vbl[j]), &(sag->sag_varbind[i].sav_vb), + sizeof(*vbl)); + if (error == 0 && sag->sag_varbind[i].sav_error != + AGENTX_PDU_ERROR_NOERROR) { + error = sag->sag_varbind[i].sav_error; + index = j + 1; + } + if (j == 0) + fail |= subagentx_strcat(&logmsg, " {"); + else + fail |= subagentx_strcat(&logmsg, ",{"); + fail |= subagentx_strcat(&logmsg, + agentx_varbind2string(&(vbl[j]))); + if (sag->sag_varbind[i].sav_error != + AGENTX_PDU_ERROR_NOERROR) { + fail |= subagentx_strcat(&logmsg, "("); + fail |= subagentx_strcat(&logmsg, + agentx_error2string( + sag->sag_varbind[i].sav_error)); + fail |= subagentx_strcat(&logmsg, ")"); + } + fail |= subagentx_strcat(&logmsg, "}"); + if (fail) { + subagentx_log_sag_warn(sag, + "Couldn't parse request"); + free(logmsg); + subagentx_get_free(sag); + return; + } + j++; + } + } + subagentx_log_sag_debug(sag, "response:%s", logmsg); + free(logmsg); + + if (agentx_response(sa->sa_ax, sas->sas_id, sag->sag_transactionid, + sag->sag_packetid, SUBAGENTX_CONTEXT_CTX(sac), 0, error, index, + vbl, nvarbind) == -1) { + subagentx_log_sag_warn(sag, "Couldn't parse request"); + subagentx_reset(sa); + } else + subagentx_wantwrite(sa, sa->sa_fd); + free(vbl); + subagentx_get_free(sag); +} + +void +subagentx_get_free(struct subagentx_get *sag) +{ + struct subagentx_varbind *sav; + struct subagentx_object *sao; + struct subagentx *sa = sag->sag_sac->sac_sas->sas_sa; + struct subagentx_varbind_index *index; + size_t i, j; + + if (sag->sag_sac != NULL) + TAILQ_REMOVE(&(sa->sa_getreqs), sag, sag_sa_getreqs); + + for (i = 0; i < sag->sag_nvarbind; i++) { + sav = &(sag->sag_varbind[i]); + for (j = 0; sav->sav_sao != NULL && + j < sav->sav_sao->sao_indexlen; j++) { + sao = sav->sav_sao; + index = &(sav->sav_index[j]); + if (sao->sao_index[j]->sai_vb.avb_type == + AGENTX_DATA_TYPE_OCTETSTRING) + free(index->sav_idata.avb_ostring.aos_string); + } + agentx_varbind_free(&(sag->sag_varbind[i].sav_vb)); + } + + free(sag->sag_context.aos_string); + free(sag->sag_varbind); + free(sag); +} + +static void +subagentx_varbind_start(struct subagentx_varbind *sav) +{ + struct subagentx_get *sag = sav->sav_sag; + struct subagentx_context *sac = sag->sag_sac; + struct subagentx_object *sao, sao_search; + struct subagentx_varbind_index *index; + struct subagentx_index *sai; + struct agentx_oid *oid; + union agentx_data *data; + struct in_addr *ipaddress; + unsigned char *ipbytes; + size_t i, j, k; + int overflow = 0, dynamic; + +#ifdef AGENTX_DEBUG + if (!sav->sav_initialized) + subagentx_log_sag_fatalx(sav->sav_sag, + "%s: sav_initialized not set", __func__); +#endif + + bcopy(&(sav->sav_vb.avb_oid), &(sao_search.sao_oid), + sizeof(sao_search.sao_oid)); + + do { + sao = RB_FIND(sac_objects, &(sac->sac_objects), &sao_search); + if (sao_search.sao_oid.aoi_idlen > 0) + sao_search.sao_oid.aoi_idlen--; + } while (sao == NULL && sao_search.sao_oid.aoi_idlen > 0); + if (sao == NULL || sao->sao_cstate != SA_CSTATE_OPEN) { + sav->sav_include = 1; + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) { + subagentx_varbind_nosuchobject(sav); + return; + } + bcopy(&(sav->sav_vb.avb_oid), &(sao_search.sao_oid), + sizeof(sao_search.sao_oid)); + sao = RB_NFIND(sac_objects, &(sac->sac_objects), &sao_search); +getnext: + while (sao != NULL && sao->sao_cstate != SA_CSTATE_OPEN) + sao = RB_NEXT(sac_objects, &(sac->sac_objects), sao); + if (sao == NULL) { + subagentx_varbind_endofmibview(sav); + return; + } + bcopy(&(sao->sao_oid), &(sav->sav_vb.avb_oid), + sizeof(sao->sao_oid)); + } + sav->sav_sao = sao; + sav->sav_indexlen = sao->sao_indexlen; + if (subagentx_object_lock(sao) == -1) { + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + + oid = &(sav->sav_vb.avb_oid); + if (sao->sao_indexlen == 0) { + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) { + if (oid->aoi_idlen != sao->sao_oid.aoi_idlen + 1 || + oid->aoi_id[oid->aoi_idlen - 1] != 0) { + subagentx_varbind_nosuchinstance(sav); + return; + } + } else { + if (oid->aoi_idlen == sao->sao_oid.aoi_idlen) { + oid->aoi_id[oid->aoi_idlen++] = 0; + sav->sav_include = 1; + } else { + sav->sav_sao = NULL; + subagentx_object_unlock(sao); + sao = RB_NEXT(sac_objects, &(sac->sac_objects), + sao); + goto getnext; + } + } + } + j = sao->sao_oid.aoi_idlen; +/* + * We can't trust what the client gives us, so sometimes we need to map it to + * index type. + * - AGENTX_PDU_TYPE_GET: we always return AGENTX_DATA_TYPE_NOSUCHINSTANCE + * - AGENTX_PDU_TYPE_GETNEXT: + * - Missing OID digits to match indices will result in the indices to be NUL- + * initialized and the request type will be set to + * SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE + * - An overflow can happen on AGENTX_DATA_TYPE_OCTETSTRING and + * AGENTX_DATA_TYPE_IPADDRESS. This results in request type being set to + * SUBAGENTX_REQUEST_TYPE_GETNEXT and will set the index to its maximum + * value: + * - AGENTX_DATA_TYPE_INTEGER: UINT32_MAX + * - AGENTX_DATA_TYPE_OCTETSTRING: aos_slen = UINT32_MAX and + * aos_string = NULL + * - AGENTX_DATA_TYPE_OID: aoi_idlen = UINT32_MAX and aoi_id[x] = UINT32_MAX + * - AGENTX_DATA_TYPE_IPADDRESS: 255.255.255.255 + */ + for (dynamic = 0, i = 0; i < sao->sao_indexlen; i++) { + index = &(sav->sav_index[i]); + index->sav_sai = sao->sao_index[i]; + data = &(index->sav_idata); + if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) + dynamic = 1; + if (j >= sav->sav_vb.avb_oid.aoi_idlen && !overflow && + sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) + continue; + switch (sao->sao_index[i]->sai_vb.avb_type) { + case AGENTX_DATA_TYPE_INTEGER: +/* Dynamic index: normal copy paste */ + if (sao->sao_index[i]->sai_type == SAI_TYPE_DYNAMIC) { + data->avb_uint32 = overflow ? + UINT32_MAX : sav->sav_vb.avb_oid.aoi_id[j]; + j++; + index->sav_idatacomplete = 1; + break; + } + sai = sao->sao_index[i]; +/* With a GET-request we need an exact match */ + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) { + if (sai->sai_vb.avb_data.avb_uint32 != + sav->sav_vb.avb_oid.aoi_id[j]) { + subagentx_varbind_nosuchinstance(sav); + return; + } + index->sav_idatacomplete = 1; + j++; + break; + } +/* A higher value automatically moves us to the next value */ + if (overflow || + sav->sav_vb.avb_oid.aoi_id[j] > + sai->sai_vb.avb_data.avb_uint32) { +/* If we're !dynamic up until now the rest of the oid doesn't matter */ + if (!dynamic) { + subagentx_varbind_endofmibview(sav); + return; + } +/* + * Else we just pick the max value and make sure we don't return + * SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE + */ + data->avb_uint32 = UINT32_MAX; + index->sav_idatacomplete = 1; + overflow = 1; + j++; + break; +/* + * A lower value automatically moves to the set value and counts as a short oid + */ + } else if (sav->sav_vb.avb_oid.aoi_id[j] < + sai->sai_vb.avb_data.avb_uint32) { + data->avb_uint32 = + sai->sai_vb.avb_data.avb_uint32; + j = sav->sav_vb.avb_oid.aoi_idlen; + break; + } +/* Normal match, except we already matched overflow at higher value */ + data->avb_uint32 = sav->sav_vb.avb_oid.aoi_id[j]; + j++; + index->sav_idatacomplete = 1; + break; + case AGENTX_DATA_TYPE_OCTETSTRING: + if (!subagentx_object_implied(sao, index->sav_sai)) { + if (overflow || sav->sav_vb.avb_oid.aoi_id[j] > + SUBAGENTX_OID_MAX_LEN - + sav->sav_vb.avb_oid.aoi_idlen) { + overflow = 1; + data->avb_ostring.aos_slen = UINT32_MAX; + index->sav_idatacomplete = 1; + continue; + } + data->avb_ostring.aos_slen = + sav->sav_vb.avb_oid.aoi_id[j++]; + } else { + if (overflow) { + data->avb_ostring.aos_slen = UINT32_MAX; + index->sav_idatacomplete = 1; + continue; + } + data->avb_ostring.aos_slen = + sav->sav_vb.avb_oid.aoi_idlen - j; + } + data->avb_ostring.aos_string = + calloc(data->avb_ostring.aos_slen + 1, 1); + if (data->avb_ostring.aos_string == NULL) { + subagentx_log_sag_warn(sag, + "Failed to bind string index"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + for (k = 0; k < data->avb_ostring.aos_slen; k++, j++) { + if (!overflow && + j == sav->sav_vb.avb_oid.aoi_idlen) + break; + + if (sav->sav_vb.avb_oid.aoi_id[j] > 255) + overflow = 1; + + data->avb_ostring.aos_string[k] = overflow ? + 0xff : sav->sav_vb.avb_oid.aoi_id[j]; + } + if (k == data->avb_ostring.aos_slen) + index->sav_idatacomplete = 1; + break; + case AGENTX_DATA_TYPE_OID: + if (!subagentx_object_implied(sao, index->sav_sai)) { + if (overflow || sav->sav_vb.avb_oid.aoi_id[j] > + SUBAGENTX_OID_MAX_LEN - + sav->sav_vb.avb_oid.aoi_idlen) { + overflow = 1; + data->avb_oid.aoi_idlen = UINT32_MAX; + index->sav_idatacomplete = 1; + continue; + } + data->avb_oid.aoi_idlen = + sav->sav_vb.avb_oid.aoi_id[j++]; + } else { + if (overflow) { + data->avb_oid.aoi_idlen = UINT32_MAX; + index->sav_idatacomplete = 1; + continue; + } + data->avb_oid.aoi_idlen = + sav->sav_vb.avb_oid.aoi_idlen - j; + } + for (k = 0; k < data->avb_oid.aoi_idlen; k++, j++) { + if (!overflow && + j == sav->sav_vb.avb_oid.aoi_idlen) { + data->avb_oid.aoi_id[k] = 0; + continue; + } + data->avb_oid.aoi_id[k] = overflow ? + UINT32_MAX : sav->sav_vb.avb_oid.aoi_id[j]; + } + if (j <= sav->sav_vb.avb_oid.aoi_idlen) + index->sav_idatacomplete = 1; + break; + case AGENTX_DATA_TYPE_IPADDRESS: + ipaddress = calloc(1, sizeof(ipaddress)); + if (ipaddress == NULL) { + subagentx_log_sag_warn(sag, + "Failed to bind ipaddress index"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + ipbytes = (unsigned char *)ipaddress; + for (k = 0; k < 4; k++, j++) { + if (!overflow && + j == sav->sav_vb.avb_oid.aoi_idlen) + break; + + if (sav->sav_vb.avb_oid.aoi_id[j] > 255) + overflow = 1; + + ipbytes[k] = overflow ? 255 : + sav->sav_vb.avb_oid.aoi_id[j]; + } + if (j <= sav->sav_vb.avb_oid.aoi_idlen) + index->sav_idatacomplete = 1; + data->avb_ostring.aos_slen = sizeof(ipaddress); + data->avb_ostring.aos_string = + (unsigned char *)ipaddress; + break; + default: +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sag, + "%s: unexpected index type", __func__); +#else + subagentx_log_sag_warnx(sag, + "%s: unexpected index type", __func__); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 1); + return; +#endif + } + } + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) { + if ((sao->sao_indexlen > 0 && + !sav->sav_index[sao->sao_indexlen - 1].sav_idatacomplete) || + j != sav->sav_vb.avb_oid.aoi_idlen || overflow) { + subagentx_varbind_nosuchinstance(sav); + return; + } + } + + if (overflow || j > sav->sav_vb.avb_oid.aoi_idlen) + sav->sav_include = 0; + +/* + * SUBAGENTX_REQUEST_TYPE_GETNEXT request can !dynamic objects can just move to + * the next object + */ + if (subagentx_varbind_request(sav) == SUBAGENTX_REQUEST_TYPE_GETNEXT && + !dynamic) { + subagentx_varbind_endofmibview(sav); + return; + } + + sao->sao_get(sav); +} + +void +subagentx_varbind_integer(struct subagentx_varbind *sav, uint32_t value) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_INTEGER; + sav->sav_vb.avb_data.avb_uint32 = value; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_string(struct subagentx_varbind *sav, const char *value) +{ + subagentx_varbind_nstring(sav, (const unsigned char *)value, + strlen(value)); +} + +void +subagentx_varbind_nstring(struct subagentx_varbind *sav, + const unsigned char *value, size_t slen) +{ + sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(slen); + if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) { + subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind string"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING; + memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, value, slen); + sav->sav_vb.avb_data.avb_ostring.aos_slen = slen; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_printf(struct subagentx_varbind *sav, const char *fmt, ...) +{ + va_list ap; + int r; + + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OCTETSTRING; + va_start(ap, fmt); + r = vasprintf((char **)&(sav->sav_vb.avb_data.avb_ostring.aos_string), + fmt, ap); + va_end(ap); + if (r == -1) { + sav->sav_vb.avb_data.avb_ostring.aos_string = NULL; + subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind string"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + sav->sav_vb.avb_data.avb_ostring.aos_slen = r; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_null(struct subagentx_varbind *sav) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NULL; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_oid(struct subagentx_varbind *sav, const uint32_t oid[], + size_t oidlen) +{ + size_t i; + + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OID; + + for (i = 0; i < oidlen; i++) + sav->sav_vb.avb_data.avb_oid.aoi_id[i] = oid[i]; + sav->sav_vb.avb_data.avb_oid.aoi_idlen = oidlen; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_object(struct subagentx_varbind *sav, + struct subagentx_object *sao) +{ + subagentx_varbind_oid(sav, sao->sao_oid.aoi_id, + sao->sao_oid.aoi_idlen); +} + +void +subagentx_varbind_index(struct subagentx_varbind *sav, + struct subagentx_index *sai) +{ + subagentx_varbind_oid(sav, sai->sai_vb.avb_oid.aoi_id, + sai->sai_vb.avb_oid.aoi_idlen); +} + + +void +subagentx_varbind_ipaddress(struct subagentx_varbind *sav, + const struct in_addr *value) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_IPADDRESS; + sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(4); + if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) { + subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind ipaddress"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, value, 4); + sav->sav_vb.avb_data.avb_ostring.aos_slen = 4; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_counter32(struct subagentx_varbind *sav, uint32_t value) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_COUNTER32; + sav->sav_vb.avb_data.avb_uint32 = value; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_gauge32(struct subagentx_varbind *sav, uint32_t value) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_GAUGE32; + sav->sav_vb.avb_data.avb_uint32 = value; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_timeticks(struct subagentx_varbind *sav, uint32_t value) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_TIMETICKS; + sav->sav_vb.avb_data.avb_uint32 = value; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_opaque(struct subagentx_varbind *sav, const char *string, + size_t strlen) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_OPAQUE; + sav->sav_vb.avb_data.avb_ostring.aos_string = malloc(strlen); + if (sav->sav_vb.avb_data.avb_ostring.aos_string == NULL) { + subagentx_log_sag_warn(sav->sav_sag, "Couldn't bind opaque"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 1); + return; + } + memcpy(sav->sav_vb.avb_data.avb_ostring.aos_string, string, strlen); + sav->sav_vb.avb_data.avb_ostring.aos_slen = strlen; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_counter64(struct subagentx_varbind *sav, uint64_t value) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_COUNTER64; + sav->sav_vb.avb_data.avb_uint64 = value; + + subagentx_varbind_finalize(sav); +} + +void +subagentx_varbind_notfound(struct subagentx_varbind *sav) +{ + if (sav->sav_indexlen == 0) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "%s invalid call", + __func__); +#else + subagentx_log_sag_warnx(sav->sav_sag, "%s invalid call", + __func__); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_GENERR, 1); +#endif + } else if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) + subagentx_varbind_nosuchinstance(sav); + else + subagentx_varbind_endofmibview(sav); +} + +void +subagentx_varbind_error(struct subagentx_varbind *sav) +{ + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 1); +} + +static void +subagentx_varbind_error_type(struct subagentx_varbind *sav, + enum agentx_pdu_error error, int done) +{ + if (sav->sav_error == AGENTX_PDU_ERROR_NOERROR) { + sav->sav_error = error; + } + + if (done) { + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NULL; + + subagentx_varbind_finalize(sav); + } +} + +static void +subagentx_varbind_finalize(struct subagentx_varbind *sav) +{ + struct subagentx_get *sag = sav->sav_sag; + struct agentx_oid oid; + union agentx_data *data; + size_t i, j; + int cmp; + + if (sav->sav_error != AGENTX_PDU_ERROR_NOERROR) { + bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid), + sizeof(sav->sav_start)); + goto done; + } + bcopy(&(sav->sav_sao->sao_oid), &oid, sizeof(oid)); + if (sav->sav_indexlen == 0) + agentx_oid_add(&oid, 0); + for (i = 0; i < sav->sav_indexlen; i++) { + data = &(sav->sav_index[i].sav_idata); + switch (sav->sav_index[i].sav_sai->sai_vb.avb_type) { + case AGENTX_DATA_TYPE_INTEGER: + if (agentx_oid_add(&oid, data->avb_uint32) == -1) + goto fail; + break; + case AGENTX_DATA_TYPE_OCTETSTRING: + if (!subagentx_object_implied(sav->sav_sao, + sav->sav_index[i].sav_sai)) { + if (agentx_oid_add(&oid, + data->avb_ostring.aos_slen) == -1) + goto fail; + } + for (j = 0; j < data->avb_ostring.aos_slen; j++) { + if (agentx_oid_add(&oid, + (uint8_t)data->avb_ostring.aos_string[j]) == + -1) + goto fail; + } + break; + case AGENTX_DATA_TYPE_OID: + if (!subagentx_object_implied(sav->sav_sao, + sav->sav_index[i].sav_sai)) { + if (agentx_oid_add(&oid, + data->avb_oid.aoi_idlen) == -1) + goto fail; + } + for (j = 0; j < data->avb_oid.aoi_idlen; j++) { + if (agentx_oid_add(&oid, + data->avb_oid.aoi_id[j]) == -1) + goto fail; + } + break; + case AGENTX_DATA_TYPE_IPADDRESS: + for (j = 0; j < 4; j++) { + if (agentx_oid_add(&oid, + data->avb_ostring.aos_string == NULL ? 0 : + (uint8_t)data->avb_ostring.aos_string[j]) == + -1) + goto fail; + } + break; + default: +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sag, + "%s: unsupported index type", __func__); +#else + bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid), + sizeof(sav->sav_start)); + sav->sav_error = AGENTX_PDU_ERROR_PROCESSINGERROR; + subagentx_object_unlock(sav->sav_sao); + subagentx_get_finalize(sav->sav_sag); + return; +#endif + } + } + cmp = agentx_oid_cmp(&(sav->sav_vb.avb_oid), &oid); + if ((subagentx_varbind_request(sav) == SUBAGENTX_REQUEST_TYPE_GETNEXT && + cmp >= 0) || cmp > 0) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sag, "indices not incremented"); +#else + subagentx_log_sag_warnx(sag, "indices not incremented"); + bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid), + sizeof(sav->sav_start)); + sav->sav_error = AGENTX_PDU_ERROR_GENERR; +#endif + } else + bcopy(&oid, &(sav->sav_vb.avb_oid), sizeof(oid)); +done: + subagentx_object_unlock(sav->sav_sao); + subagentx_get_finalize(sav->sav_sag); + return; + +fail: + subagentx_log_sag_warnx(sag, "oid too large"); + bcopy(&(sav->sav_start), &(sav->sav_vb.avb_oid), + sizeof(sav->sav_start)); + sav->sav_error = AGENTX_PDU_ERROR_GENERR; + subagentx_object_unlock(sav->sav_sao); + subagentx_get_finalize(sav->sav_sag); + +} + +static void +subagentx_varbind_nosuchobject(struct subagentx_varbind *sav) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NOSUCHOBJECT; + + if (sav->sav_sao != NULL) + subagentx_object_unlock(sav->sav_sao); + subagentx_get_finalize(sav->sav_sag); +} + +static void +subagentx_varbind_nosuchinstance(struct subagentx_varbind *sav) +{ + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_NOSUCHINSTANCE; + + if (sav->sav_sao != NULL) + subagentx_object_unlock(sav->sav_sao); + subagentx_get_finalize(sav->sav_sag); +} + +static void +subagentx_varbind_endofmibview(struct subagentx_varbind *sav) +{ + struct subagentx_object *sao; + struct agentx_varbind *vb; + size_t i; + +#ifdef AGENTX_DEBUG + if (sav->sav_sag->sag_type != AGENTX_PDU_TYPE_GETNEXT && + sav->sav_sag->sag_type != AGENTX_PDU_TYPE_GETBULK) + subagentx_log_sag_fatalx(sav->sav_sag, + "%s: invalid request type", __func__); +#endif + + if (sav->sav_sao != NULL && + (sao = RB_NEXT(sac_objects, &(sac->sac_objects), + sav->sav_sao)) != NULL && + agentx_oid_cmp(&(sao->sao_oid), &(sav->sav_end)) < 0) { + bcopy(&(sao->sao_oid), &(sav->sav_vb.avb_oid), + sizeof(sao->sao_oid)); + sav->sav_include = 1; + for (i = 0; i < sav->sav_indexlen; i++) { + vb = &(sav->sav_index[i].sav_sai->sai_vb); + if (vb->avb_type == AGENTX_DATA_TYPE_OCTETSTRING || + vb->avb_type == AGENTX_DATA_TYPE_IPADDRESS) + free(vb->avb_data.avb_ostring.aos_string); + } + bzero(&(sav->sav_index), sizeof(sav->sav_index)); + subagentx_object_unlock(sav->sav_sao); + subagentx_varbind_start(sav); + return; + } + + sav->sav_vb.avb_type = AGENTX_DATA_TYPE_ENDOFMIBVIEW; + + if (sav->sav_sao != NULL) + subagentx_object_unlock(sav->sav_sao); + subagentx_get_finalize(sav->sav_sag); +} + +enum subagentx_request_type +subagentx_varbind_request(struct subagentx_varbind *sav) +{ + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET) + return SUBAGENTX_REQUEST_TYPE_GET; + if (sav->sav_include || + (sav->sav_indexlen > 0 && + !sav->sav_index[sav->sav_indexlen - 1].sav_idatacomplete)) + return SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE; + return SUBAGENTX_REQUEST_TYPE_GETNEXT; +} + +struct subagentx_object * +subagentx_varbind_get_object(struct subagentx_varbind *sav) +{ + return sav->sav_sao; +} + +uint32_t +subagentx_varbind_get_index_integer(struct subagentx_varbind *sav, + struct subagentx_index *sai) +{ + size_t i; + + if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_INTEGER) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + return 0; +#endif + } + + for (i = 0; i < sav->sav_indexlen; i++) { + if (sav->sav_index[i].sav_sai == sai) + return sav->sav_index[i].sav_idata.avb_uint32; + } +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + return 0; +#endif +} + +const unsigned char * +subagentx_varbind_get_index_string(struct subagentx_varbind *sav, + struct subagentx_index *sai, size_t *slen, int *implied) +{ + struct subagentx_varbind_index *index; + size_t i; + + if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OCTETSTRING) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + *slen = 0; + *implied = 0; + return NULL; +#endif + } + + for (i = 0; i < sav->sav_indexlen; i++) { + if (sav->sav_index[i].sav_sai == sai) { + index = &(sav->sav_index[i]); + *slen = index->sav_idata.avb_ostring.aos_slen; + *implied = subagentx_object_implied(sav->sav_sao, sai); + return index->sav_idata.avb_ostring.aos_string; + } + } + +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + *slen = 0; + *implied = 0; + return NULL; +#endif +} + +const uint32_t * +subagentx_varbind_get_index_oid(struct subagentx_varbind *sav, + struct subagentx_index *sai, size_t *oidlen, int *implied) +{ + struct subagentx_varbind_index *index; + size_t i; + + if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OID) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + *oidlen = 0; + *implied = 0; + return NULL; +#endif + } + + for (i = 0; i < sav->sav_indexlen; i++) { + if (sav->sav_index[i].sav_sai == sai) { + index = &(sav->sav_index[i]); + *oidlen = index->sav_idata.avb_oid.aoi_idlen; + *implied = subagentx_object_implied(sav->sav_sao, sai); + return index->sav_idata.avb_oid.aoi_id; + } + } + +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + *oidlen = 0; + *implied = 0; + return NULL; +#endif +} + +const struct in_addr * +subagentx_varbind_get_index_ipaddress(struct subagentx_varbind *sav, + struct subagentx_index *sai) +{ + static struct in_addr nuladdr = {0}; + struct subagentx_varbind_index *index; + size_t i; + + if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_IPADDRESS) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + return NULL; +#endif + } + + for (i = 0; i < sav->sav_indexlen; i++) { + if (sav->sav_index[i].sav_sai == sai) { + index = &(sav->sav_index[i]); + if (index->sav_idata.avb_ostring.aos_string == NULL) + return &nuladdr; + return (struct in_addr *) + index->sav_idata.avb_ostring.aos_string; + } + } + +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + return NULL; +#endif +} + +void +subagentx_varbind_set_index_integer(struct subagentx_varbind *sav, + struct subagentx_index *sai, uint32_t value) +{ + size_t i; + + if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_INTEGER) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + + for (i = 0; i < sav->sav_indexlen; i++) { + if (sav->sav_index[i].sav_sai == sai) { + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET && + sav->sav_index[i].sav_idata.avb_uint32 != value) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, + "can't change index on GET"); +#else + subagentx_log_sag_warnx(sav->sav_sag, + "can't change index on GET"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + sav->sav_index[i].sav_idata.avb_uint32 = value; + return; + } + } +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); +#endif +} + +void +subagentx_varbind_set_index_string(struct subagentx_varbind *sav, + struct subagentx_index *sai, const char *value) +{ + subagentx_varbind_set_index_nstring(sav, sai, + (const unsigned char *)value, strlen(value)); +} + +void +subagentx_varbind_set_index_nstring(struct subagentx_varbind *sav, + struct subagentx_index *sai, const unsigned char *value, size_t slen) +{ + struct agentx_ostring *curvalue; + unsigned char *nstring; + size_t i; + + if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OCTETSTRING) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + + for (i = 0; i < sav->sav_indexlen; i++) { + if (sav->sav_index[i].sav_sai == sai) { + if (sai->sai_vb.avb_data.avb_ostring.aos_slen != 0 && + sai->sai_vb.avb_data.avb_ostring.aos_slen != slen) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, + "invalid string length on explicit length " + "string"); +#else + subagentx_log_sag_warnx(sav->sav_sag, + "invalid string length on explicit length " + "string"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + curvalue = &(sav->sav_index[i].sav_idata.avb_ostring); + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET && + (curvalue->aos_slen != slen || + memcmp(curvalue->aos_string, value, slen) != 0)) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, + "can't change index on GET"); +#else + subagentx_log_sag_warnx(sav->sav_sag, + "can't change index on GET"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + if ((nstring = recallocarray(curvalue->aos_string, + curvalue->aos_slen + 1, slen + 1, 1)) == NULL) { + subagentx_log_sag_warn(sav->sav_sag, + "Failed to bind string index"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 0); + return; + } + curvalue->aos_string = nstring; + memcpy(nstring, value, slen); + curvalue->aos_slen = slen; + return; + } + } +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); +#endif +} + +void +subagentx_varbind_set_index_oid(struct subagentx_varbind *sav, + struct subagentx_index *sai, const uint32_t *value, size_t oidlen) +{ + struct agentx_oid *curvalue, oid; + size_t i; + + if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_OID) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + + for (i = 0; i < sav->sav_indexlen; i++) { + if (sav->sav_index[i].sav_sai == sai) { + if (sai->sai_vb.avb_data.avb_oid.aoi_idlen != 0 && + sai->sai_vb.avb_data.avb_oid.aoi_idlen != oidlen) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, + "invalid oid length on explicit length " + "oid"); +#else + subagentx_log_sag_warnx(sav->sav_sag, + "invalid oid length on explicit length " + "oid"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + curvalue = &(sav->sav_index[i].sav_idata.avb_oid); + for (i = 0; i < oidlen; i++) + oid.aoi_id[i] = value[i]; + oid.aoi_idlen = oidlen; + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET && + agentx_oid_cmp(&oid, curvalue) != 0) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, + "can't change index on GET"); +#else + subagentx_log_sag_warnx(sav->sav_sag, + "can't change index on GET"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + for (i = 0; i < oidlen; i++) + curvalue->aoi_id[i] = value[i]; + curvalue->aoi_idlen = oidlen; + return; + } + } +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); +#endif +} + +void +subagentx_varbind_set_index_object(struct subagentx_varbind *sav, + struct subagentx_index *sai, struct subagentx_object *sao) +{ + subagentx_varbind_set_index_oid(sav, sai, sao->sao_oid.aoi_id, + sao->sao_oid.aoi_idlen); +} + +void +subagentx_varbind_set_index_ipaddress(struct subagentx_varbind *sav, + struct subagentx_index *sai, const struct in_addr *addr) +{ + struct agentx_ostring *curvalue; + size_t i; + + if (sai->sai_vb.avb_type != AGENTX_DATA_TYPE_IPADDRESS) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index type"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index type"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + + for (i = 0; i < sav->sav_indexlen; i++) { + if (sav->sav_index[i].sav_sai == sai) { + curvalue = &(sav->sav_index[i].sav_idata.avb_ostring); + if (curvalue->aos_string == NULL) + curvalue->aos_string = calloc(1, sizeof(*addr)); + if (curvalue->aos_string == NULL) { + subagentx_log_sag_warn(sav->sav_sag, + "Failed to bind ipaddress index"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_PROCESSINGERROR, 0); + return; + } + if (sav->sav_sag->sag_type == AGENTX_PDU_TYPE_GET && + memcmp(addr, curvalue->aos_string, + sizeof(*addr)) != 0) { +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, + "can't change index on GET"); +#else + subagentx_log_sag_warnx(sav->sav_sag, + "can't change index on GET"); + subagentx_varbind_error_type(sav, + AGENTX_PDU_ERROR_GENERR, 0); + return; +#endif + } + bcopy(addr, curvalue->aos_string, sizeof(*addr)); + return; + } + } +#ifdef AGENTX_DEBUG + subagentx_log_sag_fatalx(sav->sav_sag, "invalid index"); +#else + subagentx_log_sag_warnx(sav->sav_sag, "invalid index"); + subagentx_varbind_error_type(sav, AGENTX_PDU_ERROR_GENERR, 0); +#endif +} + +static int +subagentx_request(struct subagentx *sa, uint32_t packetid, + int (*cb)(struct agentx_pdu *, void *), void *cookie) +{ + struct subagentx_request *sar; + +#ifdef AGENTX_DEBUG + if (sa->sa_ax->ax_wblen == 0) + subagentx_log_sa_fatalx(sa, "%s: no data to be written", + __func__); +#endif + + if ((sar = calloc(1, sizeof(*sar))) == NULL) { + subagentx_log_sa_warn(sa, "couldn't create request context"); + subagentx_reset(sa); + return -1; + } + + sar->sar_packetid = packetid; + sar->sar_cb = cb; + sar->sar_cookie = cookie; + if (RB_INSERT(sa_requests, &(sa->sa_requests), sar) != NULL) { +#ifdef AGENTX_DEBUG + subagentx_log_sa_fatalx(sa, "%s: duplicate packetid", __func__); +#else + subagentx_log_sa_warnx(sa, "%s: duplicate packetid", __func__); + free(sar); + subagentx_reset(sa); + return -1; +#endif + } + + subagentx_wantwrite(sa, sa->sa_fd); + return 0; +} + +static int +subagentx_request_cmp(struct subagentx_request *r1, + struct subagentx_request *r2) +{ + return r1->sar_packetid < r2->sar_packetid ? -1 : + r1->sar_packetid > r2->sar_packetid; +} + +static int +subagentx_strcat(char **dst, const char *src) +{ + char *tmp; + size_t dstlen = 0, buflen = 0, srclen, nbuflen; + + if (*dst != NULL) { + dstlen = strlen(*dst); + buflen = ((dstlen / 512) + 1) * 512; + } + + srclen = strlen(src); + if (dst == NULL || dstlen + srclen > buflen) { + nbuflen = (((dstlen + srclen) / 512) + 1) * 512; + tmp = recallocarray(*dst, buflen, nbuflen, sizeof(*tmp)); + if (tmp == NULL) + return -1; + *dst = tmp; + buflen = nbuflen; + } + + (void)strlcat(*dst, src, buflen); + return 0; +} + +void +subagentx_read(struct subagentx *sa) +{ + struct subagentx_session *sas; + struct subagentx_context *sac; + struct subagentx_request sar_search, *sar; + struct agentx_pdu *pdu; + int error; + + if ((pdu = agentx_recv(sa->sa_ax)) == NULL) { + if (errno == EAGAIN) + return; + subagentx_log_sa_warn(sa, "lost connection"); + subagentx_reset(sa); + return; + } + + TAILQ_FOREACH(sas, &(sa->sa_sessions), sas_sa_sessions) { + if (sas->sas_id == pdu->ap_header.aph_sessionid) + break; + if (sas->sas_cstate == SA_CSTATE_WAITOPEN && + sas->sas_packetid == pdu->ap_header.aph_packetid) + break; + } + if (sas == NULL) { + subagentx_log_sa_warnx(sa, "received unexpected session: %d", + pdu->ap_header.aph_sessionid); + agentx_pdu_free(pdu); + subagentx_reset(sa); + return; + } + TAILQ_FOREACH(sac, &(sas->sas_contexts), sac_sas_contexts) { + if ((pdu->ap_header.aph_flags & + AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT) == 0 && + sac->sac_name_default == 1) + break; + if (pdu->ap_header.aph_flags & + AGENTX_PDU_FLAG_NON_DEFAULT_CONTEXT && + sac->sac_name_default == 0 && + pdu->ap_context.aos_slen == sac->sac_name.aos_slen && + memcmp(pdu->ap_context.aos_string, + sac->sac_name.aos_string, sac->sac_name.aos_slen) == 0) + break; + } + if (pdu->ap_header.aph_type != AGENTX_PDU_TYPE_RESPONSE) { + if (sac == NULL) { + subagentx_log_sa_warnx(sa, "%s: invalid context", + pdu->ap_context.aos_string); + agentx_pdu_free(pdu); + subagentx_reset(sa); + return; + } + } + + switch (pdu->ap_header.aph_type) { + case AGENTX_PDU_TYPE_GET: + case AGENTX_PDU_TYPE_GETNEXT: + case AGENTX_PDU_TYPE_GETBULK: + subagentx_get_start(sac, pdu); + break; + /* Add stubs for set functions */ + case AGENTX_PDU_TYPE_TESTSET: + case AGENTX_PDU_TYPE_COMMITSET: + case AGENTX_PDU_TYPE_UNDOSET: + if (pdu->ap_header.aph_type == AGENTX_PDU_TYPE_TESTSET) + error = AGENTX_PDU_ERROR_NOTWRITABLE; + else if (pdu->ap_header.aph_type == AGENTX_PDU_TYPE_COMMITSET) + error = AGENTX_PDU_ERROR_COMMITFAILED; + else + error = AGENTX_PDU_ERROR_UNDOFAILED; + + subagentx_log_sac_debug(sac, "unsupported call: %s", + agentx_pdutype2string(pdu->ap_header.aph_type)); + if (agentx_response(sa->sa_ax, sas->sas_id, + pdu->ap_header.aph_transactionid, + pdu->ap_header.aph_packetid, + sac == NULL ? NULL : SUBAGENTX_CONTEXT_CTX(sac), + 0, error, 1, NULL, 0) == -1) + subagentx_log_sac_warn(sac, + "transaction: %u packetid: %u: failed to send " + "reply", pdu->ap_header.aph_transactionid, + pdu->ap_header.aph_packetid); + if (sa->sa_ax->ax_wblen > 0) + subagentx_wantwrite(sa, sa->sa_fd); + break; + case AGENTX_PDU_TYPE_CLEANUPSET: + subagentx_log_sa_debug(sa, "unsupported call: %s", + agentx_pdutype2string(pdu->ap_header.aph_type)); + break; + case AGENTX_PDU_TYPE_RESPONSE: + sar_search.sar_packetid = pdu->ap_header.aph_packetid; + sar = RB_FIND(sa_requests, &(sa->sa_requests), &sar_search); + if (sar == NULL) { + if (sac == NULL) + subagentx_log_sa_warnx(sa, "received " + "response on non-request"); + else + subagentx_log_sac_warnx(sac, "received " + "response on non-request"); + break; + } + if (sac != NULL && pdu->ap_payload.ap_response.ap_error == 0) { + sac->sac_sysuptime = + pdu->ap_payload.ap_response.ap_uptime; + (void) clock_gettime(CLOCK_MONOTONIC, + &(sac->sac_sysuptimespec)); + } + RB_REMOVE(sa_requests, &(sa->sa_requests), sar); + (void) sar->sar_cb(pdu, sar->sar_cookie); + free(sar); + break; + default: + if (sac == NULL) + subagentx_log_sa_warnx(sa, "unsupported call: %s", + agentx_pdutype2string(pdu->ap_header.aph_type)); + else + subagentx_log_sac_warnx(sac, "unsupported call: %s", + agentx_pdutype2string(pdu->ap_header.aph_type)); + subagentx_reset(sa); + break; + } + agentx_pdu_free(pdu); +} + +void +subagentx_write(struct subagentx *sa) +{ + ssize_t send; + + if ((send = agentx_send(sa->sa_ax)) == -1) { + if (errno == EAGAIN) { + subagentx_wantwrite(sa, sa->sa_fd); + return; + } + subagentx_log_sa_warn(sa, "lost connection"); + subagentx_reset(sa); + return; + } + if (send > 0) + subagentx_wantwrite(sa, sa->sa_fd); +} + +RB_GENERATE_STATIC(sa_requests, subagentx_request, sar_sa_requests, + subagentx_request_cmp) +RB_GENERATE_STATIC(sac_objects, subagentx_object, sao_sac_objects, + subagentx_object_cmp) diff --git a/usr.sbin/relayd/subagentx.h b/usr.sbin/relayd/subagentx.h new file mode 100644 index 00000000000..418b86a78c1 --- /dev/null +++ b/usr.sbin/relayd/subagentx.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <netinet/in.h> + +#include <stdint.h> +#include <stddef.h> + +struct subagentx; +struct subagentx_session; +struct subagentx_context; +struct subagentx_agentcaps; +struct subagentx_region; +struct subagentx_index; +struct subagentx_object; +struct subagentx_varbind; + +enum subagentx_request_type { + SUBAGENTX_REQUEST_TYPE_GET, + SUBAGENTX_REQUEST_TYPE_GETNEXT, + SUBAGENTX_REQUEST_TYPE_GETNEXTINCLUSIVE +}; + +#define SUBAGENTX_AGENTX_MASTER "/var/agentx/master" +#define SUBAGENTX_OID_MAX_LEN 128 +#define SUBAGENTX_OID_INDEX_MAX_LEN 10 +#define SUBAGENTX_MIB2 1, 3, 6, 1, 2, 1 +#define SUBAGENTX_ENTERPRISES 1, 3, 6, 1, 4, 1 +#define SUBAGENTX_OID(...) (uint32_t []) { __VA_ARGS__ }, \ + (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t)) + +extern void (*subagentx_log_fatal)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +extern void (*subagentx_log_warn)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +extern void (*subagentx_log_info)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); +extern void (*subagentx_log_debug)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))); + +struct subagentx *subagentx(void (*)(struct subagentx *, void *, int), void *); +void subagentx_connect(struct subagentx *, int); +void subagentx_read(struct subagentx *); +void subagentx_write(struct subagentx *); +extern void (*subagentx_wantwrite)(struct subagentx *, int); +void subagentx_free(struct subagentx *); +struct subagentx_session *subagentx_session(struct subagentx *, + uint32_t[], size_t, const char *, uint8_t); +void subagentx_session_free(struct subagentx_session *); +struct subagentx_context *subagentx_context(struct subagentx_session *, + const char *); +struct subagentx_object *subagentx_context_object_find( + struct subagentx_context *, const uint32_t[], size_t, int, int); +struct subagentx_object *subagentx_context_object_nfind( + struct subagentx_context *, const uint32_t[], size_t, int, int); +uint32_t subagentx_context_uptime(struct subagentx_context *); +void subagentx_context_free(struct subagentx_context *); +struct subagentx_agentcaps *subagentx_agentcaps(struct subagentx_context *, + uint32_t[], size_t, const char *); +void subagentx_agentcaps_free(struct subagentx_agentcaps *); +struct subagentx_region *subagentx_region(struct subagentx_context *, + uint32_t[], size_t, uint8_t); +void subagentx_region_free(struct subagentx_region *); +struct subagentx_index *subagentx_index_integer_new(struct subagentx_region *, + uint32_t[], size_t); +struct subagentx_index *subagentx_index_integer_any(struct subagentx_region *, + uint32_t[], size_t); +struct subagentx_index *subagentx_index_integer_value(struct subagentx_region *, + uint32_t[], size_t, uint32_t); +struct subagentx_index *subagentx_index_integer_dynamic( + struct subagentx_region *, uint32_t[], size_t); +struct subagentx_index *subagentx_index_string_dynamic( + struct subagentx_region *, uint32_t[], size_t); +struct subagentx_index *subagentx_index_nstring_dynamic( + struct subagentx_region *, uint32_t[], size_t, size_t); +struct subagentx_index *subagentx_index_oid_dynamic(struct subagentx_region *, + uint32_t[], size_t); +struct subagentx_index *subagentx_index_noid_dynamic(struct subagentx_region *, + uint32_t[], size_t, size_t); +struct subagentx_index *subagentx_index_ipaddress_dynamic( + struct subagentx_region *, uint32_t[], size_t); +void subagentx_index_free(struct subagentx_index *); +struct subagentx_object *subagentx_object(struct subagentx_region *, uint32_t[], + size_t, struct subagentx_index *[], size_t, int, + void (*)(struct subagentx_varbind *)); +void subagentx_object_free(struct subagentx_object *); + +void subagentx_varbind_integer(struct subagentx_varbind *, uint32_t); +void subagentx_varbind_string(struct subagentx_varbind *, const char *); +void subagentx_varbind_nstring(struct subagentx_varbind *, + const unsigned char *, size_t); +void subagentx_varbind_printf(struct subagentx_varbind *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_varbind_null(struct subagentx_varbind *); +void subagentx_varbind_oid(struct subagentx_varbind *, const uint32_t[], + size_t); +void subagentx_varbind_object(struct subagentx_varbind *, + struct subagentx_object *); +void subagentx_varbind_index(struct subagentx_varbind *, + struct subagentx_index *); +void subagentx_varbind_ipaddress(struct subagentx_varbind *, + const struct in_addr *); +void subagentx_varbind_counter32(struct subagentx_varbind *, uint32_t); +void subagentx_varbind_gauge32(struct subagentx_varbind *, uint32_t); +void subagentx_varbind_timeticks(struct subagentx_varbind *, uint32_t); +void subagentx_varbind_opaque(struct subagentx_varbind *, const char *, size_t); +void subagentx_varbind_counter64(struct subagentx_varbind *, uint64_t); +void subagentx_varbind_notfound(struct subagentx_varbind *); +void subagentx_varbind_error(struct subagentx_varbind *); + +enum subagentx_request_type subagentx_varbind_request( + struct subagentx_varbind *); +struct subagentx_object * + subagentx_varbind_get_object(struct subagentx_varbind *); +uint32_t subagentx_varbind_get_index_integer(struct subagentx_varbind *, + struct subagentx_index *); +const unsigned char *subagentx_varbind_get_index_string( + struct subagentx_varbind *, struct subagentx_index *, size_t *, int *); +const uint32_t *subagentx_varbind_get_index_oid(struct subagentx_varbind *, + struct subagentx_index *, size_t *, int *); +const struct in_addr *subagentx_varbind_get_index_ipaddress( + struct subagentx_varbind *, struct subagentx_index *); +void subagentx_varbind_set_index_integer(struct subagentx_varbind *, + struct subagentx_index *, uint32_t); +void subagentx_varbind_set_index_string(struct subagentx_varbind *, + struct subagentx_index *, const char *); +void subagentx_varbind_set_index_nstring(struct subagentx_varbind *, + struct subagentx_index *, const unsigned char *, size_t); +void subagentx_varbind_set_index_oid(struct subagentx_varbind *, + struct subagentx_index *, const uint32_t *, size_t); +void subagentx_varbind_set_index_object(struct subagentx_varbind *, + struct subagentx_index *, struct subagentx_object *); +void subagentx_varbind_set_index_ipaddress(struct subagentx_varbind *, + struct subagentx_index *, const struct in_addr *); diff --git a/usr.sbin/relayd/subagentx_internal.h b/usr.sbin/relayd/subagentx_internal.h new file mode 100644 index 00000000000..4dafcf373a4 --- /dev/null +++ b/usr.sbin/relayd/subagentx_internal.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <sys/queue.h> +#include <sys/time.h> +#include <sys/tree.h> + +#include "agentx.h" + +enum subagentx_cstate { /* Current state */ + SA_CSTATE_CLOSE, /* Closed */ + SA_CSTATE_WAITOPEN, /* Connection requested */ + SA_CSTATE_OPEN, /* Open */ + SA_CSTATE_WAITCLOSE /* Close requested */ +}; + +enum subagentx_dstate { /* Desired state */ + SA_DSTATE_OPEN, /* Open */ + SA_DSTATE_CLOSE /* Close/free */ +}; + +struct subagentx { + void (*sa_nofd)(struct subagentx *, void *, int); + void *sa_cookie; + int sa_fd; + enum subagentx_cstate sa_cstate; + enum subagentx_dstate sa_dstate; + struct agentx *sa_ax; + TAILQ_HEAD(, subagentx_session) sa_sessions; + TAILQ_HEAD(, subagentx_get) sa_getreqs; + RB_HEAD(sa_requests, subagentx_request) sa_requests; +}; + +struct subagentx_session { + struct subagentx *sas_sa; + uint32_t sas_id; + uint32_t sas_timeout; + struct agentx_oid sas_oid; + struct agentx_ostring sas_descr; + enum subagentx_cstate sas_cstate; + enum subagentx_dstate sas_dstate; + uint32_t sas_packetid; + TAILQ_HEAD(, subagentx_context) sas_contexts; + TAILQ_ENTRY(subagentx_session) sas_sa_sessions; +}; + +struct subagentx_context { + struct subagentx_session *sac_sas; + int sac_name_default; + struct agentx_ostring sac_name; + uint32_t sac_sysuptime; + struct timespec sac_sysuptimespec; + enum subagentx_cstate sac_cstate; + enum subagentx_dstate sac_dstate; + TAILQ_HEAD(, subagentx_agentcaps) sac_agentcaps; + TAILQ_HEAD(, subagentx_region) sac_regions; + RB_HEAD(sac_objects, subagentx_object) sac_objects; + TAILQ_ENTRY(subagentx_context) sac_sas_contexts; +}; + +struct subagentx_get { + struct subagentx_context *sag_sac; + int sag_fd; /* Only used for logging */ + uint32_t sag_sessionid; + uint32_t sag_transactionid; + uint32_t sag_packetid; + int sag_context_default; + struct agentx_ostring sag_context; + enum agentx_pdu_type sag_type; + uint16_t sag_nonrep; + uint16_t sag_maxrep; + size_t sag_nvarbind; + struct subagentx_varbind *sag_varbind; + TAILQ_ENTRY(subagentx_get) sag_sa_getreqs; +}; + +__dead void subagentx_log_sa_fatalx(struct subagentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sa_warn(struct subagentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sa_warnx(struct subagentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sa_info(struct subagentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sa_debug(struct subagentx *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead void subagentx_log_sas_fatalx(struct subagentx_session *, const char *, + ...) __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sas_warnx(struct subagentx_session *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sas_warn(struct subagentx_session *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sas_info(struct subagentx_session *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead void subagentx_log_sac_fatalx(struct subagentx_context *, const char *, + ...) __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sac_warnx(struct subagentx_context *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sac_warn(struct subagentx_context *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sac_info(struct subagentx_context *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sac_debug(struct subagentx_context *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +__dead void subagentx_log_sag_fatalx(struct subagentx_get *, const char *, + ...) __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sag_warnx(struct subagentx_get *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sag_warn(struct subagentx_get *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); +void subagentx_log_sag_debug(struct subagentx_get *, const char *, ...) + __attribute__((__format__ (printf, 2, 3))); diff --git a/usr.sbin/relayd/subagentx_log.c b/usr.sbin/relayd/subagentx_log.c new file mode 100644 index 00000000000..d9dcca8e856 --- /dev/null +++ b/usr.sbin/relayd/subagentx_log.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2020 Martijn van Duren <martijn@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "subagentx_internal.h" + +#define SUBAGENTX_CONTEXT_NAME(sac) (sac->sac_name_default ? "<default>" : \ + (char *)sac->sac_name.aos_string) +#define SUBAGENTX_GET_CTXNAME(sag) (sag->sag_context_default ? "<default>" : \ + (char *)sag->sag_context.aos_string) + +enum subagentx_log_type { + SUBAGENTX_LOG_TYPE_FATAL, + SUBAGENTX_LOG_TYPE_WARN, + SUBAGENTX_LOG_TYPE_INFO, + SUBAGENTX_LOG_TYPE_DEBUG +}; + +void (*subagentx_log_fatal)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) = NULL; +void (*subagentx_log_warn)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) = NULL; +void (*subagentx_log_info)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) = NULL; +void (*subagentx_log_debug)(const char *, ...) + __attribute__((__format__ (printf, 1, 2))) = NULL; + + +static void +subagentx_log_do(enum subagentx_log_type, const char *, va_list, int, + struct subagentx *, struct subagentx_session *, struct subagentx_context *, + struct subagentx_get *); + +void +subagentx_log_sa_fatalx(struct subagentx *sa, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, sa, NULL, NULL, + NULL); + va_end(ap); + abort(); +} + +void +subagentx_log_sa_warn(struct subagentx *sa, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, sa, NULL, NULL, + NULL); + va_end(ap); +} + +void +subagentx_log_sa_warnx(struct subagentx *sa, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, sa, NULL, NULL, + NULL); + va_end(ap); +} + +void +subagentx_log_sa_info(struct subagentx *sa, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, sa, NULL, NULL, + NULL); + va_end(ap); +} + +void +subagentx_log_sa_debug(struct subagentx *sa, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, sa, NULL, NULL, + NULL); + va_end(ap); +} + +void +subagentx_log_sas_fatalx(struct subagentx_session *sas, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, sas, NULL, + NULL); + va_end(ap); + abort(); +} + +void +subagentx_log_sas_warnx(struct subagentx_session *sas, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, sas, NULL, + NULL); + va_end(ap); +} + +void +subagentx_log_sas_warn(struct subagentx_session *sas, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, sas, NULL, + NULL); + va_end(ap); +} + +void +subagentx_log_sas_info(struct subagentx_session *sas, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, NULL, sas, NULL, + NULL); + va_end(ap); +} + +void +subagentx_log_sac_fatalx(struct subagentx_context *sac, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, NULL, sac, + NULL); + va_end(ap); + abort(); +} + +void +subagentx_log_sac_warnx(struct subagentx_context *sac, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, NULL, sac, + NULL); + va_end(ap); +} + +void +subagentx_log_sac_warn(struct subagentx_context *sac, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, NULL, sac, + NULL); + va_end(ap); +} + +void +subagentx_log_sac_info(struct subagentx_context *sac, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_INFO, fmt, ap, 0, NULL, NULL, sac, + NULL); + va_end(ap); +} + +void +subagentx_log_sac_debug(struct subagentx_context *sac, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, NULL, NULL, sac, + NULL); + va_end(ap); +} + +void +subagentx_log_sag_fatalx(struct subagentx_get *sag, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_FATAL, fmt, ap, 0, NULL, NULL, NULL, + sag); + va_end(ap); + abort(); +} + +void +subagentx_log_sag_warnx(struct subagentx_get *sag, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 0, NULL, NULL, NULL, + sag); + va_end(ap); +} + +void +subagentx_log_sag_warn(struct subagentx_get *sag, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_WARN, fmt, ap, 1, NULL, NULL, NULL, + sag); + va_end(ap); +} + +void +subagentx_log_sag_debug(struct subagentx_get *sag, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + subagentx_log_do(SUBAGENTX_LOG_TYPE_DEBUG, fmt, ap, 0, NULL, NULL, NULL, + sag); + va_end(ap); +} + +static void +subagentx_log_do(enum subagentx_log_type type, const char *fmt, va_list ap, + int useerrno, struct subagentx *sa, struct subagentx_session *sas, + struct subagentx_context *sac, struct subagentx_get *sag) +{ + void (*subagentx_log)(const char *, ...); + char buf[1500]; + + if (type == SUBAGENTX_LOG_TYPE_FATAL) + subagentx_log = subagentx_log_fatal; + else if (type == SUBAGENTX_LOG_TYPE_WARN) + subagentx_log = subagentx_log_warn; + else if (type == SUBAGENTX_LOG_TYPE_INFO) + subagentx_log = subagentx_log_info; + else + subagentx_log = subagentx_log_debug; + if (subagentx_log == NULL) + return; + + vsnprintf(buf, sizeof(buf), fmt, ap); + + if (sag != NULL) { + if (useerrno) + subagentx_log("[fd:%d sess:%u ctx:%s trid:%u pid:%u]: " + "%s: %s", sag->sag_fd, sag->sag_sessionid, + SUBAGENTX_GET_CTXNAME(sag), sag->sag_transactionid, + sag->sag_packetid, buf, strerror(errno)); + else + subagentx_log("[fd:%d sess:%u ctx:%s trid:%u pid:%u]: " + "%s", sag->sag_fd, sag->sag_sessionid, + SUBAGENTX_GET_CTXNAME(sag), sag->sag_transactionid, + sag->sag_packetid, buf); + } else if (sac != NULL) { + sas = sac->sac_sas; + sa = sas->sas_sa; + if (useerrno) + subagentx_log("[fd:%d sess:%u ctx:%s]: %s: %s", + sa->sa_fd, sas->sas_id, SUBAGENTX_CONTEXT_NAME(sac), + buf, strerror(errno)); + else + subagentx_log("[fd:%d sess:%u ctx:%s]: %s", sa->sa_fd, + sas->sas_id, SUBAGENTX_CONTEXT_NAME(sac), buf); + } else if (sas != NULL) { + sa = sas->sas_sa; + if (useerrno) + subagentx_log("[fd:%d sess:%u]: %s: %s", sa->sa_fd, + sas->sas_id, buf, strerror(errno)); + else + subagentx_log("[fd:%d sess:%u]: %s", sa->sa_fd, + sas->sas_id, buf); + } else if (sa->sa_fd == -1) { + if (useerrno) + subagentx_log("%s: %s", buf, strerror(errno)); + else + subagentx_log("%s", buf); + } else { + if (useerrno) + subagentx_log("[fd:%d]: %s: %s", sa->sa_fd, buf, + strerror(errno)); + else + subagentx_log("[fd:%d]: %s", sa->sa_fd, buf); + } +} |