summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/external_cache.c122
-rw-r--r--src/external_inject.c150
-rw-r--r--src/read_config_lex.l1
-rw-r--r--src/read_config_yy.y13
-rw-r--r--src/sync-mode.c73
6 files changed, 319 insertions, 41 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 1c8b34f..753c809 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,6 +20,7 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
network.c cidr.c \
build.c parse.c \
channel.c multichannel.c channel_mcast.c channel_udp.c \
+ external_cache.c external_inject.c \
read_config_yy.y read_config_lex.l
# yacc and lex generate dirty code
diff --git a/src/external_cache.c b/src/external_cache.c
new file mode 100644
index 0000000..c70c818
--- /dev/null
+++ b/src/external_cache.c
@@ -0,0 +1,122 @@
+/*
+ * (C) 2006-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 "sync.h"
+#include "log.h"
+#include "cache.h"
+#include "external.h"
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <stdlib.h>
+
+static struct cache *external;
+
+static int external_cache_init(void)
+{
+ external = cache_create("external",
+ STATE_SYNC(sync)->external_cache_flags,
+ NULL);
+ if (external == NULL) {
+ dlog(LOG_ERR, "can't allocate memory for the external cache");
+ return -1;
+ }
+ return 0;
+}
+
+static void external_cache_close(void)
+{
+ cache_destroy(external);
+}
+
+static void external_cache_new(struct nf_conntrack *ct)
+{
+ struct cache_object *obj;
+ int id;
+
+ obj = cache_find(external, ct, &id);
+ if (obj == NULL) {
+retry:
+ obj = cache_object_new(external, ct);
+ if (obj == NULL)
+ return;
+
+ if (cache_add(external, obj, id) == -1) {
+ cache_object_free(obj);
+ return;
+ }
+ } else {
+ cache_del(external, obj);
+ cache_object_free(obj);
+ goto retry;
+ }
+}
+
+static void external_cache_upd(struct nf_conntrack *ct)
+{
+ cache_update_force(external, ct);
+}
+
+static void external_cache_del(struct nf_conntrack *ct)
+{
+ struct cache_object *obj;
+ int id;
+
+ obj = cache_find(external, ct, &id);
+ if (obj) {
+ cache_del(external, obj);
+ cache_object_free(obj);
+ }
+}
+
+static void external_cache_dump(int fd, int type)
+{
+ cache_dump(external, fd, type);
+}
+
+static void external_cache_commit(struct nfct_handle *h, int fd)
+{
+ cache_commit(external, h, fd);
+}
+
+static void external_cache_flush(void)
+{
+ cache_flush(external);
+}
+
+static void external_cache_stats(int fd)
+{
+ cache_stats(external, fd);
+}
+
+static void external_cache_stats_ext(int fd)
+{
+ cache_stats_extended(external, fd);
+}
+
+struct external_handler external_cache = {
+ .init = external_cache_init,
+ .close = external_cache_close,
+ .new = external_cache_new,
+ .update = external_cache_upd,
+ .destroy = external_cache_del,
+ .dump = external_cache_dump,
+ .commit = external_cache_commit,
+ .flush = external_cache_flush,
+ .stats = external_cache_stats,
+ .stats_ext = external_cache_stats_ext,
+};
diff --git a/src/external_inject.c b/src/external_inject.c
new file mode 100644
index 0000000..ec1cb16
--- /dev/null
+++ b/src/external_inject.c
@@ -0,0 +1,150 @@
+/*
+ * (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 "sync.h"
+#include "log.h"
+#include "cache.h"
+#include "origin.h"
+#include "external.h"
+#include "netlink.h"
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <errno.h>
+#include <stdlib.h>
+
+static struct nfct_handle *inject;
+
+static int external_inject_init(void)
+{
+ /* handler to directly inject conntracks into kernel-space */
+ inject = nfct_open(CONNTRACK, 0);
+ if (inject == NULL) {
+ dlog(LOG_ERR, "can't open netlink handler: %s",
+ strerror(errno));
+ dlog(LOG_ERR, "no ctnetlink kernel support?");
+ return -1;
+ }
+ /* we are directly injecting the entries into the kernel */
+ origin_register(inject, CTD_ORIGIN_INJECT);
+ return 0;
+}
+
+static void external_inject_close(void)
+{
+ origin_unregister(inject);
+ nfct_close(inject);
+}
+
+static void external_inject_new(struct nf_conntrack *ct)
+{
+ int ret, retry = 1;
+
+retry:
+ if (nl_create_conntrack(inject, ct, 0) == -1) {
+ /* if the state entry exists, we delete and try again */
+ if (errno == EEXIST && retry == 1) {
+ ret = nl_destroy_conntrack(inject, ct);
+ if (ret == 0 || (ret == -1 && errno == ENOENT)) {
+ if (retry) {
+ retry = 0;
+ goto retry;
+ }
+ }
+ dlog(LOG_ERR, "inject-add1: %s", strerror(errno));
+ dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
+ return;
+ }
+ dlog(LOG_ERR, "inject-add2: %s", strerror(errno));
+ dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
+ }
+}
+
+static void external_inject_upd(struct nf_conntrack *ct)
+{
+ int ret;
+
+ /* if we successfully update the entry, everything is OK */
+ if (nl_update_conntrack(inject, ct, 0) != -1)
+ return;
+
+ /* state entries does not exist, we have to create it */
+ if (errno == ENOENT) {
+ if (nl_create_conntrack(inject, ct, 0) == -1) {
+ dlog(LOG_ERR, "inject-upd1: %s", strerror(errno));
+ dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
+ }
+ return;
+ }
+
+ /* we failed to update the entry, there are some operations that
+ * may trigger this error, eg. unset some status bits. Try harder,
+ * delete the existing entry and create a new one. */
+ ret = nl_destroy_conntrack(inject, ct);
+ if (ret == 0 || (ret == -1 && errno == ENOENT)) {
+ if (nl_create_conntrack(inject, ct, 0) == -1) {
+ dlog(LOG_ERR, "inject-upd2: %s", strerror(errno));
+ dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
+ }
+ return;
+ }
+ dlog(LOG_ERR, "inject-upd3: %s", strerror(errno));
+ dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
+}
+
+static void external_inject_del(struct nf_conntrack *ct)
+{
+ if (nl_destroy_conntrack(inject, ct) == -1) {
+ if (errno != ENOENT) {
+ dlog(LOG_ERR, "inject-del: %s", strerror(errno));
+ dlog_ct(STATE(log), ct, NFCT_O_PLAIN);
+ }
+ }
+}
+
+static void external_inject_dump(int fd, int type)
+{
+}
+
+static void external_inject_commit(struct nfct_handle *h, int fd)
+{
+}
+
+static void external_inject_flush(void)
+{
+}
+
+static void external_inject_stats(int fd)
+{
+}
+
+static void external_inject_stats_ext(int fd)
+{
+}
+
+struct external_handler external_inject = {
+ .init = external_inject_init,
+ .close = external_inject_close,
+ .new = external_inject_new,
+ .update = external_inject_upd,
+ .destroy = external_inject_del,
+ .dump = external_inject_dump,
+ .commit = external_inject_commit,
+ .flush = external_inject_flush,
+ .stats = external_inject_stats,
+ .stats_ext = external_inject_stats_ext,
+};
diff --git a/src/read_config_lex.l b/src/read_config_lex.l
index dad7555..d3f83aa 100644
--- a/src/read_config_lex.l
+++ b/src/read_config_lex.l
@@ -135,6 +135,7 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k]
"Type" { return T_TYPE; }
"Priority" { return T_PRIO; }
"NetlinkEventsReliable" { return T_NETLINK_EVENTS_RELIABLE; }
+"DisableExternalCache" { return T_DISABLE_EXTERNAL_CACHE; }
{is_on} { return T_ON; }
{is_off} { return T_OFF; }
diff --git a/src/read_config_yy.y b/src/read_config_yy.y
index f3f4730..38c5929 100644
--- a/src/read_config_yy.y
+++ b/src/read_config_yy.y
@@ -72,6 +72,7 @@ static void __max_dedicated_links_reached(void);
%token T_FROM T_USERSPACE T_KERNELSPACE T_EVENT_ITER_LIMIT T_DEFAULT
%token T_NETLINK_OVERRUN_RESYNC T_NICE T_IPV4_DEST_ADDR T_IPV6_DEST_ADDR
%token T_SCHEDULER T_TYPE T_PRIO T_NETLINK_EVENTS_RELIABLE
+%token T_DISABLE_EXTERNAL_CACHE
%token <string> T_IP T_PATH_VAL
%token <val> T_NUMBER
@@ -698,6 +699,7 @@ sync_mode_ftfw_line: resend_queue_size
| timeout
| purge
| window_size
+ | disable_external_cache
;
sync_mode_notrack_list:
@@ -705,8 +707,19 @@ sync_mode_notrack_list:
sync_mode_notrack_line: timeout
| purge
+ | disable_external_cache
;
+disable_external_cache: T_DISABLE_EXTERNAL_CACHE T_ON
+{
+ conf.sync.external_cache_disable = 1;
+};
+
+disable_external_cache: T_DISABLE_EXTERNAL_CACHE T_OFF
+{
+ conf.sync.external_cache_disable = 0;
+};
+
resend_buffer_size: T_RESEND_BUFFER_SIZE T_NUMBER
{
print_err(CTD_CFG_WARN, "`ResendBufferSize' is deprecated. "
diff --git a/src/sync-mode.c b/src/sync-mode.c
index 9e3ac39..174df80 100644
--- a/src/sync-mode.c
+++ b/src/sync-mode.c
@@ -28,6 +28,7 @@
#include "queue.h"
#include "process.h"
#include "origin.h"
+#include "external.h"
#include <errno.h>
#include <unistd.h>
@@ -43,8 +44,6 @@ do_channel_handler_step(int i, struct nethdr *net, size_t remain)
{
char __ct[nfct_maxsize()];
struct nf_conntrack *ct = (struct nf_conntrack *)(void*) __ct;
- struct cache_object *obj;
- int id;
if (net->version != CONNTRACKD_PROTOCOL_VERSION) {
STATE_SYNC(error).msg_rcv_malformed++;
@@ -84,32 +83,13 @@ do_channel_handler_step(int i, struct nethdr *net, size_t remain)
switch(net->type) {
case NET_T_STATE_NEW:
- obj = cache_find(STATE_SYNC(external), ct, &id);
- if (obj == NULL) {
-retry:
- obj = cache_object_new(STATE_SYNC(external), ct);
- if (obj == NULL)
- return;
-
- if (cache_add(STATE_SYNC(external), obj, id) == -1) {
- cache_object_free(obj);
- return;
- }
- } else {
- cache_del(STATE_SYNC(external), obj);
- cache_object_free(obj);
- goto retry;
- }
+ STATE_SYNC(external)->new(ct);
break;
case NET_T_STATE_UPD:
- cache_update_force(STATE_SYNC(external), ct);
+ STATE_SYNC(external)->update(ct);
break;
case NET_T_STATE_DEL:
- obj = cache_find(STATE_SYNC(external), ct, &id);
- if (obj) {
- cache_del(STATE_SYNC(external), obj);
- cache_object_free(obj);
- }
+ STATE_SYNC(external)->destroy(ct);
break;
default:
STATE_SYNC(error).msg_rcv_malformed++;
@@ -275,15 +255,14 @@ static int init_sync(void)
return -1;
}
- STATE_SYNC(external) =
- cache_create("external",
- STATE_SYNC(sync)->external_cache_flags,
- NULL);
-
- if (!STATE_SYNC(external)) {
- dlog(LOG_ERR, "can't allocate memory for the external cache");
- return -1;
+ if (CONFIG(sync).external_cache_disable == 0) {
+ STATE_SYNC(external) = &external_cache;
+ } else {
+ STATE_SYNC(external) = &external_inject;
+ dlog(LOG_NOTICE, "disabling external cache");
}
+ if (STATE_SYNC(external)->init() == -1)
+ return -1;
channel_init();
@@ -361,7 +340,7 @@ static void run_sync(fd_set *readfds)
if (FD_ISSET(get_read_evfd(STATE_SYNC(commit).evfd), readfds)) {
read_evfd(STATE_SYNC(commit).evfd);
- cache_commit(STATE_SYNC(external), STATE_SYNC(commit).h, 0);
+ STATE_SYNC(external)->commit(STATE_SYNC(commit).h, 0);
}
/* flush pending messages */
@@ -371,7 +350,7 @@ static void run_sync(fd_set *readfds)
static void kill_sync(void)
{
cache_destroy(STATE_SYNC(internal));
- cache_destroy(STATE_SYNC(external));
+ STATE_SYNC(external)->close();
multichannel_close(STATE_SYNC(channel));
@@ -452,7 +431,7 @@ static int local_handler_sync(int fd, int type, void *data)
case DUMP_EXTERNAL:
ret = fork_process_new(CTD_PROC_ANY, 0, NULL, NULL);
if (ret == 0) {
- cache_dump(STATE_SYNC(external), fd, NFCT_O_PLAIN);
+ STATE_SYNC(external)->dump(fd, NFCT_O_PLAIN);
exit(EXIT_SUCCESS);
}
break;
@@ -466,7 +445,7 @@ static int local_handler_sync(int fd, int type, void *data)
case DUMP_EXT_XML:
ret = fork_process_new(CTD_PROC_ANY, 0, NULL, NULL);
if (ret == 0) {
- cache_dump(STATE_SYNC(external), fd, NFCT_O_XML);
+ STATE_SYNC(external)->dump(fd, NFCT_O_XML);
exit(EXIT_SUCCESS);
}
break;
@@ -475,7 +454,7 @@ static int local_handler_sync(int fd, int type, void *data)
del_alarm(&STATE_SYNC(reset_cache_alarm));
dlog(LOG_NOTICE, "committing external cache");
- cache_commit(STATE_SYNC(external), STATE_SYNC(commit).h, fd);
+ STATE_SYNC(external)->commit(STATE_SYNC(commit).h, fd);
/* Keep the client socket open, we want synchronous commits. */
ret = LOCAL_RET_STOLEN;
break;
@@ -492,7 +471,7 @@ static int local_handler_sync(int fd, int type, void *data)
del_alarm(&STATE_SYNC(reset_cache_alarm));
dlog(LOG_NOTICE, "flushing caches");
cache_flush(STATE_SYNC(internal));
- cache_flush(STATE_SYNC(external));
+ STATE_SYNC(external)->flush();
break;
case FLUSH_INT_CACHE:
/* inmediate flush, remove pending flush scheduled if any */
@@ -502,14 +481,14 @@ static int local_handler_sync(int fd, int type, void *data)
break;
case FLUSH_EXT_CACHE:
dlog(LOG_NOTICE, "flushing external cache");
- cache_flush(STATE_SYNC(external));
+ STATE_SYNC(external)->flush();
break;
case KILL:
killer(0);
break;
case STATS:
cache_stats(STATE_SYNC(internal), fd);
- cache_stats(STATE_SYNC(external), fd);
+ STATE_SYNC(external)->stats(fd);
dump_traffic_stats(fd);
multichannel_stats(STATE_SYNC(channel), fd);
dump_stats_sync(fd);
@@ -520,7 +499,7 @@ static int local_handler_sync(int fd, int type, void *data)
break;
case STATS_CACHE:
cache_stats_extended(STATE_SYNC(internal), fd);
- cache_stats_extended(STATE_SYNC(external), fd);
+ STATE_SYNC(external)->stats_ext(fd);
break;
case STATS_LINK:
multichannel_stats_extended(STATE_SYNC(channel),
@@ -616,6 +595,10 @@ event_new_sync(struct nf_conntrack *ct, int origin)
struct cache_object *obj;
int id;
+ /* this event has been triggered by a direct inject, skip */
+ if (origin == CTD_ORIGIN_INJECT)
+ return;
+
/* required by linux kernel <= 2.6.20 */
nfct_attr_unset(ct, ATTR_ORIG_COUNTER_BYTES);
nfct_attr_unset(ct, ATTR_ORIG_COUNTER_PACKETS);
@@ -649,6 +632,10 @@ event_update_sync(struct nf_conntrack *ct, int origin)
{
struct cache_object *obj;
+ /* this event has been triggered by a direct inject, skip */
+ if (origin == CTD_ORIGIN_INJECT)
+ return;
+
obj = cache_update_force(STATE_SYNC(internal), ct);
if (obj == NULL)
return;
@@ -663,6 +650,10 @@ event_destroy_sync(struct nf_conntrack *ct, int origin)
struct cache_object *obj;
int id;
+ /* this event has been triggered by a direct inject, skip */
+ if (origin == CTD_ORIGIN_INJECT)
+ return 0;
+
/* we don't synchronize events for objects that are not in the cache */
obj = cache_find(STATE_SYNC(internal), ct, &id);
if (obj == NULL)