diff options
Diffstat (limited to 'module')
-rw-r--r-- | module/Makefile.am | 19 | ||||
-rw-r--r-- | module/bsnmp-pcap.c | 544 | ||||
-rw-r--r-- | module/pcap-tree.def | 63 | ||||
-rw-r--r-- | module/usuals.h | 68 |
4 files changed, 694 insertions, 0 deletions
diff --git a/module/Makefile.am b/module/Makefile.am new file mode 100644 index 0000000..fcdd5cb --- /dev/null +++ b/module/Makefile.am @@ -0,0 +1,19 @@ + +INCLUDES = -DCONF_PREFIX=\"$(sysconfdir)\" + +moduledir = $(prefix)/lib +module_LTLIBRARIES = snmp_pcap.la + +snmp_pcap_la_CFLAGS = -Wall -I$(top_srcdir) +snmp_pcap_la_LDFLAGS = -module +snmp_pcap_la_SOURCES = pcap_tree.c pcap_tree.h pcap_oid.h \ + bsnmp-pcap.c usuals.h + +pcap_tree.c: pcap-tree.def + gensnmptree -e pcap > pcap_oid.h < $(srcdir)/pcap-tree.def + gensnmptree -p pcap_ < $(srcdir)/pcap-tree.def + +EXTRA_DIST = pcap-tree.def + +CLEANFILES = pcap_tree.* \ + pcap_oid.h 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, +}; + diff --git a/module/pcap-tree.def b/module/pcap-tree.def new file mode 100644 index 0000000..fb780c7 --- /dev/null +++ b/module/pcap-tree.def @@ -0,0 +1,63 @@ +# +# 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 +# Stef Walter <stef@memberwebs.com> +# + +(1 internet + (4 private + (1 enterprises + (12325 fokus + (1 begemot + (1112 pcap + (1 pcapCount INTEGER op_pcap GET) + (2 pcapTable + (1 pcapEntry : INTEGER op_pcapentry + (0 pcapIndex INTEGER GET) + + (1 pcapDescr OCTETSTRING GET SET) + (2 pcapDevice OCTETSTRING GET SET) + (3 pcapFilter OCTETSTRING GET SET) + + (10 pcapOctets COUNTER64 GET) + (11 pcapPackets COUNTER64 GET) + ) + ) + ) + ) + ) + ) + ) +) + diff --git a/module/usuals.h b/module/usuals.h new file mode 100644 index 0000000..e975dcd --- /dev/null +++ b/module/usuals.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006, 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 + * Stef Walter <stef@memberwebs.com> + */ + +#ifndef __USUALS_H__ +#define __USUALS_H__ + +#include <sys/types.h> + +#include "config.h" + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define countof(x) (sizeof(x) / sizeof(x[0])) + +#ifdef _DEBUG + #include "assert.h" + #define ASSERT(x) assert(x) +#else + #define ASSERT(x) +#endif + +#endif /* __USUALS_H__ */ |