diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-12-07 15:59:44 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@nxc.no> | 2013-12-07 17:08:34 +0200 |
commit | 3d7eea4614767c0932d4a11b1e88c6fe42c19f30 (patch) | |
tree | ee1cbef23777e11a152938893f2d77d1404fb744 | |
parent | 5c8527ed4e3f3807c1a1ba2e15816c754d165b22 (diff) | |
download | jumper-3d7eea4614767c0932d4a11b1e88c6fe42c19f30.tar.gz jumper-3d7eea4614767c0932d4a11b1e88c6fe42c19f30.tar.bz2 |
Implement configuration reloading on SIGHUP.
* src/interface.c: New file.
* src/Makefile.am (jumper_SOURCES): Add interface.c
* src/cmdline.opt: Update the use of get_priority.
* src/config.c (get_facility, get_priority): Return error code.
Change signature. All uses updated.
(ipv4_match_list_free): Remove (declared in listener.c)
(listen_kw): New keyword "id".
(cb_listen): Rewrite to support configuration updates.
(config_finish): Don't exit on failure, return error code
instead. All uses updated.
* src/environ.c (envcmp, envfree): New functions.
* src/jumper.c (command): New global.
(sigmain): Handle SIGHUP.
(reload): New function.
(main): implement configuration reloads.
* src/jumper.h: Change prototypes.
* src/listener.c: Major rewrite.
* src/progman.c (progman_decommission): New function.
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/cmdline.opt | 6 | ||||
-rw-r--r-- | src/config.c | 86 | ||||
-rw-r--r-- | src/environ.c | 25 | ||||
-rw-r--r-- | src/interface.c | 255 | ||||
-rw-r--r-- | src/jumper.c | 89 | ||||
-rw-r--r-- | src/jumper.h | 60 | ||||
-rw-r--r-- | src/listener.c | 468 | ||||
-rw-r--r-- | src/progman.c | 41 |
9 files changed, 757 insertions, 276 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 13e31a7..ebb8ce6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,9 +24,10 @@ jumper_SOURCES=\ cons_raw.c\ diag.c\ environ.c\ - listener.c\ + interface.c\ jumper.h\ jumper.c\ + listener.c\ mem.c\ progman.c\ trans.c diff --git a/src/cmdline.opt b/src/cmdline.opt index ea93fb2..543c8f7 100644 --- a/src/cmdline.opt +++ b/src/cmdline.opt @@ -55,8 +55,10 @@ OPTION(,l,PRIO, BEGIN if (strcmp(optarg, "none") == 0) log_to_stderr = -1; - else - log_to_stderr = get_priority(optarg); + else if (get_priority(optarg, &log_to_stderr)) { + diag(LOG_CRIT, "unknown syslog priority: %s", optarg); + exit(EX_USAGE); + } END OPTION(pidfile,P,FILE, diff --git a/src/config.c b/src/config.c index c5d9ebb..af2caf0 100644 --- a/src/config.c +++ b/src/config.c @@ -48,37 +48,31 @@ static struct transtab kwfac[] = { }; int -get_facility(const char *arg) +get_facility(const char *arg, int *retval) { int f; char *p; errno = 0; - f = strtoul (arg, &p, 0); - if (*p == 0 && errno == 0) - return f; - if (trans_strtotok(kwfac, arg, &f)) { - diag(LOG_CRIT, "unknown syslog facility: %s", arg); - exit(EX_USAGE); - } - return f; + f = strtoul(arg, &p, 0); + if (!(*p == 0 && errno == 0) && trans_strtotok(kwfac, arg, &f)) + return -1; + *retval = f; + return 0; } int -get_priority(const char *arg) +get_priority(const char *arg, int *retval) { int f; char *p; errno = 0; - f = strtoul (arg, &p, 0); - if (*p == 0 && errno == 0) - return f; - if (trans_strtotok(kwpri, arg, &f)) { - diag(LOG_CRIT, "unknown syslog priority: %s", arg); - exit(EX_USAGE); - } - return f; + f = strtoul(arg, &p, 0); + if (!(*p == 0 && errno == 0) && trans_strtotok(kwpri, arg, &f)) + return -1; + *retval = f; + return 0; } #define ASSERT_SCALAR(cmd, locus) \ @@ -125,18 +119,15 @@ cb_syslog_facility(enum grecs_callback_command cmd, grecs_node_t *node, { grecs_locus_t *locus = &node->locus; grecs_value_t *value = node->v.value; - int fac; ASSERT_SCALAR(cmd, locus); if (assert_grecs_value_type(&value->locus, value, GRECS_TYPE_STRING)) return 1; - if (trans_strtotok(kwfac, value->v.string, &fac)) + if (get_facility(value->v.string, varptr)) grecs_error(&value->locus, 0, "Unknown syslog facility `%s'", value->v.string); - else - *(int*)varptr = fac; return 0; } @@ -158,16 +149,6 @@ static struct grecs_keyword syslog_kw[] = { { NULL }, }; -void -ipv4_match_list_free(ipv4_match_list_t *lst) -{ - while (lst) { - ipv4_match_list_t *next = lst->next; - free(lst); - lst = next; - } -} - int str_to_cidr(grecs_value_t *val, ipv4_match_list_t **phead, ipv4_match_list_t **ptail) @@ -627,6 +608,9 @@ static struct grecs_keyword onevent_kw[] = { }; static struct grecs_keyword listen_kw[] = { + { "id", NULL, "unique identifier of this listener", + grecs_type_string, GRECS_DFLT, NULL, + offsetof(listener_t,id) }, { "match-source", NULL, "match packets coming from these IP addresses", grecs_type_cidr, GRECS_LIST, NULL, offsetof(listener_t,match_source), cb_match_list }, @@ -672,6 +656,12 @@ cb_listen(enum grecs_callback_command cmd, grecs_node_t *node, case grecs_callback_section_end: lp = *plp; + + if (!lp->id) { + grecs_error(&node->locus, 0, "id missing"); + ++err; + } + if (!lp->match_dest) { grecs_error(&node->locus, 0, "match-destination missing"); @@ -679,14 +669,35 @@ cb_listen(enum grecs_callback_command cmd, grecs_node_t *node, } if (!lp->prog) { - grecs_error(&node->locus, 0, - "command missing"); + grecs_error(&node->locus, 0, "command missing"); ++err; } if (!err) { + listener_t *p; + listener_fixup(lp); - listener_add(lp); + + p = listener_list_find_id(&clist, lp->id); + if (p) { + grecs_error(&node->locus, 0, + "duplicate listener"); + grecs_error(&p->locus, 0, + "this is the prior one"); + listener_free(lp); + return 0; + } + + p = listener_list_find_id(&llist, lp->id); + if (!p) { + interface_ref(lp->iface); + listener_list_add(&clist, lp); + } else if (listener_cmp(p, lp)) { + listener_decommission(p); + interface_ref(lp->iface); + listener_list_add(&clist, lp); + } else + listener_free(lp); } break; @@ -735,9 +746,8 @@ config_init() grecs_print_diag_fun = jumper_print_grecs_diag; } -void +int config_finish(struct grecs_node *tree) { - if (grecs_tree_process(tree, jumper_kw)) - exit(EX_CONFIG); + return grecs_tree_process(tree, jumper_kw) || grecs_error_count; } diff --git a/src/environ.c b/src/environ.c index b509604..303ade8 100644 --- a/src/environ.c +++ b/src/environ.c @@ -208,3 +208,28 @@ environ_setup(char **hint, char **kve) wordsplit_free(&ws); return new_env; } + +int +envcmp(char **a, char **b) +{ + if (!a) + return !!b; + else if (!b) + return !!a; + for (; *a && *b && strcmp(*a, *b) == 0; a++, b++); + if (!*a && !*b) + return 0; + return 1; +} + +void +envfree(char **a) +{ + int i; + + if (!a) + return; + for (i = 0; a[i]; i++) + free(a[i]); + free(a); +} diff --git a/src/interface.c b/src/interface.c new file mode 100644 index 0000000..5e1ef84 --- /dev/null +++ b/src/interface.c @@ -0,0 +1,255 @@ +/* This file is part of Jumper + Copyright (C) 2013 Sergey Poznyakoff + + Jumper is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Jumper is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jumper. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "jumper.h" + +int snaplen = 96; +int promisc_option = 1; + +struct callback { + pcap_handler function; + int type; +}; + + +static struct callback callbacks[] = { + { cons_ether_packet, DLT_EN10MB }, + { cons_ether_packet, DLT_IEEE802 }, + { cons_raw_packet, DLT_RAW }, + { cons_ppp_packet, DLT_PPP }, + { NULL, DLT_FDDI }, + { NULL, DLT_SLIP }, + { NULL, DLT_NULL }, + { NULL, 0 }, +}; + +static pcap_handler +lookup_pcap_callback(int type) +{ + struct callback *callback; + + for (callback = callbacks; callback->function; callback++) + if (type == callback->type) + return callback->function; + diag(LOG_ERR, "unknown data link type 0x%x", type); + exit(EX_SOFTWARE); +} + + +struct interface { + struct interface *prev, *next; + int refcnt; /* Number of listeners using this interface */ + char *name; /* Interface name */ + pcap_t *pcap; /* Pcap handler */ + pcap_handler hfun; /* Packet handler function */ + struct grecs_txtacc *acc;/* Expression accumulator */ + char *expr; /* Compiled expression */ +}; + +static struct interface *if_head, *if_tail; + +struct interface * +interface_lookup(const char *name) +{ + struct interface *ip; + + for (ip = if_head; ip; ip = ip->next) { + if (strcmp(ip->name, name) == 0) + return ip; + } + + ip = emalloc(sizeof(*ip)); + ip->next = NULL; + ip->prev = if_tail; + ip->pcap = NULL; + ip->name = estrdup(name); + ip->acc = NULL; + ip->expr = NULL; + + if (if_tail) + if_tail->next = ip; + else + if_head = ip; + if_tail = ip; + return ip; +} + +void +interface_deinit(struct interface *ifp) +{ + grecs_txtacc_free(ifp->acc); + ifp->acc = NULL; + ifp->expr = NULL; +} + +void +interface_ref(struct interface *ifp) +{ + ++ifp->refcnt; + if (ifp->expr) + interface_deinit(ifp); +} + +void +interface_unref(struct interface *ifp) +{ + if (--ifp->refcnt == 0) { + struct interface *p; + + p = ifp->prev; + if (p) + p->next = ifp->next; + else + if_head = ifp->next; + + p = ifp->next; + if (p) + p->prev = ifp->prev; + else + if_tail = ifp->prev; + + if (ifp->pcap) + pcap_close(ifp->pcap); + if (ifp->acc) + grecs_txtacc_free(ifp->acc); + free(ifp->name); + free(ifp); + } else if (ifp->expr) + interface_deinit(ifp); +} + +void +interfaces_init() +{ + struct interface *iface; + pcap_t *pd; + char errbuf[PCAP_ERRBUF_SIZE]; + struct bpf_program bpfcode; + + if (!if_head) { + diag(LOG_CRIT, "no listeners configured"); + exit(EX_CONFIG); + } + + for (iface = if_head; iface; iface = iface->next) { + if (!iface->expr) { + grecs_txtacc_grow_char(iface->acc, 0); + iface->expr = grecs_txtacc_finish(iface->acc, 0); + } + debug(2, ("iface %s, expr %s", + iface->name, iface->expr)); + + pd = pcap_open_live(iface->name, snaplen, + !promisc_option, + 1000, errbuf); + if (!pd) { + diag(LOG_CRIT, "pcap_open_live: %s", errbuf); + exit(EX_OSERR); + } + iface->pcap = pd; + iface->hfun = lookup_pcap_callback(pcap_datalink(pd)); + + if (pcap_compile(iface->pcap, &bpfcode, iface->expr, + 1, 0) < 0) { + diag(LOG_ERR, "pcap_compile(%s): %s", + iface->expr, pcap_geterr(iface->pcap)); + abort(); + } + + if (pcap_setfilter(iface->pcap, &bpfcode) < 0) { + diag(LOG_ERR, "pcap_setfilter: %s", + pcap_geterr(iface->pcap)); + exit(EX_OSERR); + } + } +} + +void +interfaces_fdset(fd_set *fdset, int *fdmax) +{ + struct interface *ifp; + + for (ifp = if_head; ifp; ifp = ifp->next) { + int fd = pcap_fileno(ifp->pcap); + if (fd > *fdmax) + *fdmax = fd; + FD_SET(fd, fdset); + } +} + +void +interfaces_poll(fd_set *fdset) +{ + struct interface *ifp; + + for (ifp = if_head; ifp; ifp = ifp->next) { + int fd = pcap_fileno(ifp->pcap); + if (FD_ISSET(fd, fdset)) { + struct pcap_pkthdr hdr; + const u_char *data = pcap_next(ifp->pcap, &hdr); + if (!data) + continue; + ifp->hfun(NULL, &hdr, data); + } + } +} + +void +interface_build_expr(struct interface *iface, ipv4_match_list_t *match_source, + ipv4_match_list_t *match_dest) +{ + ipv4_match_list_t *mp; + int delim; + + if (iface->acc) + grecs_txtacc_grow_string(iface->acc, " or "); + else + iface->acc = grecs_txtacc_create(); + + grecs_txtacc_grow_string(iface->acc, " ( "); + delim = 0; + if (match_source) { + grecs_txtacc_grow_string(iface->acc, " ( "); + for (mp = match_source; mp; mp = mp->next) { + if (delim) + grecs_txtacc_grow_string(iface->acc, " or"); + grecs_txtacc_grow_string(iface->acc, " src net "); + grecs_txtacc_grow_string(iface->acc, + ipv4_to_string(mp->cidr.addr)); + grecs_txtacc_grow_string(iface->acc, " mask "); + grecs_txtacc_grow_string(iface->acc, + ipv4_to_string(mp->cidr.netmask)); + delim = 1; + } + grecs_txtacc_grow_string(iface->acc, " ) and "); + } + delim = 0; + grecs_txtacc_grow_string(iface->acc, " ( "); + for (mp = match_dest; mp; mp = mp->next) { + if (delim) + grecs_txtacc_grow_string(iface->acc, " or"); + grecs_txtacc_grow_string(iface->acc, " dst net "); + grecs_txtacc_grow_string(iface->acc, + ipv4_to_string(mp->cidr.addr)); + grecs_txtacc_grow_string(iface->acc, " mask "); + grecs_txtacc_grow_string(iface->acc, + ipv4_to_string(mp->cidr.netmask)); + delim = 1; + } + grecs_txtacc_grow_string(iface->acc, " ) "); + grecs_txtacc_grow_string(iface->acc, " ) "); +} diff --git a/src/jumper.c b/src/jumper.c index 1857ea3..9be598c 100644 --- a/src/jumper.c +++ b/src/jumper.c @@ -101,7 +101,14 @@ storepid(const char *pidfile) } int signo = 0; -int stop = 0; + +enum { + command_none, + command_reload, + command_stop +}; + +int command = command_none; void sigmain(int sig) @@ -113,11 +120,44 @@ sigmain(int sig) break; case SIGALRM: break; + case SIGHUP: + command = command_reload; + break; default: - stop = 1; + command = command_stop; } } +static fd_set fdset; +static int maxfd = -1; + +static int +reload() +{ + struct grecs_node *tree; + + debug(1, ("reloading configuration")); + tree = grecs_parse(conffile); + if (!tree) + return 1; + if (config_finish(tree)) + return 1; + + progman_decommission(); + listeners_decommission(); + + listener_list_run_action(&clist, event_startup); + listener_list_concat(&llist, &clist); + + listeners_init(); + interfaces_init(); + + FD_ZERO(&fdset); + maxfd = -1; + interfaces_fdset(&fdset, &maxfd); + debug(1, ("configuration reload successful")); +} + #include "cmdline.h" int @@ -154,7 +194,8 @@ main(int argc, char **argv) if (!tree) exit(EX_CONFIG); - config_finish(tree); + if (config_finish(tree)) + exit(EX_CONFIG); if (opt_debug_level) debug_level += opt_debug_level; @@ -167,8 +208,12 @@ main(int argc, char **argv) if (!foreground && facility <= 0) facility = LOG_DAEMON; - interface_init(); - listener_init(); + listener_list_concat(&llist, &clist); + listeners_init(); + interfaces_init(); + FD_ZERO(&fdset); + maxfd = -1; + interfaces_fdset(&fdset, &maxfd); if (lint_only) return 0; @@ -190,13 +235,39 @@ main(int argc, char **argv) storepid(pidfile); signal_setup(sigmain); - listener_run_action(event_startup); + listeners_run_action(event_startup); - while (!stop) - listener_run(); + while (command != command_stop) { + struct interface *ifp; + int fd; + int rc; + struct timeval tv; + fd_set rdset; + + if (command == command_reload) { + reload(); + command = command_none; + } + + listener_proc_report(); + rdset = fdset; + rc = select(maxfd + 1, &rdset, NULL, NULL, + progman_timeout(&tv)); + if (rc < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + diag(LOG_ERR, "select: %s", strerror(errno)); + exit(EX_OSERR); + } else if (rc == 0) { + progman_expire(); + continue; + } + interfaces_poll(&rdset); + } + progman_terminate(); - listener_run_action(event_cleanup); + listeners_run_action(event_cleanup); diag(LOG_INFO, "%s %s stopped", program_name, VERSION); diff --git a/src/jumper.h b/src/jumper.h index 6644d8d..2c74beb 100644 --- a/src/jumper.h +++ b/src/jumper.h @@ -77,9 +77,9 @@ char *trans_tokfirst(struct transtab *tab, int tok, int *next); void signal_setup(void (*sf) (int)); -int get_priority(const char *arg); +int get_priority(const char *arg, int *retval); void config_help(void); -void config_finish(struct grecs_node *tree); +int config_finish(struct grecs_node *tree); typedef struct ipv4_cidr { /* IPv4 CIDR */ @@ -126,7 +126,9 @@ enum listener_status { #define REDIR_ERR 1 typedef struct listener { /* Listener */ + struct listener *prev; /* Pointer to the previous listener */ struct listener *next; /* Pointer to the next listener */ + char *id; /* Listener id */ struct interface *iface; /* Network interface */ ipv4_match_list_t *match_source; /* Source IP list (optional) */ ipv4_match_list_t *match_dest; /* Destination IP list (mandatory) */ @@ -143,21 +145,48 @@ typedef struct listener { /* Listener */ enum listener_status status; /* Status of this listener */ unsigned act_count; /* Number of running actions in stat_onexit state */ + int decommission; } listener_t; - + +typedef struct { + listener_t *head; + listener_t *tail; +} listener_list_t; + #define OPT_NOWAIT 0x01 #define OPT_STDOUT 0x02 #define OPT_STDERR 0x04 #define OPT_NULLIN 0x08 - -void interface_init(void); + +const char *ipv4_to_string(uint32_t ip); +int ipv4_match_p(ipv4_match_list_t *mp, uint32_t ip); +int ipv4_match_list_cmp(ipv4_match_list_t *a, ipv4_match_list_t *b); +void ipv4_match_list_free(ipv4_match_list_t *a); + +void interfaces_init(void); struct interface *interface_lookup(const char *name); +void interface_deinit(struct interface *ifp); +void interface_ref(struct interface *ifp); +void interface_unref(struct interface *ifp); +void interfaces_fdset(fd_set *fdset, int *fdmax); +void interfaces_poll(fd_set *fdset); +void interface_build_expr(struct interface *iface, + ipv4_match_list_t *match_source, + ipv4_match_list_t *match_dest); + +void listener_list_add(listener_list_t *lst, listener_t *lp); +void listener_list_remove(listener_list_t *lst, listener_t *lp); +listener_t *listener_list_find_id(listener_list_t *lst, const char *id); +void listener_list_free(listener_list_t *lst); +void listener_list_concat(listener_list_t *a, listener_list_t *b); +int listener_list_run_action(listener_list_t *lst, int a); void listener_fixup(listener_t *lp); -void listener_add(listener_t *lp); -void listener_init(void); -void listener_run(void); +void listener_free(listener_t *lp); +void listener_decommission(listener_t *lp); +int listener_cmp(listener_t *a, listener_t *b); listener_t *listener_locate(uint32_t src, uint32_t dst); +listener_t *listener_find_id(const char *id); void listener_print_status(listener_t *lp); void listener_proc_report(void); void listener_kill_redirector(listener_t *lp, int what); @@ -167,8 +196,15 @@ void onexit_reaction(listener_t *lp); #define KVE_MINSIZE 7 void listener_kve_init(char **kve, int kvn, listener_t *lp, ...); -int listener_run_action(int a); + +void listeners_init(void); +int listeners_run_action(int a); +void listeners_decommission(void); + + +extern listener_list_t llist; +extern listener_list_t clist; #define EX_OK 0 @@ -181,6 +217,9 @@ void cons_raw_packet(u_char *, const struct pcap_pkthdr *, const u_char *); char **environ_setup(char **hint, char **kve); +int envcmp(char **a, char **b); +void envfree(char **a); + enum process_type { type_listener, @@ -190,7 +229,8 @@ enum process_type { extern int proc_stop; void progman_cleanup(void); -void progman_terminate(void); + +void progman_terminate(); int progman_start(int type, listener_t *lp, struct grecs_locus *loc, int options, unsigned timeout, char *prog, char **env, char **kve); diff --git a/src/listener.c b/src/listener.c index 8b96985..cf3f355 100644 --- a/src/listener.c +++ b/src/listener.c @@ -18,196 +18,257 @@ #include "jumper.h" #include <sys/wait.h> -int snaplen = 96; -int promisc_option = 1; -struct callback { - pcap_handler function; - int type; -}; +const char * +ipv4_to_string(uint32_t ip) +{ + static char buf[16]; + + sprintf(buf, "%u.%u.%u.%u", + (ip >> 24) & 0xff, + (ip >> 16) & 0xff, + (ip >> 8) & 0xff, + ip & 0xff); + return buf; +} -static struct callback callbacks[] = { - { cons_ether_packet, DLT_EN10MB }, - { cons_ether_packet, DLT_IEEE802 }, - { cons_raw_packet, DLT_RAW }, - { cons_ppp_packet, DLT_PPP }, - { NULL, DLT_FDDI }, - { NULL, DLT_SLIP }, - { NULL, DLT_NULL }, - { NULL, 0 }, -}; +int +ipv4_match_p(ipv4_match_list_t *mp, uint32_t ip) +{ + for (; mp; mp = mp->next) + if (mp->cidr.addr == (mp->cidr.netmask & ip)) + return 1; + return 0; +} -pcap_handler -lookup_pcap_callback(int type) +int +ipv4_match_list_cmp(ipv4_match_list_t *a, ipv4_match_list_t *b) { - struct callback *callback; + for (; a && b && a->cidr.addr == b->cidr.addr && + a->cidr.netmask == b->cidr.netmask; + a = a->next, b = b ->next); + if (!a && !b) + return 0; + return 1; +} - for (callback = callbacks; callback->function; callback++) - if (type == callback->type) - return callback->function; - diag(LOG_ERR, "unknown data link type 0x%x", type); - exit(EX_SOFTWARE); +void +ipv4_match_list_free(ipv4_match_list_t *a) +{ + while (a) { + ipv4_match_list_t *next = a->next; + free(a); + a = next; + } } -struct interface { - struct interface *next; - char *name; - pcap_t *pcap; - pcap_handler hfun; - char *expr; -}; +static int +event_cmp(event_t *a, event_t *b) +{ + for (; a && b; a = a->next, b = b ->next) { + if (a->type != b->type) + return 1; + switch (a->type) { + case event_startup: + case event_cleanup: + continue; + default: + if (a->code != b->code) + return 1; + } + } + if (!a && !b) + return 0; + return 1; +} -static struct interface *if_head, *if_tail; +static void +event_free(event_t *a) +{ + while (a) { + event_t *next = a->next; + free(a); + a = next; + } +} -struct interface * -interface_lookup(const char *name) +static int +act_cmp(action_t *a, action_t *b) { - struct interface *ip; - - for (ip = if_head; ip; ip = ip->next) { - if (strcmp(ip->name, name) == 0) - return ip; + for (; a && b; a = a->next, b = b ->next) { + if (event_cmp(a->evt, b->evt)) + return 1; + if (strcmp(a->command, b->command)) + return 1; + if (envcmp(a->env, b->env)) + return 1; + if (a->options != b->options) + return 1; + if (a->timeout != b->timeout) + return 1; + } + if (!a && !b) + return 0; + return 1; +} + +static void +act_free(action_t *a) +{ + while (a) { + action_t *next = a->next; + event_free(a->evt); + free(a->command); + envfree(a->env); + a = next; } +} + +void +listener_list_add(listener_list_t *lst, listener_t *lp) +{ + lp->prev = lst->tail; + if (lst->tail) + lst->tail->next = lp; + else + lst->head = lp; + lst->tail = lp; +} + +void +listener_list_remove(listener_list_t *lst, listener_t *lp) +{ + listener_t *p; - ip = emalloc(sizeof(*ip)); - ip->next = NULL; - ip->pcap = NULL; - ip->name = estrdup(name); - ip->expr = NULL; + p = lp->prev; + if (p) + p->next = lp->next; + else + lst->head = lp->next; - if (if_tail) - if_tail->next = ip; + p = lp->next; + if (p) + p->prev = lp->prev; else - if_head = ip; - if_tail = ip; - return ip; + lst->tail = p; } void -interface_init() +listener_list_concat(listener_list_t *a, listener_list_t *b) { - struct interface *iface; - pcap_t *pd; - char errbuf[PCAP_ERRBUF_SIZE]; - - if (!if_head) { - diag(LOG_CRIT, "no listeners configured"); - exit(EX_CONFIG); - } + if (!b->head) + return; + b->head->prev = a->tail; + if (a->tail) + a->tail->next = b->head; + else + a->head = b->head; + a->tail = b->tail; + b->head = b->tail = NULL; +} - for (iface = if_head; iface; iface = iface->next) { - pd = pcap_open_live(iface->name, snaplen, !promisc_option, - 1000, errbuf); - if (!pd) { - diag(LOG_CRIT, "pcap_open_live: %s", errbuf); - exit(EX_OSERR); - } - iface->pcap = pd; - iface->hfun = lookup_pcap_callback(pcap_datalink(pd)); +void +listener_list_free(listener_list_t *lst) +{ + listener_t *p; + + for (p = lst->head; p; ) { + listener_t *next = p->next; + listener_free(p); + p = next; } + lst->head = lst->tail = NULL; +} + +listener_t * +listener_list_find_id(listener_list_t *lst, const char *id) +{ + listener_t *p; + + for (p = lst->head; p; p = p->next) + if (strcmp(p->id, id) == 0) + break; + return p; } -listener_t *l_head, *l_tail; +listener_list_t llist; +listener_list_t clist; void listener_fixup(listener_t *lp) { - lp->prog = estrdup(lp->prog); lp->locus.beg.file = estrdup(lp->locus.beg.file); lp->locus.end.file = estrdup(lp->locus.end.file); } void -listener_add(listener_t *lp) +listener_decommission(listener_t *lp) { - if (l_tail) - l_tail->next = lp; - else - l_head = lp; - l_tail = lp; + lp->decommission = 1; + interface_unref(lp->iface); } -static const char * -ipv4_to_string(uint32_t ip) +void +listener_free(listener_t *lp) { - static char buf[16]; + free(lp->id); + ipv4_match_list_free(lp->match_source); + ipv4_match_list_free(lp->match_dest); + free(lp->prog); + envfree(lp->env); + act_free(lp->act_head); + free(lp->locus.beg.file); + free(lp->locus.end.file); +} - sprintf(buf, "%u.%u.%u.%u", - (ip >> 24) & 0xff, - (ip >> 16) & 0xff, - (ip >> 8) & 0xff, - ip & 0xff); - return buf; -} +/* Compare two listeners. Return 0 if equal. */ +int +listener_cmp(listener_t *a, listener_t *b) +{ + if (a->iface != b->iface) { + debug(5, ("%s/%s: interfaces differ", a->id, b->id)); + return 1; + } + + if (ipv4_match_list_cmp(a->match_source, b->match_source)) { + debug(5, ("%s/%s: match-source lists differ", a->id, b->id)); + return 1; + } + + if (ipv4_match_list_cmp(a->match_dest, b->match_dest)) { + debug(5, ("%s/%s: match-destination lists differ", a->id, b->id)); + return 1; + } -#define addstr(s) grecs_line_acc_grow(s, strlen(s)) + if (envcmp(a->env, b->env)) { + debug(5, ("%s/%s: environments differ", a->id, b->id)); + return 1; + } + + if (a->options != b->options) { + debug(5, ("%s/%s: options differ", a->id, b->id)); + return 1; + } + + if (act_cmp(a->act_head, b->act_head)) { + debug(5, ("%s/%s: action lists differ", a->id, b->id)); + return 1; + } + + return 0; +} void -listener_init() +listeners_init() { - struct interface *ifp; listener_t *lp; - ipv4_match_list_t *mp; - int delim; - struct bpf_program bpfcode; - grecs_line_acc_create(); - for (lp = l_head; lp; lp = lp->next) { - grecs_line_begin(); - if (lp->iface->expr) { - addstr(lp->iface->expr); - addstr(" or "); - } - addstr(" ( "); - delim = 0; - if (lp->match_source) { - addstr(" ( "); - for (mp = lp->match_source; mp; mp = mp->next) { - if (delim) - addstr(" or"); - addstr(" src net "); - addstr(ipv4_to_string(mp->cidr.addr)); - addstr(" mask "); - addstr(ipv4_to_string(mp->cidr.netmask)); - delim = 1; - } - addstr(" ) and "); - } - delim = 0; - addstr(" ( "); - for (mp = lp->match_dest; mp; mp = mp->next) { - if (delim) - addstr(" or"); - addstr(" dst net "); - addstr(ipv4_to_string(mp->cidr.addr)); - addstr(" mask "); - addstr(ipv4_to_string(mp->cidr.netmask)); - delim = 1; - } - addstr(" ) "); - addstr(" ) "); - lp->iface->expr = grecs_line_finish(); - } - - for (ifp = if_head; ifp; ifp = ifp->next) { - debug(2, ("iface %s, expr %s", ifp->name, ifp->expr)); - - if (pcap_compile(ifp->pcap, &bpfcode, ifp->expr, 1, 0) < 0) { - diag(LOG_ERR, "pcap_compile(%s): %s", - ifp->expr, pcap_geterr(ifp->pcap)); - abort(); - } - - if (pcap_setfilter(ifp->pcap, &bpfcode) < 0) { - diag(LOG_ERR, "pcap_setfilter: %s", - pcap_geterr(ifp->pcap)); - exit(EX_OSERR); - } - } - grecs_line_acc_free(); + for (lp = llist.head; lp; lp = lp->next) + interface_build_expr(lp->iface, lp->match_source, + lp->match_dest); } @@ -278,89 +339,49 @@ event_match(action_t *act, enum event_type type, int code) static int runaction(listener_t *lp, action_t *act, event_t *evt); -int -listener_run_action(int a) +static int +listener_run_action(listener_t *lp, int a) { - listener_t *lp; + action_t *act; char *kve[KVE_MINSIZE+2]; - int err; + int err = 0; - for (lp = l_head; lp; lp = lp->next) { - action_t *act; - - listener_kve_init(kve, sizeof(kve)/sizeof(kve[0]), - lp, - "event", event_str[a], - NULL); - - for (act = lp->act_head; act; act = act->next) { - event_t *evt = event_match_type(act, a); - if (evt && runaction(lp, act, evt)) - err++; - } + listener_kve_init(kve, sizeof(kve)/sizeof(kve[0]), + lp, + "event", event_str[a], + NULL); + + for (act = lp->act_head; act; act = act->next) { + event_t *evt = event_match_type(act, a); + if (evt && runaction(lp, act, evt)) + err++; } return err; } - -void -listener_run() -{ |