aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-12-07 15:59:44 +0200
committerSergey Poznyakoff <gray@nxc.no>2013-12-07 17:08:34 +0200
commit3d7eea4614767c0932d4a11b1e88c6fe42c19f30 (patch)
treeee1cbef23777e11a152938893f2d77d1404fb744
parent5c8527ed4e3f3807c1a1ba2e15816c754d165b22 (diff)
downloadjumper-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.am3
-rw-r--r--src/cmdline.opt6
-rw-r--r--src/config.c86
-rw-r--r--src/environ.c25
-rw-r--r--src/interface.c255
-rw-r--r--src/jumper.c89
-rw-r--r--src/jumper.h60
-rw-r--r--src/listener.c468
-rw-r--r--src/progman.c41
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()
-{