summaryrefslogtreecommitdiff
path: root/module/bsnmp-jails.c
diff options
context:
space:
mode:
Diffstat (limited to 'module/bsnmp-jails.c')
-rw-r--r--module/bsnmp-jails.c947
1 files changed, 947 insertions, 0 deletions
diff --git a/module/bsnmp-jails.c b/module/bsnmp-jails.c
new file mode 100644
index 0000000..f08da30
--- /dev/null
+++ b/module/bsnmp-jails.c
@@ -0,0 +1,947 @@
+/*
+ * Copyright (c) 2008, Stefan Walter
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Stefan Walter <stef@memberwebs.com>
+ */
+
+#include "usuals.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/jail.h>
+#include <sys/limits.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include <syslog.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <bsnmp/snmpmod.h>
+#include <pcap.h>
+
+#include "common/hash.h"
+
+#include "jails_tree.h"
+#include "jails_oid.h"
+
+#define SNAP_LEN 48
+
+/* our module handle */
+static struct lmodule *module;
+
+/* OIDs */
+static const struct asn_oid oid_jails = OIDX_jails;
+
+/* the Object Resource registration index */
+static u_int reg_index = 0;
+
+struct monitor {
+ TAILQ_ENTRY(monitor) link;
+
+ int refs;
+
+ char *device;
+ pcap_t *handle;
+ void *watch;
+ struct bpf_program filter;
+ int filter_valid;
+};
+
+TAILQ_HEAD(monitor_list, monitor);
+
+/* list of monitor structures */
+static struct monitor_list monitors = TAILQ_HEAD_INITIALIZER (monitors);
+
+struct jaildat {
+ uint32_t index;
+ TAILQ_ENTRY(jaildat) link;
+ int mark;
+
+ /* Configuration */
+ char *host;
+ char *path;
+ struct sockaddr_in addr;
+
+ /* Network monitor */
+ struct monitor *monitor;
+
+ /* Stats gathered */
+ uint64_t in_octets;
+ uint64_t in_packets;
+ uint64_t out_octets;
+ uint64_t out_packets;
+};
+
+TAILQ_HEAD(jaildat_list, jaildat);
+
+/* list of jail structures */
+static struct jaildat_list jaildats = TAILQ_HEAD_INITIALIZER (jaildats);
+
+/* number of if jail structures */
+static u_int jaildat_count = 0;
+
+/* Hash of jail structures by id */
+static hsh_t *jaildat_by_host = NULL;
+
+/* Hash of jail structures by address */
+static hsh_t *jaildat_by_address = NULL;
+
+/* Timer for refreshing the jaildat info */
+static void *timer_refresh = NULL;
+
+/* The monitor network filter */
+static u_char *network_filter = NULL;
+
+
+/* -----------------------------------------------------------------------------
+ * HELPERS
+ */
+
+static void
+emsg(const char *format, ...)
+{
+ va_list va;
+ va_start (va, format);
+ vsyslog (LOG_ERR, format, va);
+ va_end (va);
+}
+
+typedef void* (*if_enumerator) (struct ifreq *ifr, void* data);
+
+static void*
+enumerate_ifs (if_enumerator func, void* data)
+{
+ int sockfd = -1;
+ struct ifconf ifc;
+ struct ifreq *ifr = NULL;
+ unsigned char *ptr;
+ unsigned char *buf = NULL;
+ void *result = NULL;
+
+ ASSERT (func);
+
+ ifc.ifc_len = 32768;
+ buf = malloc (ifc.ifc_len);
+ ifc.ifc_req = (struct ifreq*)buf;
+ if (!buf) {
+ emsg ("couldn't allocate buffer to list interfaces: out of memory");
+ goto cleanup;
+ }
+
+ sockfd = socket (AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ emsg ("couldn't create socket to list interfaces: %s", strerror (errno));
+ goto cleanup;
+ }
+
+ if (ioctl (sockfd, SIOCGIFCONF, &ifc) < 0) {
+ emsg ("couldn't list interfaces: %s", strerror (errno));
+ goto cleanup;
+ }
+
+ #define IFR_SIZE(ifr) \
+ (max ((ifr)->ifr_addr.sa_len, sizeof((ifr)->ifr_addr)) + sizeof((ifr)->ifr_name))
+
+ for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
+ ifr = (struct ifreq*)ptr;
+ ptr += IFR_SIZE (ifr);
+
+ result = (func) (ifr, data);
+ if (result)
+ break;
+ }
+
+cleanup:
+ if (sockfd >= 0)
+ close (sockfd);
+ if (buf)
+ free (buf);
+ return result;
+}
+
+/* -----------------------------------------------------------------------------
+ * MONITORING
+ */
+
+#pragma pack(1)
+
+/* Ethernet header */
+struct ethhdr {
+ #define ETHER_ADDR_LEN 6
+ u_char dhost[ETHER_ADDR_LEN]; /* Destination host address */
+ u_char shost[ETHER_ADDR_LEN]; /* Source host address */
+ u_short type; /* IP? ARP? RARP? etc */
+};
+
+/* IP4 header */
+struct ip4hdr {
+ uint8_t vhl; /* version << 4 | header length >> 2 */
+ uint8_t tos; /* type of service */
+ uint16_t len; /* total length */
+ uint16_t id; /* identification */
+ uint16_t off; /* fragment offset field */
+ #define IP_RF 0x8000 /* reserved fragment flag */
+ #define IP_DF 0x4000 /* dont fragment flag */
+ #define IP_MF 0x2000 /* more fragments flag */
+ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ uint8_t ttl; /* time to live */
+ uint8_t proto; /* protocol */
+ uint16_t sum; /* checksum */
+ struct in_addr src, dst; /* source and dest address */
+};
+
+/* IP6 header */
+struct ip6hdr {
+ int32_t flow;
+ int16_t payload;
+ int8_t next;
+ int8_t hops;
+ struct in6_addr src, dst;
+};
+
+#pragma pack()
+
+static void
+process_ip4 (const struct ip4hdr *hdr, uint32_t octets)
+{
+ struct sockaddr_in addr;
+ struct jaildat *jail;
+
+ ASSERT (hdr);
+
+ /* Try incoming */
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_family = AF_INET;
+ addr.sin_len = sizeof (addr);
+ addr.sin_port = 0;
+ memcpy (&addr.sin_addr, &hdr->dst, sizeof (addr.sin_addr));
+
+ jail = hsh_get (jaildat_by_address, &addr, addr.sin_len);
+ if (jail) {
+ jail->in_octets += octets;
+ ++jail->in_packets;
+ return;
+ }
+
+ /* Try outgoing */
+ memcpy (&addr.sin_addr, &hdr->src, sizeof (addr.sin_addr));
+
+ jail = hsh_get (jaildat_by_address, &addr, addr.sin_len);
+ if (jail) {
+ jail->out_octets += octets;
+ ++jail->out_packets;
+ return;
+ }
+}
+
+static void
+process_ip6 (const struct ip6hdr *hdr, uint32_t octets)
+{
+ struct sockaddr_in6 addr;
+ struct jaildat *jail;
+
+ ASSERT (hdr);
+
+ /* Try incoming */
+ memset (&addr, 0, sizeof (addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_len = sizeof (addr);
+ addr.sin6_port = 0;
+ memcpy (&addr.sin6_addr, &hdr->dst, sizeof (addr.sin6_addr));
+
+ jail = hsh_get (jaildat_by_address, &addr, addr.sin6_len);
+ if (jail) {
+ jail->in_octets += octets;
+ ++jail->in_packets;
+ return;
+ }
+
+ /* Try outgoing */
+ memcpy (&addr.sin6_addr, &hdr->src, sizeof (addr.sin6_addr));
+
+ jail = hsh_get (jaildat_by_address, &addr, addr.sin6_len);
+ if (jail) {
+ jail->out_octets += octets;
+ ++jail->out_packets;
+ return;
+ }
+}
+
+static void
+monitor_packet (u_char *data, const struct pcap_pkthdr *hdr, const u_char *bytes)
+{
+ struct ethhdr *eth;
+ int minlen, octets, type;
+
+ /* Short packet, don't care */
+ if (hdr->len < sizeof (struct ethhdr))
+ return;
+
+ eth = (struct ethhdr*)bytes;
+ bytes += sizeof (struct ethhdr);
+ octets = hdr->len - sizeof (struct ethhdr);
+ type = ntohs (eth->type);
+
+ /* IPv4 packet? */
+ if (type == 0x0800) {
+ minlen = (sizeof (struct ethhdr) + sizeof (struct ip4hdr));
+ if (hdr->len >= minlen) {
+ ASSERT (hdr->caplen >= minlen);
+ process_ip4 ((const struct ip4hdr*)bytes, octets);
+ }
+
+ /* IPv6 packet? */
+ } else if (type == 0x86DD) {
+ minlen = (sizeof (struct ethhdr) + sizeof (struct ip6hdr));
+ if (hdr->len >= minlen) {
+ ASSERT (hdr->caplen >= minlen);
+ process_ip6 ((const struct ip6hdr*)bytes, octets);
+ }
+ }
+}
+
+static void
+monitor_io (int fd, void *data)
+{
+ struct monitor* mon = (struct monitor*)data;
+ int n_packets;
+
+ n_packets = pcap_dispatch (mon->handle, -1, monitor_packet, (u_char*)mon);
+ if (n_packets < 0)
+ emsg ("couldn't capture packets in monitor: %s", pcap_geterr (mon->handle));
+}
+
+static void
+monitor_free (struct monitor *mon)
+{
+ ASSERT (mon);
+
+ if (mon->device)
+ free (mon->device);
+ if (mon->watch)
+ fd_deselect (mon->watch);
+ if (mon->handle)
+ pcap_close (mon->handle);
+ if (mon->filter_valid)
+ pcap_freecode (&mon->filter);
+
+ TAILQ_REMOVE (&monitors, mon, link);
+ free (mon);
+}
+
+static struct monitor*
+monitor_create (const char *device)
+{
+ char errbuf[PCAP_ERRBUF_SIZE];
+ struct monitor* mon = NULL;
+ int success = 0;
+ int fd;
+
+ ASSERT (device);
+ ASSERT (network_filter);
+
+ mon = calloc (1, sizeof (struct monitor));
+ if (!mon) {
+ emsg ("couldn't allocate monitor: out of memory");
+ goto cleanup;
+ }
+
+ TAILQ_INSERT_TAIL(&monitors, mon, link);
+ mon->device = strdup (device);
+
+ mon->handle = pcap_open_live (mon->device, SNAP_LEN, 0, 100, errbuf);
+ if (!mon->handle) {
+ emsg ("couldn't open monitor on %s: %s", mon->device, errbuf);
+ goto cleanup;
+ }
+
+ if (pcap_compile (mon->handle, &mon->filter, network_filter, 1, 0) < 0) {
+ emsg ("couldn't compile monitor expression: %s", pcap_geterr (mon->handle));
+ goto cleanup;
+ }
+
+ mon->filter_valid = 1;
+ if (pcap_setfilter (mon->handle, &mon->filter) < 0) {
+ emsg ("couldn't setup monitor expression: %s", pcap_geterr (mon->handle));
+ goto cleanup;
+ }
+
+ if (pcap_setnonblock (mon->handle, 1, errbuf) < 0) {
+ emsg ("couldn't set monitor in non-block mode: %s", errbuf);
+ goto cleanup;
+ }
+
+ fd = pcap_get_selectable_fd (mon->handle);
+ if (fd < 0) {
+ emsg ("couldn't get selectable monitor: %s", pcap_geterr (mon->handle));
+ goto cleanup;
+ }
+
+ mon->watch = fd_select (fd, monitor_io, mon, module);
+ if (!mon->watch) {
+ emsg ("couldn't listen to monitor: %s", strerror (errno));
+ goto cleanup;
+ }
+
+ success = 1;
+
+cleanup:
+ if (!success && mon) {
+ monitor_free (mon);
+ mon = NULL;
+ }
+
+ return mon;
+}
+
+static void
+monitor_ref (struct monitor *mon)
+{
+ ASSERT (mon);
+ ASSERT (mon->refs >= 0);
+ ++mon->refs;
+}
+
+static void
+monitor_unref (struct monitor *mon)
+{
+ ASSERT (mon);
+
+ --mon->refs;
+ ASSERT (mon->refs >= 0);
+
+ if (mon->refs == 0)
+ monitor_free (mon);
+}
+
+static struct monitor*
+monitor_for_device (const char *device)
+{
+ struct monitor *mon;
+
+ ASSERT (device);
+
+ while ((mon = TAILQ_FIRST (&monitors)) != NULL) {
+ ASSERT (mon->device);
+ if (strcmp (mon->device, device) == 0)
+ return mon;
+ }
+
+ return monitor_create (device);
+}
+
+static void*
+monitor_addr_enumerator (struct ifreq *ifr, void *data)
+{
+ struct sockaddr *addr = (struct sockaddr*)data;
+ int match = 0;
+
+ ASSERT (ifr);
+ ASSERT (addr);
+
+ switch (ifr->ifr_addr.sa_family) {
+ case AF_INET:
+ if (addr->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in*)addr;
+ if (memcmp (&(sin->sin_addr), &(((struct sockaddr_in*)&ifr->ifr_addr)->sin_addr),
+ sizeof (sin->sin_addr)) == 0)
+ match = 1;
+ }
+ break;
+
+ case AF_INET6:
+ if (addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)addr;
+ if (memcmp (&sin6->sin6_addr, &(((struct sockaddr_in6*)&ifr->ifr_addr)->sin6_addr),
+ sizeof (sin6->sin6_addr)) == 0)
+ match = 1;
+ }
+ break;
+
+ default:
+ break;
+ };
+
+ if (!match)
+ return NULL;
+
+ return monitor_for_device (ifr->ifr_name);
+}
+
+static struct monitor*
+monitor_for_address (struct sockaddr *addr)
+{
+ return (struct monitor*)enumerate_ifs (monitor_addr_enumerator, addr);
+}
+
+static int
+monitor_test_filter (const char *filter)
+{
+ struct bpf_program bpf;
+ int ret;
+
+ ret = pcap_compile_nopcap (SNAP_LEN, DLT_EN10MB, &bpf, (char*)filter, 1, 0);
+ if (ret >= 0)
+ pcap_freecode (&bpf);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * JAIL LOOKUPS
+ */
+
+static void
+jail_free (struct jaildat *jail)
+{
+ ASSERT (jail);
+
+ if (jail->host)
+ free (jail->host);
+ jail->host = NULL;
+
+ if (jail->path)
+ free (jail->path);
+ jail->path = NULL;
+
+ if (jail->monitor)
+ monitor_unref (jail->monitor);
+ jail->monitor = NULL;
+
+ if (jail->index)
+ TAILQ_REMOVE (&jaildats, jail, link);
+
+ free (jail);
+}
+
+static int
+jail_update (struct jaildat *jail, const char *host,
+ const char *path, struct in_addr *addr)
+{
+ struct monitor *mon;
+ char *dup;
+
+ ASSERT (jail);
+
+ if (!host)
+ host = "";
+
+ if (!jail->host || strcmp (jail->host, host) != 0) {
+ dup = strdup (host);
+ if (!dup)
+ return -1;
+ if (jail->host) {
+ hsh_rem (jaildat_by_host, jail->host, HSH_KEY_STRING);
+ free (jail->host);
+ }
+ jail->host = dup;
+ hsh_set (jaildat_by_host, jail->host, HSH_KEY_STRING, jail);
+ }
+
+ if (!path)
+ path = "";
+ if (!jail->path || strcmp (jail->path, path) != 0) {
+ dup = strdup (path);
+ if (!dup)
+ return -1;
+ if (jail->path)
+ free (jail->path);
+ jail->path = dup;
+ }
+
+ if (memcmp (&jail->addr.sin_addr, addr, sizeof (jail->addr.sin_addr)) != 0) {
+ if (jail->addr.sin_len)
+ hsh_rem (jaildat_by_address, &(jail->addr), jail->addr.sin_len);
+ jail->addr.sin_family = AF_INET;
+ jail->addr.sin_len = sizeof (jail->addr);
+ jail->addr.sin_port = 0;
+ memcpy (&jail->addr.sin_addr, addr, sizeof (jail->addr.sin_addr));
+ hsh_set (jaildat_by_address, &(jail->addr), jail->addr.sin_len, jail);
+
+ mon = monitor_for_address ((struct sockaddr*)&jail->addr);
+ if (mon && mon != jail->monitor) {
+ monitor_ref (mon);
+ if (jail->monitor)
+ monitor_unref (jail->monitor);
+ jail->monitor = mon;
+ }
+ }
+
+ return 0;
+}
+
+static struct jaildat*
+jail_alloc (const char *host, const char *path, struct in_addr *addr)
+{
+ struct jaildat *jail;
+
+ jail = (struct jaildat*)calloc (1, sizeof (struct jaildat));
+ if (!jail)
+ return NULL;
+
+ if (jail_update (jail, host, path, addr) < 0) {
+ jail_free (jail);
+ return NULL;
+ }
+
+ jaildat_count++;
+ jail->index = jaildat_count;
+ INSERT_OBJECT_INT (jail, &jaildats);
+
+ return jail;
+}
+
+static void
+jail_refresh_all (void* unused)
+{
+ struct xprison *sxp, *xp;
+ struct in_addr in;
+ struct jaildat *jail, *tmp;
+ size_t i, len;
+
+ /* Get the length of the list */
+ if (sysctlbyname ("security.jail.list", NULL, &len, NULL, 0) == -1) {
+ emsg ("couldn't lookup jail list: %s", strerror (errno));
+ return;
+ }
+
+ /* Retrieve actual data */
+ for (i = 0; i < 4; i++) {
+ if (len <= 0)
+ return;
+ sxp = xp = malloc (len);
+ if (sxp == NULL) {
+ emsg ("out of memory");
+ return;
+ }
+
+ if (sysctlbyname ("security.jail.list", xp, &len, NULL, 0) == -1) {
+ free (sxp);
+ sxp = NULL;
+ if (errno == ENOMEM)
+ continue;
+ }
+
+ if (sxp == NULL) {
+ emsg ("couldn't retrieve jail list: %s", strerror (errno));
+ return;
+ }
+
+ break;
+ }
+
+ /* Make sure its kosher */
+ if (len < sizeof (*xp) || len % sizeof (*xp) || xp->pr_version != XPRISON_VERSION) {
+ emsg ("kernel and userland out of sync");
+ free (sxp);
+ return;
+ }
+
+ /* Mark and prepare for sweep below */
+ TAILQ_FOREACH (jail, &jaildats, link)
+ jail->mark = 1;
+
+ /* Allocate new jails, and update old ones */
+ for (i = 0; i < len / sizeof (*xp); ++i) {
+ in.s_addr = ntohl (xp->pr_ip);
+ jail = hsh_get (jaildat_by_host, xp->pr_host, HSH_KEY_STRING);
+ if (!jail)
+ jail = jail_alloc (xp->pr_host, xp->pr_path, &in);
+ else
+ jail_update (jail, xp->pr_host, xp->pr_path, &in);
+
+ jail->mark = 0;
+ xp++;
+ }
+
+ /* Sweep any jails that are no longer */
+ TAILQ_FOREACH_SAFE (jail, &jaildats, link, tmp) {
+ if (jail->mark)
+ jail_free (jail);
+ }
+
+ free (sxp);
+}
+
+/* -----------------------------------------------------------------------------
+ * CALLBACKS/CONFIG
+ */
+
+int
+op_jailconfig (struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ int r = SNMP_ERR_NOERROR;
+
+ switch (which) {
+ case LEAF_jailNetworkFilter:
+
+ if (op == SNMP_OP_GET)
+ return string_get (value, network_filter, -1);
+
+ /* Remainder only at initialization */
+ if (community != COMM_INITIALIZE)
+ return SNMP_ERR_NOT_WRITEABLE;
+
+ switch (op) {
+ case SNMP_OP_SET:
+ if ((r = string_save (value, ctx, -1, &network_filter)) == SNMP_ERR_NOERROR) {
+ if (monitor_test_filter (network_filter) < 0)
+ r = SNMP_ERR_GENERR;
+ }
+ if (r != SNMP_ERR_NOERROR)
+ string_rollback (ctx, &network_filter);
+ break;
+ case SNMP_OP_COMMIT:
+ string_commit (ctx);
+ break;
+ case SNMP_OP_ROLLBACK:
+ string_rollback (ctx, &network_filter);
+ break;
+ default:
+ ASSERT(0);
+ break;
+ };
+
+ return r;
+
+ default:
+ break;
+ };
+
+ ASSERT(0);
+ return -1;
+}
+
+int
+op_jailentry (struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ struct jaildat *jail = NULL;
+
+ switch (op) {
+ case SNMP_OP_GETNEXT:
+ jail = NEXT_OBJECT_INT (&jaildats, &value->var, sub);
+ if (jail == NULL)
+ return SNMP_ERR_NOSUCHNAME;
+ value->var.len = sub + 1;
+ value->var.subs[sub] = jail->index;
+ break;
+
+ case SNMP_OP_GET:
+ jail = FIND_OBJECT_INT (&jaildats, &value->var, sub);
+ if (jail == NULL)
+ return SNMP_ERR_NOSUCHNAME;
+ break;
+
+ default:
+ jail = FIND_OBJECT_INT (&jaildats, &value->var, sub);
+ break;
+ };
+
+ if (op != SNMP_OP_GET && op != SNMP_OP_GETNEXT)
+ return SNMP_ERR_NOT_WRITEABLE;
+
+ switch (which) {
+ case LEAF_jailIndex:
+ value->v.integer = jail->index;
+ return SNMP_ERR_NOERROR;
+ case LEAF_jailHost:
+ return string_get (value, jail->host, -1);
+ case LEAF_jailInOctets:
+ value->v.uint32 = jail->in_octets;
+ return SNMP_ERR_NOERROR;
+ case LEAF_jailInPackets:
+ value->v.uint32 = jail->in_packets;
+ return SNMP_ERR_NOERROR;
+ case LEAF_jailOutOctets:
+ value->v.uint32 = jail->out_octets;
+ return SNMP_ERR_NOERROR;
+ case LEAF_jailOutPackets:
+ value->v.uint32 = jail->out_packets;
+ return SNMP_ERR_NOERROR;
+ default:
+ ASSERT (0);
+ return SNMP_ERR_NOSUCHNAME;
+ };
+}
+
+int
+op_jail (struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+
+ switch (op) {
+ case SNMP_OP_GET:
+ break;
+
+ case SNMP_OP_SET:
+ return SNMP_ERR_NOT_WRITEABLE;
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ return SNMP_ERR_NOERROR;
+
+ default:
+ ASSERT(0);
+ break;
+ };
+
+ switch (which) {
+ case LEAF_jailCount:
+ value->v.integer = jaildat_count;
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ };
+
+ return SNMP_ERR_NOERROR;
+}
+
+
+/* -----------------------------------------------------------------------------
+ * MODULE
+ */
+
+/* Called, when the module is to be unloaded after it was successfully loaded */
+static int
+module_fini (void)
+{
+ struct jaildat *jail;
+ struct monitor *mon;
+
+ if (reg_index)
+ or_unregister (reg_index);
+
+ if (network_filter)
+ free (network_filter);
+ network_filter = NULL;
+
+ if (jaildat_by_address)
+ hsh_free (jaildat_by_address);
+ jaildat_by_address = NULL;
+
+ if (jaildat_by_host)
+ hsh_free (jaildat_by_host);
+ jaildat_by_host = NULL;
+
+ while ((jail = TAILQ_FIRST(&jaildats)) != NULL)
+ jail_free (jail);
+
+ while ((mon = TAILQ_FIRST(&monitors)) != NULL)
+ monitor_free (mon);
+
+ if (timer_refresh)
+ timer_stop (timer_refresh);
+ timer_refresh = NULL;
+
+ return 0;
+}
+
+/* the initialisation function */
+static int
+module_init (struct lmodule *mod, int argc, char *argv[])
+{
+ int success = 0;
+
+ module = mod;
+
+ if (argc != 0) {
+ syslog (LOG_ERR, "bad number of arguments for %s", __func__);
+ return EINVAL;
+ }
+
+ network_filter = strdup ("ip or ip6");
+ if (!network_filter)
+ goto cleanup;
+
+ jaildat_by_host = hsh_create ();
+ if (!jaildat_by_host)
+ goto cleanup;
+
+ jaildat_by_address = hsh_create ();
+ if (!jaildat_by_address)
+ goto cleanup;
+
+ success = 1;
+
+cleanup:
+ if (!success) {
+ emsg ("error initializing: out of memory");
+ module_fini ();
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+/* Module is started */
+static void
+module_start (void)
+{
+ reg_index = or_register (&oid_jails, "The MIB for jail interface data.", module);
+
+ jail_refresh_all (NULL);
+
+ timer_refresh = timer_start_repeat (500, 500, jail_refresh_all, NULL, module);
+ if (!timer_refresh)
+ emsg ("couldn't setup timer to refresh jails");
+}
+
+const struct snmp_module config = {
+ .comment = "This module implements SNMP monitoring of jails",
+ .init = module_init,
+ .start = module_start,
+ .fini = module_fini,
+ .tree = jails_ctree,
+ .tree_size = jails_CTREE_SIZE,
+};
+