From ef047d03613bf9fa105db009773136817e2ec4c6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 23 May 2009 12:54:51 +0200 Subject: conntrackd: detect where the events comes from Since Linux kernel 2.6.29, ctnetlink reports the changes that have been done using ctnetlink. With this patch, conntrackd can recognize who is the origin of the event messages. For example, this is interesting to avoid a messy implicit bulk send during the commit of entries. Signed-off-by: Pablo Neira Ayuso --- include/Makefile.am | 2 +- include/cache.h | 4 ++- include/conntrackd.h | 6 ++--- include/origin.h | 14 +++++++++++ src/Makefile.am | 2 +- src/cache_iterators.c | 15 ++++------- src/origin.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/run.c | 18 ++++++++----- src/stats-mode.c | 9 ++++--- src/sync-mode.c | 51 ++++++++++++++++++++++++++++++------- 10 files changed, 157 insertions(+), 34 deletions(-) create mode 100644 include/origin.h create mode 100644 src/origin.c diff --git a/include/Makefile.am b/include/Makefile.am index 0ea056c..b72fb36 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -4,5 +4,5 @@ noinst_HEADERS = alarm.h jhash.h cache.h linux_list.h linux_rbtree.h \ debug.h log.h hash.h mcast.h conntrack.h \ network.h filter.h queue.h vector.h cidr.h \ traffic_stats.h netlink.h fds.h event.h bitops.h channel.h \ - process.h + process.h origin.h diff --git a/include/cache.h b/include/cache.h index 371170d..b6facdc 100644 --- a/include/cache.h +++ b/include/cache.h @@ -121,8 +121,10 @@ void *cache_get_extra(struct cache *, void *); void cache_iterate(struct cache *c, void *data, int (*iterate)(void *data1, void *data2)); /* iterators */ +struct nfct_handle; + void cache_dump(struct cache *c, int fd, int type); -void cache_commit(struct cache *c); +void cache_commit(struct cache *c, struct nfct_handle *h); void cache_flush(struct cache *c); void cache_bulk(struct cache *c); diff --git a/include/conntrackd.h b/include/conntrackd.h index 013ec4f..81cfd51 100644 --- a/include/conntrackd.h +++ b/include/conntrackd.h @@ -218,9 +218,9 @@ struct ct_mode { struct nf_conntrack *ct, void *data); int (*purge)(void); - void (*event_new)(struct nf_conntrack *ct); - void (*event_upd)(struct nf_conntrack *ct); - int (*event_dst)(struct nf_conntrack *ct); + void (*event_new)(struct nf_conntrack *ct, int origin); + void (*event_upd)(struct nf_conntrack *ct, int origin); + int (*event_dst)(struct nf_conntrack *ct, int origin); }; /* conntrackd modes */ diff --git a/include/origin.h b/include/origin.h new file mode 100644 index 0000000..b2d1823 --- /dev/null +++ b/include/origin.h @@ -0,0 +1,14 @@ +#ifndef _ORIGIN_H_ +#define _ORIGIN_H_ + +enum { + CTD_ORIGIN_NOT_ME = 0, /* this event comes from the kernel or + any process, but not conntrackd */ + CTD_ORIGIN_COMMIT, /* event comes from committer */ +}; + +int origin_register(struct nfct_handle *h, int origin_type); +int origin_find(const struct nlmsghdr *nlh); +int origin_unregister(struct nfct_handle *h); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index decc545..c338fee 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,7 +12,7 @@ conntrack_LDFLAGS = $(all_libraries) @LIBNETFILTER_CONNTRACK_LIBS@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ local.c log.c mcast.c udp.c netlink.c vector.c \ - filter.c fds.c event.c process.c \ + filter.c fds.c event.c process.c origin.c \ cache.c cache_iterators.c \ cache_timer.c cache_wt.c \ sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \ diff --git a/src/cache_iterators.c b/src/cache_iterators.c index dfccc68..542ab91 100644 --- a/src/cache_iterators.c +++ b/src/cache_iterators.c @@ -175,20 +175,16 @@ static int do_commit_master(void *data, struct hashtable_node *n) } /* no need to clone, called from child process */ -void cache_commit(struct cache *c) +void cache_commit(struct cache *c, struct nfct_handle *h) { unsigned int commit_ok = c->stats.commit_ok; unsigned int commit_fail = c->stats.commit_fail; - struct __commit_container tmp; + struct __commit_container tmp = { + .h = h, + .c = c, + }; struct timeval commit_start, commit_stop, res; - tmp.h = nfct_open(CONNTRACK, 0); - if (tmp.h == NULL) { - dlog(LOG_ERR, "can't create handler to commit entries"); - return; - } - tmp.c = c; - gettimeofday(&commit_start, NULL); /* commit master conntrack first, then related ones */ hashtable_iterate(c->h, &tmp, do_commit_master); @@ -206,7 +202,6 @@ void cache_commit(struct cache *c) if (commit_fail) dlog(LOG_NOTICE, "%u entries can't be " "committed", commit_fail); - nfct_close(tmp.h); dlog(LOG_NOTICE, "commit has taken %lu.%06lu seconds", res.tv_sec, res.tv_usec); diff --git a/src/origin.c b/src/origin.c new file mode 100644 index 0000000..3c65f3d --- /dev/null +++ b/src/origin.c @@ -0,0 +1,70 @@ +/* + * (C) 2009 by Pablo Neira Ayuso + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "conntrackd.h" +#include "origin.h" + +static LIST_HEAD(origin_list); + +struct origin { + struct list_head head; + unsigned int nl_portid; + int type; +}; + +/* register a Netlink socket as origin of possible events */ +int origin_register(struct nfct_handle *h, int origin_type) +{ + struct origin *nlp; + + nlp = calloc(sizeof(struct origin), 1); + if (nlp == NULL) + return -1; + + nlp->nl_portid = nfnl_portid(nfct_nfnlh(h)); + nlp->type = origin_type; + + list_add(&nlp->head, &origin_list); + return 0; +} + +/* look up for the origin of this Netlink event */ +int origin_find(const struct nlmsghdr *nlh) +{ + struct origin *this; + + list_for_each_entry(this, &origin_list, head) { + if (this->nl_portid == nlh->nlmsg_pid) { + return this->type; + } + } + return CTD_ORIGIN_NOT_ME; +} + +int origin_unregister(struct nfct_handle *h) +{ + struct origin *this, *tmp; + + list_for_each_entry_safe(this, tmp, &origin_list, head) { + if (this->nl_portid == nfnl_portid(nfct_nfnlh(h))) { + list_del(&this->head); + free(this); + return 1; + } + } + return 0; +} diff --git a/src/run.c b/src/run.c index 09e2ae9..e54764c 100644 --- a/src/run.c +++ b/src/run.c @@ -26,6 +26,7 @@ #include "fds.h" #include "traffic_stats.h" #include "process.h" +#include "origin.h" #include #include @@ -228,10 +229,13 @@ static void do_polling_alarm(struct alarm_block *a, void *data) add_alarm(&STATE(polling_alarm), CONFIG(poll_kernel_secs), 0); } -static int event_handler(enum nf_conntrack_msg_type type, +static int event_handler(const struct nlmsghdr *nlh, + enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data) { + int origin_type; + STATE(stats).nl_events_received++; /* skip user-space filtering if already do it in the kernel */ @@ -240,15 +244,17 @@ static int event_handler(enum nf_conntrack_msg_type type, goto out; } + origin_type = origin_find(nlh); + switch(type) { case NFCT_T_NEW: - STATE(mode)->event_new(ct); + STATE(mode)->event_new(ct, origin_type); break; case NFCT_T_UPDATE: - STATE(mode)->event_upd(ct); + STATE(mode)->event_upd(ct, origin_type); break; case NFCT_T_DESTROY: - if (STATE(mode)->event_dst(ct)) + if (STATE(mode)->event_dst(ct, origin_type)) update_traffic_stats(ct); break; default: @@ -334,8 +340,8 @@ init(void) dlog(LOG_ERR, "no ctnetlink kernel support?"); return -1; } - nfct_callback_register(STATE(event), NFCT_T_ALL, - event_handler, NULL); + nfct_callback_register2(STATE(event), NFCT_T_ALL, + event_handler, NULL); register_fd(nfct_fd(STATE(event)), STATE(fds)); } diff --git a/src/stats-mode.c b/src/stats-mode.c index af1c136..b84c7a1 100644 --- a/src/stats-mode.c +++ b/src/stats-mode.c @@ -141,7 +141,8 @@ static int purge_stats(void) return 0; } -static void event_new_stats(struct nf_conntrack *ct) +static void +event_new_stats(struct nf_conntrack *ct, int origin) { int id; struct cache_object *obj; @@ -162,13 +163,15 @@ static void event_new_stats(struct nf_conntrack *ct) return; } -static void event_update_stats(struct nf_conntrack *ct) +static void +event_update_stats(struct nf_conntrack *ct, int origin) { nfct_attr_unset(ct, ATTR_TIMEOUT); cache_update_force(STATE_STATS(cache), ct); } -static int event_destroy_stats(struct nf_conntrack *ct) +static int +event_destroy_stats(struct nf_conntrack *ct, int origin) { int id; struct cache_object *obj; diff --git a/src/sync-mode.c b/src/sync-mode.c index 0d35923..91e028e 100644 --- a/src/sync-mode.c +++ b/src/sync-mode.c @@ -27,6 +27,7 @@ #include "event.h" #include "queue.h" #include "process.h" +#include "origin.h" #include #include @@ -385,6 +386,14 @@ static void dump_stats_sync_extended(int fd) send(fd, buf, size, 0); } +/* this is called once the committer process has finished */ +static void commit_done_cb(void *data) +{ + struct nfct_handle *h = data; + origin_unregister(h); + nfct_close(h); +} + /* handler for requests coming via UNIX socket */ static int local_handler_sync(int fd, int type, void *data) { @@ -419,16 +428,29 @@ static int local_handler_sync(int fd, int type, void *data) exit(EXIT_SUCCESS); } break; - case COMMIT: + case COMMIT: { + struct nfct_handle *h; + /* delete the reset alarm if any before committing */ del_alarm(&STATE_SYNC(reset_cache_alarm)); - ret = fork_process_new(NULL, NULL); + + /* disposable handler for commit operations */ + h = nfct_open(CONNTRACK, 0); + if (h == NULL) { + dlog(LOG_ERR, "can't create handler to commit"); + break; + } + origin_register(h, CTD_ORIGIN_COMMIT); + + /* fork new process and insert it the process list */ + ret = fork_process_new(commit_done_cb, h); if (ret == 0) { dlog(LOG_NOTICE, "committing external cache"); - cache_commit(STATE_SYNC(external)); + cache_commit(STATE_SYNC(external), h); exit(EXIT_SUCCESS); } break; + } case RESET_TIMERS: if (!alarm_pending(&STATE_SYNC(reset_cache_alarm))) { dlog(LOG_NOTICE, "flushing conntrack table in %d secs", @@ -557,7 +579,8 @@ static int resync_sync(enum nf_conntrack_msg_type type, return NFCT_CB_CONTINUE; } -static void event_new_sync(struct nf_conntrack *ct) +static void +event_new_sync(struct nf_conntrack *ct, int origin) { struct cache_object *obj; int id; @@ -578,7 +601,11 @@ retry: cache_object_free(obj); return; } - sync_send(obj, NET_T_STATE_NEW); + /* only synchronize events that have been triggered by other + * processes or the kernel, but don't propagate events that + * have been triggered by conntrackd itself, eg. commits. */ + if (origin == CTD_ORIGIN_NOT_ME) + sync_send(obj, NET_T_STATE_NEW); } else { cache_del(STATE_SYNC(internal), obj); cache_object_free(obj); @@ -586,7 +613,8 @@ retry: } } -static void event_update_sync(struct nf_conntrack *ct) +static void +event_update_sync(struct nf_conntrack *ct, int origin) { struct cache_object *obj; @@ -594,21 +622,26 @@ static void event_update_sync(struct nf_conntrack *ct) if (obj == NULL) return; - sync_send(obj, NET_T_STATE_UPD); + if (origin == CTD_ORIGIN_NOT_ME) + sync_send(obj, NET_T_STATE_UPD); } -static int event_destroy_sync(struct nf_conntrack *ct) +static int +event_destroy_sync(struct nf_conntrack *ct, int origin) { struct cache_object *obj; int id; + /* we don't synchronize events for objects that are not in the cache */ obj = cache_find(STATE_SYNC(internal), ct, &id); if (obj == NULL) return 0; if (obj->status != C_OBJ_DEAD) { cache_object_set_status(obj, C_OBJ_DEAD); - sync_send(obj, NET_T_STATE_DEL); + if (origin == CTD_ORIGIN_NOT_ME) { + sync_send(obj, NET_T_STATE_DEL); + } cache_object_put(obj); } return 1; -- cgit v1.2.3