summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/cache_iterators.c15
-rw-r--r--src/origin.c70
-rw-r--r--src/run.c18
-rw-r--r--src/stats-mode.c9
-rw-r--r--src/sync-mode.c51
6 files changed, 136 insertions, 29 deletions
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 <pablo@netfilter.org>
+ *
+ * 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 <errno.h>
#include <signal.h>
@@ -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 <errno.h>
#include <unistd.h>
@@ -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;