diff options
Diffstat (limited to 'module/bsnmp-pcap.c')
-rw-r--r-- | module/bsnmp-pcap.c | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/module/bsnmp-pcap.c b/module/bsnmp-pcap.c new file mode 100644 index 0000000..21224ca --- /dev/null +++ b/module/bsnmp-pcap.c @@ -0,0 +1,544 @@ +/* + * 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/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 "pcap_tree.h" +#include "pcap_oid.h" + +#define SNAP_LEN 48 +#define DEFAULT_FILTER "ip or ip6" + +/* our module handle */ +static struct lmodule *module; + +/* OIDs */ +static const struct asn_oid oid_pcap = OIDX_pcap; + +/* the Object Resource registration index */ +static u_int reg_index = 0; + +struct monitor { + uint32_t index; + TAILQ_ENTRY(monitor) link; + + u_char *descr; + u_char *device; + pcap_t *handle; + void *watch; + u_char *filter; + struct bpf_program filter_bpf; + int filter_valid; + + /* Stats gathered */ + uint64_t seen_octets; + uint64_t seen_packets; +}; + +TAILQ_HEAD(monitor_list, monitor); + +/* list of monitor structures */ +static struct monitor_list monitors = TAILQ_HEAD_INITIALIZER (monitors); + +/* Number of monitors */ +static int monitor_count = 0; + +/* ----------------------------------------------------------------------------- + * HELPERS + */ + +static void +emsg(const char *format, ...) +{ + va_list va; + va_start (va, format); + vsyslog (LOG_ERR, format, va); + va_end (va); +} + +/* ----------------------------------------------------------------------------- + * 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 +monitor_packet (u_char *data, const struct pcap_pkthdr *hdr, const u_char *bytes) +{ + struct monitor *mon = (struct monitor*)data; + int octets; + + /* Short packet, don't care */ + if (hdr->len < sizeof (struct ethhdr)) + return; + + octets = hdr->len - sizeof (struct ethhdr); + mon->seen_octets += octets; + mon->seen_packets += 1; +} + +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->descr) + free (mon->descr); + 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_bpf); + if (mon->filter) + free (mon->filter); + + TAILQ_REMOVE (&monitors, mon, link); + monitor_count--; + free (mon); +} + +static struct monitor* +monitor_alloc (int index) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + struct monitor* mon; + + mon = calloc (1, sizeof (struct monitor)); + if (!mon) { + emsg ("couldn't allocate monitor: out of memory"); + return NULL; + } + + mon->index = index; + INSERT_OBJECT_INT (mon, &monitors); + monitor_count++; + + mon->device = pcap_lookupdev (errbuf); + if (!mon->device) { + mon->device = strdup ("any"); + if (!mon->device) { + monitor_free (mon); + return NULL; + } + } + + mon->filter = strdup (DEFAULT_FILTER); + if (!mon->filter) { + monitor_free (mon); + return NULL; + } + + mon->descr = strdup (""); + if (!mon->descr) { + monitor_free (mon); + return NULL; + } + + return mon; +} + +static void +monitor_start (struct monitor *mon) +{ + char errbuf[PCAP_ERRBUF_SIZE]; + int fd; + + ASSERT (mon->device); + ASSERT (mon->filter); + + 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); + return; + } + + if (pcap_compile (mon->handle, &mon->filter_bpf, mon->filter, 1, 0) < 0) { + emsg ("couldn't compile monitor expression: %s", pcap_geterr (mon->handle)); + return; + } + + mon->filter_valid = 1; + if (pcap_setfilter (mon->handle, &mon->filter_bpf) < 0) { + emsg ("couldn't setup monitor expression: %s", pcap_geterr (mon->handle)); + return; + } + + if (pcap_setnonblock (mon->handle, 1, errbuf) < 0) { + emsg ("couldn't set monitor in non-block mode: %s", errbuf); + return; + } + + fd = pcap_get_selectable_fd (mon->handle); + if (fd < 0) { + emsg ("couldn't get selectable monitor: %s", pcap_geterr (mon->handle)); + return; + } + + mon->watch = fd_select (fd, monitor_io, mon, module); + if (!mon->watch) { + emsg ("couldn't listen to monitor: %s", strerror (errno)); + return; + } +} + +/* ----------------------------------------------------------------------------- + * CALLBACKS/CONFIG + */ + +static int +op_config (struct monitor *mon, 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 index, r; + + /* Just return values, no creation */ + if (op == SNMP_OP_GET || op == SNMP_OP_GETNEXT) { + + if (!mon) + return SNMP_ERR_NOSUCHNAME; + + switch (which) { + case LEAF_pcapIndex: + value->v.integer = mon->index; + return SNMP_ERR_NOERROR; + case LEAF_pcapDescr: + return string_get (value, mon->descr, -1); + case LEAF_pcapDevice: + return string_get (value, mon->device, -1); + case LEAF_pcapFilter: + return string_get (value, mon->filter, -1); + default: + ASSERT (0); + return SNMP_ERR_NOSUCHNAME; + } + } + + /* Remainder only at initialization */ + if (community != COMM_INITIALIZE) + return mon ? SNMP_ERR_NOT_WRITEABLE : SNMP_ERR_NO_CREATION; + + /* No writing to pcapIndex */ + if (which == LEAF_pcapIndex) + return SNMP_ERR_NOT_WRITEABLE; + + if (index_decode (&value->var, sub, iidx, &index)) + return SNMP_ERR_NO_CREATION; + + /* Create it if necessary */ + if (!mon) { + mon = monitor_alloc (index); + if (!mon) { + emsg ("out of memory"); + return SNMP_ERR_GENERR; + } + } + + switch (which) { + + /* pcapDescr */ + case LEAF_pcapDescr: + switch (op) { + case SNMP_OP_SET: + return string_save (value, ctx, -1, &mon->descr); + case SNMP_OP_ROLLBACK: + return SNMP_ERR_NOERROR; + case SNMP_OP_COMMIT: + return SNMP_ERR_NOERROR; + default: + ASSERT (0); + return SNMP_ERR_GENERR; + } + break; + + /* pcapDevice */ + case LEAF_pcapDevice: + switch (op) { + case SNMP_OP_SET: + return string_save (value, ctx, -1, &mon->device); + case SNMP_OP_ROLLBACK: + return SNMP_ERR_NOERROR; + case SNMP_OP_COMMIT: + return SNMP_ERR_NOERROR; + default: + ASSERT (0); + return SNMP_ERR_GENERR; + } + break; + + /* pcapFilter */ + case LEAF_pcapFilter: + switch (op) { + case SNMP_OP_SET: + r = string_save (value, ctx, -1, &mon->filter); + return r; + case SNMP_OP_ROLLBACK: + return SNMP_ERR_NOERROR; + case SNMP_OP_COMMIT: + return SNMP_ERR_NOERROR; + default: + ASSERT (0); + return SNMP_ERR_GENERR; + } + break; + + /* Unknown OID */ + default: + ASSERT (0); + return SNMP_ERR_NOSUCHNAME; + } +} + +int +op_pcapentry (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 monitor *mon = NULL; + + switch (op) { + case SNMP_OP_GETNEXT: + mon = NEXT_OBJECT_INT (&monitors, &value->var, sub); + if (mon == NULL) + return SNMP_ERR_NOSUCHNAME; + value->var.len = sub + 1; + value->var.subs[sub] = mon->index; + break; + + case SNMP_OP_GET: + mon = FIND_OBJECT_INT (&monitors, &value->var, sub); + if (mon == NULL) + return SNMP_ERR_NOSUCHNAME; + break; + + default: + mon = FIND_OBJECT_INT (&monitors, &value->var, sub); + break; + }; + + /* Send configuration stuff off elsewhere */ + switch (which) { + case LEAF_pcapIndex: + case LEAF_pcapDescr: + case LEAF_pcapDevice: + case LEAF_pcapFilter: + return op_config (mon, ctx, value, sub, iidx, op); + } + + if (op != SNMP_OP_GET && op != SNMP_OP_GETNEXT) + return SNMP_ERR_NOT_WRITEABLE; + + switch (which) { + case LEAF_pcapOctets: + value->v.counter64 = mon->seen_octets; + return SNMP_ERR_NOERROR; + case LEAF_pcapPackets: + value->v.counter64 = mon->seen_packets; + return SNMP_ERR_NOERROR; + default: + ASSERT (0); + return SNMP_ERR_NOSUCHNAME; + }; +} + +int +op_pcap (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_pcapCount: + value->v.integer = monitor_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 monitor *mon; +fprintf(stderr, "fini\n"); + + if (reg_index) + or_unregister (reg_index); + + while ((mon = TAILQ_FIRST(&monitors)) != NULL) + monitor_free (mon); + + return 0; +} + +/* the initialisation function */ +static int +module_init (struct lmodule *mod, int argc, char *argv[]) +{ + module = mod; + + if (argc != 0) { + syslog (LOG_ERR, "bad number of arguments for %s", __func__); + return EINVAL; + } + + return 0; +} + +/* Module is started */ +static void +module_start (void) +{ + struct monitor *mon; +fprintf(stderr, "start\n"); + reg_index = or_register (&oid_pcap, "The MIB for pcap data.", module); + + /* Start each monitor */ + TAILQ_FOREACH (mon, &monitors, link) { + monitor_start (mon); + } +} + +const struct snmp_module config = { + .comment = "This module implements SNMP pcap monitoring of traffic", + .init = module_init, + .start = module_start, + .fini = module_fini, + .tree = pcap_ctree, + .tree_size = pcap_CTREE_SIZE, +}; + |