summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlex Harpin <development@landsofshadow.co.uk>2015-11-26 22:10:55 +0000
committerAlex Harpin <development@landsofshadow.co.uk>2015-11-26 22:10:55 +0000
commit71f0da9318b926578ed44818f4b2709a821980ae (patch)
tree86c9f489859ccfdacdb1860fa6d3e6cc869c11d6 /src
parentfc3a760c5e481a8302d166f7e7f758f027545f33 (diff)
parent5df0941f73bffabd775d1c14e62295cfe46956eb (diff)
downloadconntrack-tools-71f0da9318b926578ed44818f4b2709a821980ae.tar.gz
conntrack-tools-71f0da9318b926578ed44818f4b2709a821980ae.zip
Merge tag 'conntrack-tools-1.4.3' into lithium
conntrack-tools 1.4.3 release
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am40
-rw-r--r--src/cache.c5
-rw-r--r--src/channel.c1
-rw-r--r--src/conntrack.c340
-rw-r--r--src/cthelper.c54
-rw-r--r--src/expect.c47
-rw-r--r--src/filter.c4
-rw-r--r--src/helpers/Makefile.am29
-rw-r--r--src/helpers/amanda.c203
-rw-r--r--src/helpers/dhcpv6.c123
-rw-r--r--src/helpers/ftp.c3
-rw-r--r--src/helpers/sane.c173
-rw-r--r--src/helpers/ssdp.c134
-rw-r--r--src/helpers/tftp.c138
-rw-r--r--src/helpers/tns.c1
-rw-r--r--src/internal_bypass.c4
-rw-r--r--src/local.c12
-rw-r--r--src/netlink.c2
-rw-r--r--src/nfct-extensions/helper.c273
-rw-r--r--src/nfct-extensions/timeout.c441
-rw-r--r--src/nfct.c180
-rw-r--r--src/parse.c8
-rw-r--r--src/process.c2
-rw-r--r--src/read_config_lex.l2
-rw-r--r--src/read_config_yy.y9
-rw-r--r--src/run.c9
-rw-r--r--src/sync-notrack.c2
-rw-r--r--src/tcp.c12
-rw-r--r--src/udp.c8
29 files changed, 1632 insertions, 627 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ec03e46..a1d00f8 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,8 @@
include $(top_srcdir)/Make_global.am
+if HAVE_CTHELPER
SUBDIRS = helpers
+endif
AM_YFLAGS = -d
@@ -11,17 +13,28 @@ sbin_PROGRAMS = conntrack conntrackd nfct
conntrack_SOURCES = conntrack.c
conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp.la ../extensions/libct_proto_udplite.la ../extensions/libct_proto_icmp.la ../extensions/libct_proto_icmpv6.la ../extensions/libct_proto_sctp.la ../extensions/libct_proto_dccp.la ../extensions/libct_proto_gre.la ../extensions/libct_proto_unknown.la ${LIBNETFILTER_CONNTRACK_LIBS} ${LIBMNL_LIBS} ${LIBNFNETLINK_LIBS}
-nfct_SOURCES = nfct.c \
- helpers.c \
- nfct-extensions/timeout.c \
- nfct-extensions/helper.c
+nfct_SOURCES = nfct.c
+
+if HAVE_CTHELPER
+nfct_SOURCES += helpers.c \
+ nfct-extensions/helper.c
+endif
+
+if HAVE_CTTIMEOUT
+nfct_SOURCES += nfct-extensions/timeout.c
+endif
nfct_LDADD = ${LIBMNL_LIBS} \
- ${LIBNETFILTER_CONNTRACK_LIBS} \
- ${LIBNETFILTER_CTTIMEOUT_LIBS} \
- ${LIBNETFILTER_CTHELPER_LIBS} \
${libdl_LIBS}
+if HAVE_CTTIMEOUT
+nfct_LDADD += ${LIBNETFILTER_CTTIMEOUT_LIBS}
+endif
+
+if HAVE_CTHELPER
+nfct_LDADD += ${LIBNETFILTER_CTHELPER_LIBS}
+endif
+
nfct_LDFLAGS = -export-dynamic
conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
@@ -29,7 +42,7 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
filter.c fds.c event.c process.c origin.c date.c \
cache.c cache-ct.c cache-exp.c \
cache_timer.c \
- ctnl.c cthelper.c \
+ ctnl.c \
sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \
traffic_stats.c stats-mode.c \
network.c cidr.c \
@@ -39,15 +52,22 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \
external_cache.c external_inject.c \
internal_cache.c internal_bypass.c \
read_config_yy.y read_config_lex.l \
- stack.c helpers.c utils.c expect.c
+ stack.c
+
+if HAVE_CTHELPER
+conntrackd_SOURCES += cthelper.c helpers.c utils.c expect.c
+endif
# yacc and lex generate dirty code
read_config_yy.o read_config_lex.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-missing-declarations -Wno-implicit-function-declaration -Wno-nested-externs -Wno-undef -Wno-redundant-decls
conntrackd_LDADD = ${LIBMNL_LIBS} ${LIBNETFILTER_CONNTRACK_LIBS} \
- ${LIBNETFILTER_QUEUE_LIBS} ${LIBNETFILTER_CTHELPER_LIBS} \
${libdl_LIBS} ${LIBNFNETLINK_LIBS}
+if HAVE_CTHELPER
+conntrackd_LDADD += ${LIBNETFILTER_CTHELPER_LIBS} ${LIBNETFILTER_QUEUE_LIBS}
+endif
+
conntrackd_LDFLAGS = -export-dynamic
EXTRA_DIST = read_config_yy.h
diff --git a/src/cache.c b/src/cache.c
index 7c41e54..79a024f 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -34,7 +34,7 @@ struct cache_feature *cache_feature[CACHE_MAX_FEATURE] = {
};
struct cache *cache_create(const char *name, enum cache_type type,
- unsigned int features,
+ unsigned int features,
struct cache_extra *extra,
struct cache_ops *ops)
{
@@ -53,7 +53,8 @@ struct cache *cache_create(const char *name, enum cache_type type,
return NULL;
memset(c, 0, sizeof(struct cache));
- strcpy(c->name, name);
+ strncpy(c->name, name, CACHE_MAX_NAMELEN);
+ c->name[CACHE_MAX_NAMELEN - 1] = '\0';
c->type = type;
for (i = 0; i < CACHE_MAX_FEATURE; i++) {
diff --git a/src/channel.c b/src/channel.c
index 8b7c319..acbfa7d 100644
--- a/src/channel.c
+++ b/src/channel.c
@@ -109,6 +109,7 @@ channel_open(struct channel_conf *cfg)
if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) {
free(c);
+ close(fd);
return NULL;
}
close(fd);
diff --git a/src/conntrack.c b/src/conntrack.c
index 7d2a365..00b09b6 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -82,6 +82,9 @@ static struct {
/* Allows filtering by ctlabels */
struct nfct_bitmask *label;
+
+ /* Allows setting/removing specific ctlabels */
+ struct nfct_bitmask *label_modify;
} tmpl;
static int alloc_tmpl_objects(void)
@@ -109,6 +112,8 @@ static void free_tmpl_objects(void)
nfexp_destroy(tmpl.exp);
if (tmpl.label)
nfct_bitmask_destroy(tmpl.label);
+ if (tmpl.label_modify)
+ nfct_bitmask_destroy(tmpl.label_modify);
}
enum ct_command {
@@ -255,6 +260,12 @@ enum ct_options {
CT_OPT_LABEL_BIT = 24,
CT_OPT_LABEL = (1 << CT_OPT_LABEL_BIT),
+
+ CT_OPT_ADD_LABEL_BIT = 25,
+ CT_OPT_ADD_LABEL = (1 << CT_OPT_ADD_LABEL_BIT),
+
+ CT_OPT_DEL_LABEL_BIT = 26,
+ CT_OPT_DEL_LABEL = (1 << CT_OPT_DEL_LABEL_BIT),
};
/* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */
@@ -289,6 +300,8 @@ static const char *optflags[NUMBER_OF_OPT] = {
[CT_OPT_ANY_NAT_BIT] = "any-nat",
[CT_OPT_ZONE_BIT] = "zone",
[CT_OPT_LABEL_BIT] = "label",
+ [CT_OPT_ADD_LABEL_BIT] = "label-add",
+ [CT_OPT_DEL_LABEL_BIT] = "label-del",
};
static struct option original_opts[] = {
@@ -330,12 +343,14 @@ static struct option original_opts[] = {
{"any-nat", 2, 0, 'j'},
{"zone", 1, 0, 'w'},
{"label", 1, 0, 'l'},
+ {"label-add", 1, 0, '<'},
+ {"label-del", 2, 0, '>'},
{0, 0, 0, 0}
};
static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:"
"p:t:u:e:a:z[:]:{:}:m:i:f:o:n::"
- "g::c:b:C::Sj::w:l:";
+ "g::c:b:C::Sj::w:l:<:>::";
/* Table of legal combinations of commands and options. If any of the
* given commands make an option legal, that option is legal (applies to
@@ -350,26 +365,26 @@ static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:"
static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
/* Well, it's better than "Re: Linux vs FreeBSD" */
{
- /* s d r q p t u z e [ ] { } a m i f n g o c b j w l*/
-/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2},
-/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0},
-/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0},
-/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2,0},
-/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0},
-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2},
-/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0},
-/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0},
-/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
-/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+ /* s d r q p t u z e [ ] { } a m i f n g o c b j w l < > */
+/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2,0,0},
+/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0,2,0},
+/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,2,2,2},
+/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2,2,0,0},
+/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,2,0,0},
+/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2,0,0},
+/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0,0,0},
+/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0},
+/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
static const int cmd2type[][2] = {
@@ -402,6 +417,8 @@ static const int opt2type[] = {
['j'] = CT_OPT_ANY_NAT,
['w'] = CT_OPT_ZONE,
['l'] = CT_OPT_LABEL,
+ ['<'] = CT_OPT_ADD_LABEL,
+ ['>'] = CT_OPT_DEL_LABEL,
};
static const int opt2family_attr[][2] = {
@@ -420,11 +437,17 @@ static const int opt2attr[] = {
['d'] = ATTR_ORIG_L3PROTO,
['r'] = ATTR_REPL_L3PROTO,
['q'] = ATTR_REPL_L3PROTO,
+ ['{'] = ATTR_ORIG_L3PROTO,
+ ['}'] = ATTR_ORIG_L3PROTO,
+ ['['] = ATTR_ORIG_L3PROTO,
+ [']'] = ATTR_ORIG_L3PROTO,
['m'] = ATTR_MARK,
['c'] = ATTR_SECMARK,
['i'] = ATTR_ID,
['w'] = ATTR_ZONE,
['l'] = ATTR_CONNLABELS,
+ ['<'] = ATTR_CONNLABELS,
+ ['>'] = ATTR_CONNLABELS,
};
static char exit_msg[NUMBER_OF_CMD][64] = {
@@ -472,6 +495,11 @@ static const char usage_expectation_parameters[] =
" --mask-src ip\t\tSource mask address\n"
" --mask-dst ip\t\tDestination mask address\n";
+static const char usage_update_parameters[] =
+ "Updating parameters and options:\n"
+ " --label-add label\tAdd label\n"
+ " --label-del label\tDelete label\n";
+
static const char usage_parameters[] =
"Common parameters and options:\n"
" -s, --orig-src ip\t\tSource address from original direction\n"
@@ -523,7 +551,7 @@ static struct ctproto_handler *findproto(char *name, int *pnum)
/* is it in the list of supported protocol? */
list_for_each_entry(cur, &proto_list, head) {
- if (strcmp(cur->name, name) == 0) {
+ if (strcasecmp(cur->name, name) == 0) {
*pnum = cur->protonum;
return cur;
}
@@ -890,20 +918,20 @@ add_command(unsigned int *cmd, const int newcmd)
*cmd |= newcmd;
}
-static char *get_table(int argc, char *argv[])
+static char *get_optional_arg(int argc, char *argv[])
{
- char *table = NULL;
+ char *arg = NULL;
/* Nasty bug or feature in getopt_long ?
* It seems that it behaves badly with optional arguments.
* Fortunately, I just stole the fix from iptables ;) */
if (optarg)
- return 0;
+ return arg;
else if (optind < argc && argv[optind][0] != '-' &&
argv[optind][0] != '!')
- table = argv[optind++];
+ arg = argv[optind++];
- return table;
+ return arg;
}
enum {
@@ -915,7 +943,7 @@ enum {
static unsigned int check_type(int argc, char *argv[])
{
- const char *table = get_table(argc, argv);
+ const char *table = get_optional_arg(argc, argv);
/* default to conntrack subsystem if nothing has been specified. */
if (table == NULL)
@@ -1045,6 +1073,7 @@ usage(char *prog)
fprintf(stdout, "\n%s", usage_tables);
fprintf(stdout, "\n%s", usage_conntrack_parameters);
fprintf(stdout, "\n%s", usage_expectation_parameters);
+ fprintf(stdout, "\n%s", usage_update_parameters);
fprintf(stdout, "\n%s\n", usage_parameters);
}
@@ -1349,7 +1378,7 @@ static int print_cb(enum nf_conntrack_msg_type type,
if (output_mask & _O_ID)
op_flags |= NFCT_OF_ID;
- nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
+ nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap);
printf("%s\n", buf);
return NFCT_CB_CONTINUE;
@@ -1376,6 +1405,72 @@ static void copy_status(struct nf_conntrack *tmp, const struct nf_conntrack *ct)
}
}
+static struct nfct_bitmask *xnfct_bitmask_clone(const struct nfct_bitmask *a)
+{
+ struct nfct_bitmask *b = nfct_bitmask_clone(a);
+ if (!b)
+ exit_error(OTHER_PROBLEM, "out of memory");
+ return b;
+}
+
+static void copy_label(struct nf_conntrack *tmp, const struct nf_conntrack *ct)
+{
+ struct nfct_bitmask *ctb, *newmask;
+ unsigned int i;
+
+ if ((options & (CT_OPT_ADD_LABEL|CT_OPT_DEL_LABEL)) == 0)
+ return;
+
+ nfct_copy_attr(tmp, ct, ATTR_CONNLABELS);
+ ctb = (void *) nfct_get_attr(tmp, ATTR_CONNLABELS);
+
+ if (options & CT_OPT_ADD_LABEL) {
+ if (ctb == NULL) {
+ nfct_set_attr(tmp, ATTR_CONNLABELS,
+ xnfct_bitmask_clone(tmpl.label_modify));
+ return;
+ }
+ /* If we send a bitmask shorter than the kernel sent to us, the bits we
+ * omit will be cleared (as "padding"). So we always have to send the
+ * same sized bitmask as we received.
+ *
+ * Mask has to have the same size as the labels, otherwise it will not
+ * be encoded by libnetfilter_conntrack, as different sizes are not
+ * accepted by the kernel.
+ */
+ newmask = nfct_bitmask_new(nfct_bitmask_maxbit(ctb));
+
+ for (i = 0; i <= nfct_bitmask_maxbit(ctb); i++) {
+ if (nfct_bitmask_test_bit(tmpl.label_modify, i)) {
+ nfct_bitmask_set_bit(ctb, i);
+ nfct_bitmask_set_bit(newmask, i);
+ } else if (nfct_bitmask_test_bit(ctb, i)) {
+ /* Kernel only retains old bit values that are sent as
+ * zeroes in BOTH labels and mask.
+ */
+ nfct_bitmask_unset_bit(ctb, i);
+ }
+ }
+ nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask);
+ } else if (ctb != NULL) {
+ /* CT_OPT_DEL_LABEL */
+ if (tmpl.label_modify == NULL) {
+ newmask = nfct_bitmask_new(0);
+ if (newmask)
+ nfct_set_attr(tmp, ATTR_CONNLABELS, newmask);
+ return;
+ }
+
+ for (i = 0; i <= nfct_bitmask_maxbit(ctb); i++) {
+ if (nfct_bitmask_test_bit(tmpl.label_modify, i))
+ nfct_bitmask_unset_bit(ctb, i);
+ }
+
+ newmask = xnfct_bitmask_clone(tmpl.label_modify);
+ nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask);
+ }
+}
+
static int update_cb(enum nf_conntrack_msg_type type,
struct nf_conntrack *ct,
void *data)
@@ -1395,6 +1490,9 @@ static int update_cb(enum nf_conntrack_msg_type type,
if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL))
return NFCT_CB_CONTINUE;
+ if (filter_label(ct))
+ return NFCT_CB_CONTINUE;
+
tmp = nfct_new();
if (tmp == NULL)
exit_error(OTHER_PROBLEM, "out of memory");
@@ -1403,6 +1501,7 @@ static int update_cb(enum nf_conntrack_msg_type type,
nfct_copy(tmp, obj, NFCT_CP_META);
copy_mark(tmp, ct, &tmpl.mark);
copy_status(tmp, ct);
+ copy_label(tmp, ct);
/* do not send NFCT_Q_UPDATE if ct appears unchanged */
if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) {
@@ -1411,12 +1510,10 @@ static int update_cb(enum nf_conntrack_msg_type type,
}
res = nfct_query(ith, NFCT_Q_UPDATE, tmp);
- if (res < 0) {
- nfct_destroy(tmp);
- exit_error(OTHER_PROBLEM,
- "Operation failed: %s",
+ if (res < 0)
+ fprintf(stderr,
+ "Operation failed: %s\n",
err2str(errno, CT_UPDATE));
- }
nfct_callback_register(ith, NFCT_T_ALL, print_cb, NULL);
res = nfct_query(ith, NFCT_Q_GET, tmp);
@@ -1601,7 +1698,8 @@ static int nfct_mnl_socket_open(void)
}
static struct nlmsghdr *
-nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type)
+nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type,
+ uint8_t family)
{
struct nlmsghdr *nlh;
struct nfgenmsg *nfh;
@@ -1612,7 +1710,7 @@ nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type)
nlh->nlmsg_seq = time(NULL);
nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
- nfh->nfgen_family = AF_INET;
+ nfh->nfgen_family = family;
nfh->version = NFNETLINK_V0;
nfh->res_id = 0;
@@ -1625,13 +1723,13 @@ static void nfct_mnl_socket_close(void)
}
static int
-nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb)
+nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb, uint8_t family)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
int res;
- nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type);
+ nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, family);
res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len);
if (res < 0)
@@ -1651,13 +1749,13 @@ nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb)
}
static int
-nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb)
+nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb, uint8_t family)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
int res;
- nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type);
+ nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, family);
res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len);
if (res < 0)
@@ -1818,6 +1916,65 @@ static int mnl_nfct_dump_cb(const struct nlmsghdr *nlh, void *data)
static struct ctproto_handler *h;
+static void labelmap_init(void)
+{
+ if (labelmap)
+ return;
+ labelmap = nfct_labelmap_new(NULL);
+ if (!labelmap)
+ perror("nfct_labelmap_new");
+}
+
+static void merge_bitmasks(struct nfct_bitmask **current,
+ struct nfct_bitmask *src)
+{
+ unsigned int i;
+
+ if (*current == NULL) {
+ *current = src;
+ return;
+ }
+
+ /* "current" must be the larger bitmask object */
+ if (nfct_bitmask_maxbit(src) > nfct_bitmask_maxbit(*current)) {
+ struct nfct_bitmask *tmp = *current;
+ *current = src;
+ src = tmp;
+ }
+
+ for (i = 0; i <= nfct_bitmask_maxbit(src); i++) {
+ if (nfct_bitmask_test_bit(src, i))
+ nfct_bitmask_set_bit(*current, i);
+ }
+
+ nfct_bitmask_destroy(src);
+}
+
+static void
+nfct_set_addr_from_opt(int opt, struct nf_conntrack *ct, union ct_address *ad,
+ int *family)
+{
+ int l3protonum;
+
+ options |= opt2type[opt];
+ l3protonum = parse_addr(optarg, ad);
+ if (l3protonum == AF_UNSPEC) {
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid IP address `%s'", optarg);
+ }
+ set_family(family, l3protonum);
+ if (l3protonum == AF_INET) {
+ nfct_set_attr_u32(ct,
+ opt2family_attr[opt][0],
+ ad->v4);
+ } else if (l3protonum == AF_INET6) {
+ nfct_set_attr(ct,
+ opt2family_attr[opt][1],
+ &ad->v6);
+ }
+ nfct_set_attr_u8(ct, opt2attr[opt], l3protonum);
+}
+
int main(int argc, char *argv[])
{
int c, cmd;
@@ -1825,7 +1982,7 @@ int main(int argc, char *argv[])
int res = 0, partial;
size_t socketbuffersize = 0;
int family = AF_UNSPEC;
- int l3protonum, protonum = 0;
+ int protonum = 0;
union ct_address ad;
unsigned int command = 0;
@@ -1896,47 +2053,15 @@ int main(int argc, char *argv[])
case 'd':
case 'r':
case 'q':
- options |= opt2type[c];
-
- l3protonum = parse_addr(optarg, &ad);
- if (l3protonum == AF_UNSPEC) {
- exit_error(PARAMETER_PROBLEM,
- "Invalid IP address `%s'", optarg);
- }
- set_family(&family, l3protonum);
- if (l3protonum == AF_INET) {
- nfct_set_attr_u32(tmpl.ct,
- opt2family_attr[c][0],
- ad.v4);
- } else if (l3protonum == AF_INET6) {
- nfct_set_attr(tmpl.ct,
- opt2family_attr[c][1],
- &ad.v6);
- }
- nfct_set_attr_u8(tmpl.ct, opt2attr[c], l3protonum);
+ nfct_set_addr_from_opt(c, tmpl.ct, &ad, &family);
break;
case '{':
case '}':
+ nfct_set_addr_from_opt(c, tmpl.exptuple, &ad, &family);
+ break;
case '[':
case ']':
- options |= opt2type[c];
- l3protonum = parse_addr(optarg, &ad);
- if (l3protonum == AF_UNSPEC) {
- exit_error(PARAMETER_PROBLEM,
- "Invalid IP address `%s'", optarg);
- }
- set_family(&family, l3protonum);
- if (l3protonum == AF_INET) {
- nfct_set_attr_u32(tmpl.mask,
- opt2family_attr[c][0],
- ad.v4);
- } else if (l3protonum == AF_INET6) {
- nfct_set_attr(tmpl.mask,
- opt2family_attr[c][1],
- &ad.v6);
- }
- nfct_set_attr_u8(tmpl.mask,
- ATTR_ORIG_L3PROTO, l3protonum);
+ nfct_set_addr_from_opt(c, tmpl.mask, &ad, &family);
break;
case 'p':
options |= CT_OPT_PROTO;
@@ -1970,12 +2095,8 @@ int main(int argc, char *argv[])
case 'o':
options |= CT_OPT_OUTPUT;
parse_parameter(optarg, &output_mask, PARSE_OUTPUT);
- if (output_mask & _O_CL) {
- if (!labelmap)
- labelmap = nfct_labelmap_new(NULL);
- if (!labelmap)
- perror("nfct_labelmap_new");
- }
+ if (output_mask & _O_CL)
+ labelmap_init();
break;
case 'z':
options |= CT_OPT_ZERO;
@@ -1987,12 +2108,7 @@ int main(int argc, char *argv[])
options |= opt2type[c];
- if (optarg)
- continue;
- else if (optind < argc && argv[optind][0] != '-'
- && argv[optind][0] != '!')
- tmp = argv[optind++];
-
+ tmp = get_optional_arg(argc, argv);
if (tmp == NULL)
continue;
@@ -2020,19 +2136,39 @@ int main(int argc, char *argv[])
tmpl.filter_mark_kernel.mask = tmpl.mark.mask;
break;
case 'l':
+ case '<':
+ case '>':
options |= opt2type[c];
- char *optarg2 = strdup(optarg);
- if (!labelmap)
- labelmap = nfct_labelmap_new(NULL);
- if (!labelmap)
- exit_error(OTHER_PROBLEM, "unable to open labelmap file");
+ labelmap_init();
+ if ((options & (CT_OPT_DEL_LABEL|CT_OPT_ADD_LABEL)) ==
+ (CT_OPT_DEL_LABEL|CT_OPT_ADD_LABEL))
+ exit_error(OTHER_PROBLEM, "cannot use --label-add and "
+ "--label-del at the same time");
+
+ if (c == '>') { /* DELETE */
+ char *tmp = get_optional_arg(argc, argv);
+ if (tmp == NULL) /* delete all labels */
+ break;
+ optarg = tmp;
+ }
+
+ char *optarg2 = strdup(optarg);
unsigned int max = parse_label_get_max(optarg);
struct nfct_bitmask * b = nfct_bitmask_new(max);
+ if (!b)
+ exit_error(OTHER_PROBLEM, "out of memory");
parse_label(b, optarg2);
- tmpl.label = b;
+
+ /* join "-l foo -l bar" into single bitmask object */
+ if (c == 'l') {
+ merge_bitmasks(&tmpl.label, b);
+ } else {
+ merge_bitmasks(&tmpl.label_modify, b);
+ }
+
free(optarg2);
break;
case 'a':
@@ -2114,7 +2250,7 @@ int main(int argc, char *argv[])
res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK,
IPCTNL_MSG_CT_GET_DYING,
- mnl_nfct_dump_cb);
+ mnl_nfct_dump_cb, family);
nfct_mnl_socket_close();
break;
@@ -2124,7 +2260,7 @@ int main(int argc, char *argv[])
res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK,
IPCTNL_MSG_CT_GET_UNCONFIRMED,
- mnl_nfct_dump_cb);
+ mnl_nfct_dump_cb, family);
nfct_mnl_socket_close();
break;
@@ -2191,6 +2327,10 @@ int main(int argc, char *argv[])
if (options & CT_OPT_MARK)
nfct_set_attr_u32(tmpl.ct, ATTR_MARK, tmpl.mark.value);
+ if (options & CT_OPT_ADD_LABEL)
+ nfct_set_attr(tmpl.ct, ATTR_CONNLABELS,
+ xnfct_bitmask_clone(tmpl.label_modify));
+
cth = nfct_open(CONNTRACK, 0);
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
@@ -2389,7 +2529,7 @@ int main(int argc, char *argv[])
res = nfct_mnl_get(NFNL_SUBSYS_CTNETLINK,
IPCTNL_MSG_CT_GET_STATS,
- nfct_global_stats_cb);
+ nfct_global_stats_cb, AF_UNSPEC);
nfct_mnl_socket_close();
@@ -2434,7 +2574,7 @@ try_proc_count:
res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK,
IPCTNL_MSG_CT_GET_STATS_CPU,
- nfct_stats_cb);
+ nfct_stats_cb, AF_UNSPEC);
nfct_mnl_socket_close();
@@ -2453,7 +2593,7 @@ try_proc_count:
res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK_EXP,
IPCTNL_MSG_EXP_GET_STATS_CPU,
- nfexp_stats_cb);
+ nfexp_stats_cb, AF_UNSPEC);
nfct_mnl_socket_close();
diff --git a/src/cthelper.c b/src/cthelper.c
index 5a8a92a..54eb830 100644
--- a/src/cthelper.c
+++ b/src/cthelper.c
@@ -31,6 +31,7 @@
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
+#define _GNU_SOURCE
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <net/ethernet.h>
@@ -182,6 +183,15 @@ pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct,
nfct_nlmsg_build(nlh, myct->ct);
mnl_attr_nest_end(nlh, nest);
+ if (myct->exp) {
+ nest = mnl_attr_nest_start(nlh, NFQA_EXP);
+ if (nest == NULL)
+ return -1;
+
+ nfexp_nlmsg_build(nlh, myct->exp);
+ mnl_attr_nest_end(nlh, nest);
+ }
+
if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) {
dlog(LOG_ERR, "failed to send verdict: %s", strerror(errno));
return -1;
@@ -267,11 +277,11 @@ static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data)
if (!attr[NFQA_PAYLOAD]) {
dlog(LOG_ERR, "packet with no payload");
- goto err;
+ goto err1;
}
if (!attr[NFQA_CT] || !attr[NFQA_CT_INFO]) {
dlog(LOG_ERR, "no CT attached to this packet");
- goto err;
+ goto err1;
}
pkt = mnl_attr_get_payload(attr[NFQA_PAYLOAD]);
@@ -282,22 +292,22 @@ static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data)
queue_num = ntohs(nfg->res_id);
if (pkt_get(pkt, pktlen, ntohs(ph->hw_protocol), &protoff))
- goto err;
+ goto err1;
ct = nfct_new();
if (ct == NULL)
- goto err;
+ goto err1;
if (nfct_payload_parse(mnl_attr_get_payload(attr[NFQA_CT]),
mnl_attr_get_payload_len(attr[NFQA_CT]),
l3num, ct) < 0) {
dlog(LOG_ERR, "cannot convert message to CT");
- goto err;
+ goto err2;
}
myct = calloc(1, sizeof(struct myct));
if (myct == NULL)
- goto err;
+ goto err2;
myct->ct = ct;
ctinfo = ntohl(mnl_attr_get_u32(attr[NFQA_CT_INFO]));
@@ -305,36 +315,37 @@ static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data)
/* XXX: 256 bytes enough for possible NAT mangling in helpers? */
pktb = pktb_alloc(AF_INET, pkt, pktlen, 256);
if (pktb == NULL)
- goto err;
+ goto err3;
/* Misconfiguration: if no helper found, accept the packet. */
helper = helper_run(pktb, protoff, myct, ctinfo, queue_num, &verdict);
if (!helper)
- goto err_pktb;
+ goto err4;
if (pkt_verdict_issue(helper, myct, queue_num, id, verdict, pktb) < 0)
- goto err_pktb;
+ goto err4;
- if (ct != NULL)
- nfct_destroy(ct);
- if (myct && myct->priv_data != NULL)
+ nfct_destroy(ct);
+ if (myct->exp != NULL)
+ nfexp_destroy(myct->exp);
+ if (myct->priv_data != NULL)
free(myct->priv_data);
- if (myct != NULL)
- free(myct);
+ free(myct);
return MNL_CB_OK;
-err_pktb:
+err4:
pktb_free(pktb);
-err:
+err3:
+ free(myct);
+err2:
+ nfct_destroy(ct);
+err1:
/* In case of error, we don't want to disrupt traffic. We accept all.
* This is connection tracking after all. The policy is not to drop
* packet unless we enter some inconsistent state.
*/
pkt_verdict_error(queue_num, id);
- if (ct != NULL)
- nfct_destroy(ct);
-
return MNL_CB_OK;
}
@@ -458,7 +469,10 @@ static int cthelper_nfqueue_setup(struct ctd_helper_instance *cur)
nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff);
mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_CONNTRACK));
mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(0xffffffff));
- mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, htonl(cur->queue_len));
+ if (cur->queue_len > 0) {
+ mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN,
+ htonl(cur->queue_len));
+ }
if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) {
dlog(LOG_ERR, "failed to send configuration");
diff --git a/src/expect.c b/src/expect.c
index 470b9ae..5add7be 100644
--- a/src/expect.c
+++ b/src/expect.c
@@ -39,8 +39,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master,
if (saddr) {
switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) {
- int i;
- uint32_t addr[4] = {};
+ uint32_t addr[4];
case AF_INET:
nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET);
@@ -52,10 +51,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master,
case AF_INET6:
nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6);
nfct_set_attr(expected, ATTR_IPV6_SRC, saddr->ip6);
-
- for (i=0; i<4; i++)
- memset(&addr[i], 0xffffffff, sizeof(uint32_t));
-
+ memset(addr, 0xff, sizeof(addr));
nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6);
nfct_set_attr(mask, ATTR_IPV6_SRC, addr);
break;
@@ -64,8 +60,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master,
}
} else {
switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) {
- int i;
- uint32_t addr[4] = {};
+ uint32_t addr[4];
case AF_INET:
nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET);
@@ -75,9 +70,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master,
nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0x00000000);
break;
case AF_INET6:
- for (i=0; i<4; i++)
- memset(&addr[i], 0x00000000, sizeof(uint32_t));
-
+ memset(addr, 0x00, sizeof(addr));
nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6);
nfct_set_attr(expected, ATTR_IPV6_SRC, addr);
@@ -116,8 +109,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master,
}
switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) {
- uint32_t addr[4] = {};
- int i;
+ uint32_t addr[4];
case AF_INET:
nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET);
@@ -127,10 +119,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master,
case AF_INET6:
nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6);
nfct_set_attr(expected, ATTR_IPV6_DST, daddr->ip6);
-
- for (i=0; i<4; i++)
- memset(addr, 0xffffffff, sizeof(uint32_t));
-
+ memset(addr, 0xff, sizeof(addr));
nfct_set_attr(mask, ATTR_IPV6_DST, addr);
break;
default:
@@ -212,3 +201,27 @@ cthelper_get_addr_dst(struct nf_conntrack *ct, int dir,
break;
}
}
+
+void cthelper_get_port_src(struct nf_conntrack *ct, int dir, uint16_t *port)
+{
+ switch (dir) {
+ case MYCT_DIR_ORIG:
+ *port = nfct_get_attr_u16(ct, ATTR_PORT_SRC);
+ break;
+ case MYCT_DIR_REPL:
+ *port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC);
+ break;
+ }
+}
+
+void cthelper_get_port_dst(struct nf_conntrack *ct, int dir, uint16_t *port)
+{
+ switch (dir) {
+ case MYCT_DIR_ORIG:
+ *port = nfct_get_attr_u16(ct, ATTR_PORT_DST);
+ break;
+ case MYCT_DIR_REPL:
+ *port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST);
+ break;
+ }
+}
diff --git a/src/filter.c b/src/filter.c
index 8fac71b..1ae2cc5 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -33,8 +33,8 @@
struct ct_filter {
int logic[CT_FILTER_MAX];
- u_int32_t l4protomap[IPPROTO_MAX/32];
- u_int16_t statemap[IPPROTO_MAX];
+ uint32_t l4protomap[IPPROTO_MAX/32];
+ uint16_t statemap[IPPROTO_MAX];
struct hashtable *h;
struct hashtable *h6;
struct vector *v;
diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am
index 589b4f3..78ef7aa 100644
--- a/src/helpers/Makefile.am
+++ b/src/helpers/Makefile.am
@@ -1,8 +1,21 @@
include $(top_srcdir)/Make_global.am
-pkglib_LTLIBRARIES = ct_helper_ftp.la \
+pkglib_LTLIBRARIES = ct_helper_amanda.la \
+ ct_helper_dhcpv6.la \
+ ct_helper_ftp.la \
ct_helper_rpc.la \
- ct_helper_tns.la
+ ct_helper_tftp.la \
+ ct_helper_tns.la \
+ ct_helper_sane.la \
+ ct_helper_ssdp.la
+
+ct_helper_amanda_la_SOURCES = amanda.c
+ct_helper_amanda_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
+ct_helper_amanda_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS)
+
+ct_helper_dhcpv6_la_SOURCES = dhcpv6.c
+ct_helper_dhcpv6_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
+ct_helper_dhcpv6_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS)
ct_helper_ftp_la_SOURCES = ftp.c
ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
@@ -12,6 +25,18 @@ ct_helper_rpc_la_SOURCES = rpc.c
ct_helper_rpc_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
ct_helper_rpc_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS)
+ct_helper_tftp_la_SOURCES = tftp.c
+ct_helper_tftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
+ct_helper_tftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS)
+
ct_helper_tns_la_SOURCES = tns.c
ct_helper_tns_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
ct_helper_tns_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS)
+
+ct_helper_sane_la_SOURCES = sane.c
+ct_helper_sane_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
+ct_helper_sane_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS)
+
+ct_helper_ssdp_la_SOURCES = ssdp.c
+ct_helper_ssdp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS)
+ct_helper_ssdp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS)
diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c
new file mode 100644
index 0000000..9e6c4e7
--- /dev/null
+++ b/src/helpers/amanda.c
@@ -0,0 +1,203 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Adapted from:
+ *
+ * Amanda extension for IP connection tracking
+ *
+ * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
+ * based on HW's ip_conntrack_irc.c as well as other modules
+ * (C) 2006 Patrick McHardy <kaber@trash.net>
+ *
+ * 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.
+ */
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_udp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+static int nat_amanda(struct pkt_buff *pkt, uint32_t ctinfo,
+ unsigned int matchoff, unsigned int matchlen,
+ struct nf_expect *exp)
+{
+ char buffer[sizeof("65535")];
+ uint16_t port, initial_port;
+ unsigned int ret;
+ const struct nf_conntrack *expected;
+ struct nf_conntrack *nat_tuple;
+
+ nat_tuple = nfct_new();
+ if (nat_tuple == NULL)
+ return NF_ACCEPT;
+
+ expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED);
+
+ /* Connection comes from client. */
+ initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST);
+ nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, IP_CT_DIR_ORIGINAL);
+
+ /* libnetfilter_conntrack needs this */
+ nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0);
+ nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0);
+ nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP);
+ nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0);
+
+ /* When you see the packet, we need to NAT it the same as the
+ * this one (ie. same IP: it will be TCP and master is UDP). */
+ nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master");
+
+ /* Try to get same port: if not, try to change it. */
+ for (port = ntohs(initial_port); port != 0; port++) {
+ int res;
+
+ nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port));
+ nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple);
+
+ res = cthelper_add_expect(exp);
+ if (res == 0)
+ break;
+ else if (res != -EBUSY) {
+ port = 0;
+ break;
+ }
+ }
+
+ if (port == 0) {
+ pr_debug("all ports in use\n");
+ return NF_DROP;
+ }
+
+ sprintf(buffer, "%u", port);
+ ret = nfq_udp_mangle_ipv4(pkt, matchoff, matchlen, buffer,
+ strlen(buffer));
+ if (ret != NF_ACCEPT) {
+ pr_debug("cannot mangle packet\n");
+ cthelper_del_expect(exp);
+ }
+ return ret;
+}
+
+static char amanda_buffer[65536];
+static unsigned int master_timeout = 300;
+
+enum amanda_strings {
+ SEARCH_CONNECT,
+ SEARCH_NEWLINE,
+ SEARCH_DATA,
+ SEARCH_MESG,
+ SEARCH_INDEX,
+};
+
+static const char *conns[] = { "DATA ", "MESG ", "INDEX " };
+
+static int
+amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+ struct myct *myct, uint32_t ctinfo)
+{
+ struct nf_expect *exp;
+ char *data, *data_limit, *tmp;
+ unsigned int dataoff, i;
+ uint16_t port, len;
+ int ret = NF_ACCEPT;
+ struct iphdr *iph;
+ union nfct_attr_grp_addr saddr, daddr;
+
+ /* Only look at packets from the Amanda server */
+ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
+ return NF_ACCEPT;
+
+ /* increase the UDP timeout of the master connection as replies from
+ * Amanda clients to the server can be quite delayed */
+ nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, master_timeout);
+
+ /* No data? */
+ iph = (struct iphdr *)pktb_network_header(pkt);
+ dataoff = iph->ihl*4 + sizeof(struct udphdr);
+ if (dataoff >= pktb_len(pkt)) {
+ pr_debug("amanda_help: pktlen = %u\n", pktb_len(pkt));
+ return NF_ACCEPT;
+ }
+
+ memcpy(amanda_buffer, pktb_network_header(pkt) + dataoff,
+ pktb_len(pkt) - dataoff);
+ data = amanda_buffer;
+ data_limit = amanda_buffer + pktb_len(pkt) - dataoff;
+ *data_limit = '\0';
+
+ /* Search for the CONNECT string */
+ data = strstr(data, "CONNECT ");
+ if (!data)
+ goto out;
+ data += strlen("CONNECT ");
+
+ /* Only search first line. */
+ if ((tmp = strchr(data, '\n')))
+ *tmp = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(conns); i++) {
+ char *match = strstr(data, conns[i]);
+ if (!match)
+ continue;
+ tmp = data = match + strlen(conns[i]);
+ port = strtoul(data, &data, 10);
+ len = data - tmp;
+ if (port == 0 || len > 5)
+ break;
+
+ exp = nfexp_new();
+ if (exp == NULL)
+ return NF_DROP;
+
+ cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr);
+ cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr);
+ cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port);
+
+ if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
+ IPPROTO_TCP, NULL, &port, 0)) {
+ nfexp_destroy(exp);
+ return NF_DROP;
+ }
+
+ if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) {
+ ret = nat_amanda(pkt, ctinfo, tmp - amanda_buffer,
+ len, exp);
+ } else
+ myct->exp = exp;
+ }
+out:
+ return ret;
+}
+
+static struct ctd_helper amanda_helper = {
+ .name = "amanda",
+ .l4proto = IPPROTO_UDP,
+ .cb = amanda_helper_cb,
+ .policy = {
+ [0] = {
+ .name = "amanda",
+ .expect_max = ARRAY_SIZE(conns),
+ .expect_timeout = 180,
+ },
+ },
+};
+
+void __attribute__ ((constructor)) amanda_init(void);
+
+void amanda_init(void)
+{
+ helper_register(&amanda_helper);
+}
diff --git a/src/helpers/dhcpv6.c b/src/helpers/dhcpv6.c
new file mode 100644
index 0000000..73632ec
--- /dev/null
+++ b/src/helpers/dhcpv6.c
@@ -0,0 +1,123 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Adapted from:
+ *
+ * DHCPv6 multicast connection tracking helper.
+ *
+ * (c) 2012 Google Inc.
+ *
+ * Original author: Darren Willis <djw@google.com>
+ *
+ * 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.
+ */
+
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_udp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+#define DHCPV6_CLIENT_PORT 546
+
+static uint16_t dhcpv6_port;
+
+/* Timeouts for DHCPv6 replies, in seconds, indexed by message type. */
+static const int dhcpv6_timeouts[] = {
+ 0, /* No message has type 0. */
+ 120, /* Solicit. */
+ 0, /* Advertise. */
+ 30, /* Request. */
+ 4, /* Confirm. */
+ 600, /* Renew. */
+ 600, /* Rebind. */
+ 0, /* Reply. */
+ 1, /* Release. */
+ 1, /* Decline. */
+ 0, /* Reconfigure. */
+ 120, /* Information Request. */
+ 0, /* Relay-forward. */
+ 0 /* Relay-reply. */
+};
+
+static inline int ipv6_addr_is_multicast(const struct in6_addr *addr)
+{
+ return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000);
+}
+
+static int
+dhcpv6_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+ struct myct *myct, uint32_t ctinfo)
+{
+ struct iphdr *iph = (struct iphdr *)pktb_network_header(pkt);
+ struct ip6_hdr *ip6h = (struct ip6_hdr *)pktb_network_header(pkt);
+ int dir = CTINFO2DIR(ctinfo);
+ union nfct_attr_grp_addr addr;
+ struct nf_expect *exp;
+ uint8_t *dhcpv6_msg_type;
+
+ if (iph->version != 6 || !ipv6_addr_is_multicast(&ip6h->ip6_dst))
+ return NF_ACCEPT;
+
+ dhcpv6_msg_type = pktb_network_header(pkt) + protoff + sizeof(struct udphdr);
+ if (*dhcpv6_msg_type > ARRAY_SIZE(dhcpv6_timeouts)) {
+ printf("Dropping DHCPv6 message with bad type %u\n",
+ *dhcpv6_msg_type);
+ return NF_DROP;
+ }
+
+ exp = nfexp_new();
+ if (exp == NULL)
+ return NF_ACCEPT;
+
+ cthelper_get_addr_src(myct->ct, dir, &addr);
+
+ if (cthelper_expect_init(exp, myct->ct, 0, NULL, &addr,
+ IPPROTO_UDP, NULL, &dhcpv6_port,
+ NF_CT_EXPECT_PERMANENT)) {
+ nfexp_destroy(exp);
+ return NF_DROP;
+ }
+
+ myct->exp = exp;
+
+ if (dhcpv6_timeouts[*dhcpv6_msg_type] > 0) {
+ nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT,
+ dhcpv6_timeouts[*dhcpv6_msg_type]);
+ }
+
+ return NF_ACCEPT;
+}
+
+static struct ctd_helper dhcpv6_helper = {
+ .name = "dhcpv6",
+ .l4proto = IPPROTO_UDP,
+ .cb = dhcpv6_helper_cb,
+ .policy = {
+ [0] = {
+ .name = "dhcpv6",
+ .expect_max = 1,
+ .expect_timeout = 300,
+ },
+ },
+};
+
+void __attribute__ ((constructor)) dhcpv6_init(void);
+
+void dhcpv6_init(void)
+{
+ dhcpv6_port = htons(DHCPV6_CLIENT_PORT);
+ helper_register(&dhcpv6_helper);
+}
diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c
index 2c8dcd6..24ee877 100644
--- a/src/helpers/ftp.c
+++ b/src/helpers/ftp.c
@@ -25,6 +25,7 @@
#include <ctype.h> /* for isdigit */
#include <errno.h>
+#define _GNU_SOURCE
#include <netinet/tcp.h>
#include <libmnl/libmnl.h>
@@ -58,7 +59,7 @@ enum nf_ct_ftp_type {
};
static int
-get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term)
+get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, uint8_t term)
{
const char *end;
int ret = in6_pton(src, min_t(size_t, dlen, 0xffff),
diff --git a/src/helpers/sane.c b/src/helpers/sane.c
new file mode 100644
index 0000000..c30f4ba
--- /dev/null
+++ b/src/helpers/sane.c
@@ -0,0 +1,173 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Port this helper to userspace.
+ */
+
+/* SANE connection tracking helper
+ * (SANE = Scanner Access Now Easy)
+ * For documentation about the SANE network protocol see
+ * http://www.sane-project.org/html/doc015.html
+ */
+
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ * Author: Michal Schmidt <mschmidt@redhat.com>
+ * Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c):
+ * (C) 1999-2001 Paul `Rusty' Russell
+ * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ * (C) 2003 Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <netinet/ip.h>
+#define _GNU_SOURCE
+#include <netinet/tcp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+enum sane_state {
+ SANE_STATE_NORMAL,
+ SANE_STATE_START_REQUESTED,
+};
+
+struct sane_request {
+ uint32_t RPC_code;
+#define SANE_NET_START 7 /* RPC code */
+
+ uint32_t handle;
+};
+
+struct sane_reply_net_start {
+ uint32_t status;
+#define SANE_STATUS_SUCCESS 0
+
+ uint16_t zero;
+ uint16_t port;
+ /* other fields aren't interesting for conntrack */
+};
+
+struct nf_ct_sane_master {
+ enum sane_state state;
+};
+
+static int
+sane_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+ struct myct *myct, uint32_t ctinfo)
+{
+ unsigned int dataoff, datalen;
+ const struct tcphdr *th;
+ void *sb_ptr;
+ int ret = NF_ACCEPT;
+ int dir = CTINFO2DIR(ctinfo);
+ struct nf_ct_sane_master *ct_sane_info = myct->priv_data;
+ struct nf_expect *exp;
+ struct sane_request *req;
+ struct sane_reply_net_start *reply;
+ union nfct_attr_grp_addr saddr;
+ union nfct_attr_grp_addr daddr;
+
+ /* Until there's been traffic both ways, don't look in packets. */
+ if (ctinfo != IP_CT_ESTABLISHED &&
+ ctinfo != IP_CT_ESTABLISHED_REPLY)
+ return NF_ACCEPT;
+
+ th = (struct tcphdr *)(pktb_network_header(pkt) + protoff);
+
+ /* No data? */
+ dataoff = protoff + th->doff * 4;
+ if (dataoff >= pktb_len(pkt))
+ return NF_ACCEPT;
+
+ datalen = pktb_len(pkt) - dataoff;
+
+ sb_ptr = pktb_network_header(pkt) + dataoff;
+
+ if (dir == MYCT_DIR_ORIG) {
+ if (datalen != sizeof(struct sane_request))
+ goto out;
+
+ req = sb_ptr;
+ if (req->RPC_code != htonl(SANE_NET_START)) {
+ /* Not an interesting command */
+ ct_sane_info->state = SANE_STATE_NORMAL;
+ goto out;
+ }
+
+ /* We're interested in the next reply */
+ ct_sane_info->state = SANE_STATE_START_REQUESTED;
+ goto out;
+ }
+
+ /* Is it a reply to an uninteresting command? */
+ if (ct_sane_info->state != SANE_STATE_START_REQUESTED)
+ goto out;
+
+ /* It's a reply to SANE_NET_START. */
+ ct_sane_info->state = SANE_STATE_NORMAL;
+
+ if (datalen < sizeof(struct sane_reply_net_start)) {
+ pr_debug("nf_ct_sane: NET_START reply too short\n");
+ goto out;
+ }
+
+ reply = sb_ptr;
+ if (reply->status != htonl(SANE_STATUS_SUCCESS)) {
+ /* saned refused the command */
+ pr_debug("nf_ct_sane: unsuccessful SANE_STATUS = %u\n",
+ ntohl(reply->status));
+ goto out;
+ }
+
+ /* Invalid saned reply? Ignore it. */
+ if (reply->zero != 0)
+ goto out;
+
+ exp = nfexp_new();
+ if (exp == NULL)
+ return NF_DROP;
+
+ cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr);
+ cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr);
+
+ if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
+ IPPROTO_TCP, NULL, &reply->port, 0)) {
+ nfexp_destroy(exp);
+ return NF_DROP;
+ }
+ myct->exp = exp;
+out:
+ return ret;
+}
+
+static struct ctd_helper sane_helper = {
+ .name = "sane",
+ .l4proto = IPPROTO_TCP,
+ .priv_data_len = sizeof(struct nf_ct_sane_master),
+ .cb = sane_helper_cb,
+ .policy = {
+ [0] = {
+ .name = "sane",
+ .expect_max = 1,
+ .expect_timeout = 5 * 60,
+ },
+ },
+};
+
+static void __attribute__ ((constructor)) sane_init(void)
+{
+ helper_register(&sane_helper);
+}
diff --git a/src/helpers/ssdp.c b/src/helpers/ssdp.c
new file mode 100644
index 0000000..bc41087
--- /dev/null
+++ b/src/helpers/ssdp.c
@@ -0,0 +1,134 @@
+/*
+ * SSDP connection tracking helper
+ * (SSDP = Simple Service Discovery Protocol)
+ * For documentation about SSDP see
+ * http://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol
+ *
+ * Copyright (C) 2014 Ashley Hughes <ashley.hughes@blueyonder.co.uk>
+ * Based on the SSDP conntrack helper (nf_conntrack_ssdp.c),
+ * :http://marc.info/?t=132945775100001&r=1&w=2
+ * (C) 2012 Ian Pilcher <arequipeno@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+#define SSDP_MCAST_ADDR "239.255.255.250"
+#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
+#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
+
+#define SSDP_M_SEARCH "M-SEARCH"
+#define SSDP_M_SEARCH_SIZE (sizeof SSDP_M_SEARCH - 1)
+
+static int ssdp_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+ struct myct *myct, uint32_t ctinfo)
+{
+ int ret = NF_ACCEPT;
+ union nfct_attr_grp_addr daddr, saddr, taddr;
+ struct iphdr *net_hdr = (struct iphdr *)pktb_network_header(pkt);
+ int good_packet = 0;
+ struct nf_expect *exp;
+ uint16_t port;
+ unsigned int dataoff;
+ void *sb_ptr;
+
+ cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr);
+ switch (nfct_get_attr_u8(myct->ct, ATTR_L3PROTO)) {
+ case AF_INET:
+ inet_pton(AF_INET, SSDP_MCAST_ADDR, &(taddr.ip));
+ if (daddr.ip == taddr.ip)
+ good_packet = 1;
+ break;
+ case AF_INET6:
+ inet_pton(AF_INET6, UPNP_MCAST_LL_ADDR, &(taddr.ip6));
+ if (daddr.ip6[0] == taddr.ip6[0] &&
+ daddr.ip6[1] == taddr.ip6[1] &&
+ daddr.ip6[2] == taddr.ip6[2] &&
+ daddr.ip6[3] == taddr.ip6[3]) {
+ good_packet = 1;
+ break;
+ }
+ inet_pton(AF_INET6, UPNP_MCAST_SL_ADDR, &(taddr.ip6));
+ if (daddr.ip6[0] == taddr.ip6[0] &&
+ daddr.ip6[1] == taddr.ip6[1] &&
+ daddr.ip6[2] == taddr.ip6[2] &&
+ daddr.ip6[3] == taddr.ip6[3]) {
+ good_packet = 1;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!good_packet) {
+ pr_debug("ssdp_help: destination address not multicast; ignoring\n");
+ return NF_ACCEPT;
+ }
+
+ /* No data? Ignore */
+ dataoff = net_hdr->ihl*4 + sizeof(struct udphdr);
+ if (dataoff >= pktb_len(pkt)) {
+ pr_debug("ssdp_help: UDP payload too small for M-SEARCH; ignoring\n");
+ return NF_ACCEPT;
+ }
+
+ sb_ptr = pktb_network_header(pkt) + dataoff;
+
+ if (memcmp(sb_ptr, SSDP_M_SEARCH, SSDP_M_SEARCH_SIZE) != 0) {
+ pr_debug("ssdp_help: UDP payload does not begin with 'M-SEARCH'; ignoring\n");
+ return NF_ACCEPT;
+ }
+
+ cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr);
+ cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port);
+
+ exp = nfexp_new();
+ if (exp == NULL)
+ return NF_DROP;
+
+ if (cthelper_expect_init(exp, myct->ct, 0, NULL, &saddr,
+ IPPROTO_UDP, NULL, &port,
+ NF_CT_EXPECT_PERMANENT)) {
+ nfexp_destroy(exp);
+ return NF_DROP;
+ }
+ myct->exp = exp;
+
+ return ret;
+}
+
+static struct ctd_helper ssdp_helper = {
+ .name = "ssdp",
+ .l4proto = IPPROTO_UDP,
+ .priv_data_len = 0,
+ .cb = ssdp_helper_cb,
+ .policy = {
+ [0] = {
+ .name = "ssdp",
+ .expect_max = 1,
+ .expect_timeout = 5 * 60,
+ },
+ },
+};
+
+static void __attribute__ ((constructor)) ssdp_init(void)
+{
+ helper_register(&ssdp_helper);
+}
diff --git a/src/helpers/tftp.c b/src/helpers/tftp.c
new file mode 100644
index 0000000..45591c6
--- /dev/null
+++ b/src/helpers/tftp.c
@@ -0,0 +1,138 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Adapted from:
+ *
+ * (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_udp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+struct tftphdr {
+ uint16_t opcode;
+};
+
+#define TFTP_OPCODE_READ 1
+#define TFTP_OPCODE_WRITE 2
+#define TFTP_OPCODE_DATA 3
+#define TFTP_OPCODE_ACK 4
+#define TFTP_OPCODE_ERROR 5
+
+static unsigned int nat_tftp(struct pkt_buff *pkt, uint32_t ctinfo,
+ struct nf_conntrack *ct, struct nf_expect *exp)
+{
+ struct nf_conntrack *nat_tuple;
+ static uint32_t zero[4] = { 0, 0, 0, 0 };
+
+ nat_tuple = nfct_new();
+ if (nat_tuple == NULL)
+ return NF_ACCEPT;
+
+ switch (nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
+ case AF_INET:
+ nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0);
+ nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0);
+ break;
+ case AF_INET6:
+ nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET6);
+ nfct_set_attr(nat_tuple, ATTR_IPV6_SRC, &zero);
+ nfct_set_attr(nat_tuple, ATTR_IPV6_DST, &zero);
+ break;
+ }
+ nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_UDP);
+ nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC,
+ nfct_get_attr_u16(ct, ATTR_PORT_SRC));
+ nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0);
+
+ nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, MYCT_DIR_REPL);
+ nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master");
+ nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple);
+
+ return NF_ACCEPT;
+}
+
+static int
+tftp_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+ struct myct *myct, uint32_t ctinfo)
+{
+ const struct tftphdr *tfh;
+ struct nf_expect *exp;
+ unsigned int ret = NF_ACCEPT;
+ union nfct_attr_grp_addr saddr, daddr;
+ uint16_t dport;
+
+ tfh = (struct tftphdr *)(pktb_network_header(pkt) + protoff + sizeof(struct udphdr));
+
+ switch (ntohs(tfh->opcode)) {
+ case TFTP_OPCODE_READ:
+ case TFTP_OPCODE_WRITE:
+ /* RRQ and WRQ works the same way */
+ exp = nfexp_new();
+ if (exp == NULL) {
+ pr_debug("cannot alloc expectation\n");
+ return NF_DROP;
+ }
+
+ cthelper_get_addr_src(myct->ct, MYCT_DIR_REPL, &saddr);
+ cthelper_get_addr_dst(myct->ct, MYCT_DIR_REPL, &daddr);
+ cthelper_get_port_dst(myct->ct, MYCT_DIR_REPL, &dport);
+
+ if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
+ IPPROTO_UDP, NULL, &dport, 0)) {
+ nfexp_destroy(exp);
+ return NF_DROP;
+ }
+
+ if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK)
+ ret = nat_tftp(pkt, ctinfo, myct->ct, exp);
+
+ myct->exp = exp;
+ break;
+ case TFTP_OPCODE_DATA:
+ case TFTP_OPCODE_ACK:
+ pr_debug("Data/ACK opcode\n");
+ break;
+ case TFTP_OPCODE_ERROR:
+ pr_debug("Error opcode\n");
+ break;
+ default:
+ pr_debug("Unknown opcode\n");
+ }
+ return ret;
+}
+
+static struct ctd_helper tftp_helper = {
+ .name = "tftp",
+ .l4proto = IPPROTO_UDP,
+ .cb = tftp_helper_cb,
+ .policy = {
+ [0] = {
+ .name = "tftp",
+ .expect_max = 1,
+ .expect_timeout = 5 * 60,
+ },
+ },
+};
+
+static void __attribute__ ((constructor)) tftp_init(void)
+{
+ helper_register(&tftp_helper);
+}
diff --git a/src/helpers/tns.c b/src/helpers/tns.c
index 5833fea..2b4fed4 100644
--- a/src/helpers/tns.c
+++ b/src/helpers/tns.c
@@ -18,6 +18,7 @@
#include <ctype.h> /* for isdigit */
#include <errno.h>
+#define _GNU_SOURCE
#include <netinet/tcp.h>
#include <libmnl/libmnl.h>
diff --git a/src/internal_bypass.c b/src/internal_bypass.c
index ce2ae46..61988c7 100644
--- a/src/internal_bypass.c
+++ b/src/internal_bypass.c
@@ -49,7 +49,7 @@ internal_bypass_ct_dump_cb(enum nf_conntrack_msg_type type,
static void internal_bypass_ct_dump(int fd, int type)
{
struct nfct_handle *h;
- u_int32_t family = AF_UNSPEC;
+ uint32_t family = AF_UNSPEC;
int ret;
h = nfct_open(CONFIG(netlink).subsys_id, 0);
@@ -180,7 +180,7 @@ internal_bypass_exp_dump_cb(enum nf_conntrack_msg_type type,
static void internal_bypass_exp_dump(int fd, int type)
{
struct nfct_handle *h;
- u_int32_t family = AF_UNSPEC;
+ uint32_t family = AF_UNSPEC;
int ret;
h = nfct_open(CONFIG(netlink).subsys_id, 0);
diff --git a/src/local.c b/src/local.c
index feff608..3395b4c 100644
--- a/src/local.c
+++ b/src/local.c
@@ -77,7 +77,7 @@ int do_local_server_step(struct local_server *server, void *data,
int rfd;
struct sockaddr_un local;
socklen_t sin_size = sizeof(struct sockaddr_un);
-
+
rfd = accept(server->fd, (struct sockaddr *) &local, &sin_size);
if (rfd == -1)
return -1;
@@ -117,11 +117,10 @@ void local_client_destroy(int fd)
int do_local_client_step(int fd, void (*process)(char *buf))
{
- int numbytes;
char buf[1024];
memset(buf, 0, sizeof(buf));
- while ((numbytes = recv(fd, buf, sizeof(buf)-1, 0)) > 0) {
+ while (recv(fd, buf, sizeof(buf)-1, 0) > 0) {
buf[sizeof(buf)-1] = '\0';
if (process)
process(buf);
@@ -148,11 +147,14 @@ int do_local_request(int request,
ret = send(fd, &request, sizeof(int), 0);
if (ret == -1)
- return -1;
+ goto err1;
do_local_client_step(fd, step);
local_client_destroy(fd);
-
+
return 0;
+err1:
+ local_client_destroy(fd);
+ return -1;
}
diff --git a/src/netlink.c b/src/netlink.c
index 5be102e..189f55a 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -26,7 +26,7 @@
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <sys/fcntl.h>
+#include <fcntl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
struct nfct_handle *nl_init_event_handler(void)
diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c
index f91fc41..dfc55e7 100644
--- a/src/nfct-extensions/helper.c
+++ b/src/nfct-extensions/helper.c
@@ -37,54 +37,60 @@ nfct_cmd_helper_usage(char *argv[])
"[parameters...]\n", VERSION, argv[0]);
}
-int
-nfct_cmd_helper_parse_params(int argc, char *argv[])
+static int nfct_cmd_helper_list(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_helper_delete(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_helper_get(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_helper_flush(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]);
+
+static int
+nfct_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[], int cmd)
{
- int cmd = NFCT_CMD_NONE, ret = 0;
+ int ret;
if (argc < 3) {
- fprintf(stderr, "nfct v%s: Missing command\n"
- "%s helper list|add|delete|get|flush "
- "[parameters...]\n", VERSION, argv[0]);
- exit(EXIT_FAILURE);
+ nfct_cmd_helper_usage(argv);
+ return -1;
}
- if (strncmp(argv[2], "list", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_LIST;
- else if (strncmp(argv[2], "add", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_ADD;
- else if (strncmp(argv[2], "delete", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_DELETE;
- else if (strncmp(argv[2], "get", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_GET;
- else if (strncmp(argv[2], "flush", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_FLUSH;
- else if (strncmp(argv[2], "disable", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_DISABLE;
- else {
+
+ switch (cmd) {
+ case NFCT_CMD_LIST:
+ case NFCT_CMD_ADD:
+ case NFCT_CMD_DELETE:
+ case NFCT_CMD_GET:
+ case NFCT_CMD_FLUSH:
+ case NFCT_CMD_DISABLE:
+ break;
+ default:
fprintf(stderr, "nfct v%s: Unknown command: %s\n",
VERSION, argv[2]);
nfct_cmd_helper_usage(argv);
exit(EXIT_FAILURE);
}
- switch(cmd) {
+
+ switch (cmd) {
case NFCT_CMD_LIST:
- ret = nfct_cmd_helper_list(argc, argv);
+ ret = nfct_cmd_helper_list(nl, argc, argv);
break;
case NFCT_CMD_ADD:
- ret = nfct_cmd_helper_add(argc, argv);
+ ret = nfct_cmd_helper_add(nl, argc, argv);
break;
case NFCT_CMD_DELETE:
- ret = nfct_cmd_helper_delete(argc, argv);
+ ret = nfct_cmd_helper_delete(nl, argc, argv);
break;
case NFCT_CMD_GET:
- ret = nfct_cmd_helper_get(argc, argv);
+ ret = nfct_cmd_helper_get(nl, argc, argv);
break;
case NFCT_CMD_FLUSH:
- ret = nfct_cmd_helper_flush(argc, argv);
+ ret = nfct_cmd_helper_flush(nl, argc, argv);
break;
case NFCT_CMD_DISABLE:
- ret = nfct_cmd_helper_disable(argc, argv);
+ ret = nfct_cmd_helper_disable(nl, argc, argv);
break;
+ default:
+ nfct_cmd_helper_usage(argv);
+ return -1;
}
return ret;
@@ -115,13 +121,11 @@ err:
return MNL_CB_OK;
}
-int nfct_cmd_helper_list(int argc, char *argv[])
+static int nfct_cmd_helper_list(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
unsigned int seq, portid;
- int ret;
if (argc > 3) {
nfct_perror("too many arguments");
@@ -132,42 +136,17 @@ int nfct_cmd_helper_list(int argc, char *argv[])
nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_GET,
NLM_F_DUMP, seq);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
- return -1;
- }
-
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
portid = mnl_socket_get_portid(nl);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
+ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, nfct_helper_cb, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
- return -1;
- }
- mnl_socket_close(nl);
-
return 0;
}
-int nfct_cmd_helper_add(int argc, char *argv[])
+static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
@@ -175,12 +154,11 @@ int nfct_cmd_helper_add(int argc, char *argv[])
uint16_t l3proto;
uint8_t l4proto;
struct ctd_helper *helper;
- int ret, j;
+ int j;
if (argc < 6) {
nfct_perror("missing parameters\n"
- "syntax: nfct helper add name "
- "family protocol");
+ "syntax: nfct add helper name family protocol");
return -1;
}
@@ -248,47 +226,22 @@ int nfct_cmd_helper_add(int argc, char *argv[])
nfct_helper_free(t);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
- return -1;
- }
-
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
portid = mnl_socket_get_portid(nl);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
+ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
- return -1;
- }
- mnl_socket_close(nl);
-
return 0;
}
-int nfct_cmd_helper_delete(int argc, char *argv[])
+static int
+nfct_cmd_helper_delete(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
struct nfct_helper *t;
- int ret;
if (argc < 4) {
nfct_perror("missing helper policy name");
@@ -341,48 +294,21 @@ int nfct_cmd_helper_delete(int argc, char *argv[])
nfct_helper_free(t);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
- return -1;
- }
-
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
portid = mnl_socket_get_portid(nl);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
+ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
- return -1;
- }
-
- mnl_socket_close(nl);
-
return 0;
}
-int nfct_cmd_helper_get(int argc, char *argv[])
+static int nfct_cmd_helper_get(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
struct nfct_helper *t;
- int ret;
if (argc < 4) {
nfct_perror("missing helper policy name");
@@ -435,46 +361,21 @@ int nfct_cmd_helper_get(int argc, char *argv[])
nfct_helper_free(t);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
- return -1;
- }
-
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
portid = mnl_socket_get_portid(nl);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
- return -1;
- }
-
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, nfct_helper_cb, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
+ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- mnl_socket_close(nl);
return 0;
}
-int nfct_cmd_helper_flush(int argc, char *argv[])
+static int
+nfct_cmd_helper_flush(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
- int ret;
if (argc > 3) {
nfct_perror("too many arguments");
@@ -485,43 +386,18 @@ int nfct_cmd_helper_flush(int argc, char *argv[])
nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_DEL,
NLM_F_ACK, seq);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
- return -1;
- }
-
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
portid = mnl_socket_get_portid(nl);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
- return -1;
- }
-
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
+ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- mnl_socket_close(nl);
-
return 0;
}
-int nfct_cmd_helper_disable(int argc, char *argv[])
+static int
+nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
@@ -529,12 +405,10 @@ int nfct_cmd_helper_disable(int argc, char *argv[])
uint16_t l3proto;
uint8_t l4proto;
struct ctd_helper *helper;
- int ret;
if (argc < 6) {
nfct_perror("missing parameters\n"
- "syntax: nfct helper add name "
- "family protocol");
+ "syntax: nfct add helper name family protocol");
return -1;
}
@@ -580,36 +454,21 @@ int nfct_cmd_helper_disable(int argc, char *argv[])
nfct_helper_free(t);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
- return -1;
- }
-
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
portid = mnl_socket_get_portid(nl);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
+ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
- return -1;
- }
- mnl_socket_close(nl);
-
return 0;
}
+static struct nfct_extension helper = {
+ .type = NFCT_SUBSYS_HELPER,
+ .parse_params = nfct_helper_parse_params,
+};
+
+static void __init helper_init(void)
+{
+ nfct_extension_register(&helper);
+}
diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c
index 5b32023..1cb04a1 100644
--- a/src/nfct-extensions/timeout.c
+++ b/src/nfct-extensions/timeout.c
@@ -1,5 +1,5 @@
/*
- * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2012-2013 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
@@ -19,6 +19,7 @@
#include <unistd.h>
#include <time.h>
#include <netinet/in.h>
+#include <netdb.h>
#include <errno.h>
#include <libmnl/libmnl.h>
@@ -31,50 +32,67 @@ static void
nfct_cmd_timeout_usage(char *argv[])
{
fprintf(stderr, "nfct v%s: Missing command\n"
- "%s timeout list|add|delete|get|flush "
- "[parameters...]\n", VERSION, argv[0]);
+ "%s <list|add|delete|get|flush|set> timeout "
+ "[<parameters>, ...]\n", VERSION, argv[0]);
}
-int nfct_cmd_timeout_parse_params(int argc, char *argv[])
+static int nfct_cmd_timeout_list(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_delete(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_get(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]);
+
+static int
+nfct_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[], int cmd)
{
- int cmd = NFCT_CMD_NONE, ret;
+ int ret;
if (argc < 3) {
nfct_cmd_timeout_usage(argv);
return -1;
}
- if (strncmp(argv[2], "list", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_LIST;
- else if (strncmp(argv[2], "add", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_ADD;
- else if (strncmp(argv[2], "delete", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_DELETE;
- else if (strncmp(argv[2], "get", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_GET;
- else if (strncmp(argv[2], "flush", strlen(argv[2])) == 0)
- cmd = NFCT_CMD_FLUSH;
- else {
- fprintf(stderr, "nfct v%s: Unknown command: %s\n",
- VERSION, argv[2]);
+
+ switch (cmd) {
+ case NFCT_CMD_LIST:
+ case NFCT_CMD_ADD:
+ case NFCT_CMD_DELETE:
+ case NFCT_CMD_GET:
+ case NFCT_CMD_FLUSH:
+ case NFCT_CMD_DEFAULT_SET:
+ case NFCT_CMD_DEFAULT_GET:
+ break;
+ default:
nfct_cmd_timeout_usage(argv);
return -1;
}
- switch(cmd) {
+
+ switch (cmd) {
case NFCT_CMD_LIST:
- ret = nfct_cmd_timeout_list(argc, argv);
+ ret = nfct_cmd_timeout_list(nl, argc, argv);
break;
case NFCT_CMD_ADD:
- ret = nfct_cmd_timeout_add(argc, argv);
+ ret = nfct_cmd_timeout_add(nl, argc, argv);
break;
case NFCT_CMD_DELETE:
- ret = nfct_cmd_timeout_delete(argc, argv);
+ ret = nfct_cmd_timeout_delete(nl, argc, argv);
break;
case NFCT_CMD_GET:
- ret = nfct_cmd_timeout_get(argc, argv);
+ ret = nfct_cmd_timeout_get(nl, argc, argv);
break;
case NFCT_CMD_FLUSH:
- ret = nfct_cmd_timeout_flush(argc, argv);
+ ret = nfct_cmd_timeout_flush(nl, argc, argv);
+ break;
+ case NFCT_CMD_DEFAULT_SET:
+ ret = nfct_cmd_timeout_default_set(nl, argc, argv);
break;
+ case NFCT_CMD_DEFAULT_GET:
+ ret = nfct_cmd_timeout_default_get(nl, argc, argv);
+ break;
+ default:
+ nfct_cmd_timeout_usage(argv);
+ return -1;
}
return ret;
@@ -105,13 +123,11 @@ err:
return MNL_CB_OK;
}
-int nfct_cmd_timeout_list(int argc, char *argv[])
+static int nfct_cmd_timeout_list(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
unsigned int seq, portid;
- int ret;
if (argc > 3) {
nfct_perror("too many arguments");
@@ -122,35 +138,11 @@ int nfct_cmd_timeout_list(int argc, char *argv[])
nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_GET,
NLM_F_DUMP, seq);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
- return -1;
- }
-
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
portid = mnl_socket_get_portid(nl);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
- return -1;
- }
-
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, nfct_timeout_cb, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
+ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- mnl_socket_close(nl);
return 0;
}
@@ -167,69 +159,71 @@ static uint32_t nfct_timeout_attr_max[IPPROTO_MAX] = {
[IPPROTO_RAW] = NFCT_TIMEOUT_ATTR_GENERIC_MAX,
};
-int nfct_cmd_timeout_add(int argc, char *argv[])
+static int nfct_cmd_get_l3proto(char *argv[])
{
- struct mnl_socket *nl;
- char buf[MNL_SOCKET_BUFFER_SIZE];
- struct nlmsghdr *nlh;
- uint32_t portid, seq;
- struct nfct_timeout *t;
- uint16_t l3proto;
- uint8_t l4proto;
- int ret, i;
- unsigned int j;
+ int l3proto;
- if (argc < 6) {
- nfct_perror("missing parameters\n"
- "syntax: nfct timeout add name "
- "family protocol state1 "
- "timeout1 state2 timeout2...");
- return -1;
- }
-
- t = nfct_timeout_alloc();
- if (t == NULL) {
- nfct_perror("OOM");
- return -1;
- }
-
- nfct_timeout_attr_set(t, NFCT_TIMEOUT_ATTR_NAME, argv[3]);
-
- if (strcmp(argv[4], "inet") == 0)
+ if (strcmp(*argv, "inet") == 0)
l3proto = AF_INET;
- else if (strcmp(argv[4], "inet6") == 0)
+ else if (strcmp(*argv, "inet6") == 0)
l3proto = AF_INET6;
else {
nfct_perror("unknown layer 3 protocol");
return -1;
}
+ return l3proto;
+}
+
+static int nfct_cmd_get_l4proto(char *argv[])
+{
+ int l4proto;
+ struct protoent *pent;
+
+ pent = getprotobyname(*argv);
+ if (!pent) {
+ /* In Debian, /etc/protocols says ipv6-icmp. Support icmpv6
+ * as well not to break backward compatibility.
+ */
+ if (strcmp(*argv, "icmpv6") == 0)
+ l4proto = IPPROTO_ICMPV6;
+ else if (strcmp(*argv, "generic") == 0)
+ l4proto = IPPROTO_RAW;
+ else {
+ nfct_perror("unknown layer 4 protocol");
+ return -1;
+ }
+ } else
+ l4proto = pent->p_proto;
+
+ return l4proto;
+}
+
+static int
+nfct_cmd_timeout_parse(struct nfct_timeout *t, int argc, char *argv[])
+{
+ int l3proto, l4proto;
+ unsigned int j;
+ const char *proto_name;
+
+ l3proto = nfct_cmd_get_l3proto(argv);
+ if (l3proto < 0)
+ return -1;
+
nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO, l3proto);
- if (strcmp(argv[5], "tcp") == 0)
- l4proto = IPPROTO_TCP;
- else if (strcmp(argv[5], "udp") == 0)
- l4proto = IPPROTO_UDP;
- else if (strcmp(argv[5], "udplite") == 0)
- l4proto = IPPROTO_UDPLITE;
- else if (strcmp(argv[5], "sctp") == 0)
- l4proto = IPPROTO_SCTP;
- else if (strcmp(argv[5], "dccp") == 0)
- l4proto = IPPROTO_DCCP;
- else if (strcmp(argv[5], "icmp") == 0)
- l4proto = IPPROTO_ICMP;
- else if (strcmp(argv[5], "icmpv6") == 0)
- l4proto = IPPROTO_ICMPV6;
- else if (strcmp(argv[5], "gre") == 0)
- l4proto = IPPROTO_GRE;
- else if (strcmp(argv[5], "generic") == 0)
- l4proto = IPPROTO_RAW;
- else {
- nfct_perror("unknown layer 4 protocol");
+ argc--;
+ argv++;
+ proto_name = *argv;
+
+ l4proto = nfct_cmd_get_l4proto(argv);
+ if (l4proto < 0)
return -1;
- }
+
nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO, l4proto);
+ argc--;
+ argv++;
- for (i=6; i<argc; i+=2) {
+ for (; argc>1; argc-=2, argv+=2) {
int matching = -1;
for (j=0; j<nfct_timeout_attr_max[l4proto]; j++) {
@@ -241,76 +235,76 @@ int nfct_cmd_timeout_add(int argc, char *argv[])
nfct_perror("state name is NULL");
return -1;
}
- if (strcasecmp(argv[i], state_name) != 0)
+ if (strcasecmp(*argv, state_name) != 0)
continue;
matching = j;
break;
}
if (matching != -1) {
- if (i+1 >= argc) {
- nfct_perror("missing value for this timeout");
- return -1;
- }
nfct_timeout_policy_attr_set_u32(t, matching,
- atoi(argv[i+1]));
- matching = -1;
+ atoi(*(argv+1)));
} else {
fprintf(stderr, "nfct v%s: Wrong state name: `%s' "
"for protocol `%s'\n",
- VERSION, argv[i], argv[5]);
+ VERSION, *argv, proto_name);
return -1;
}
}
+ if (argc > 0) {
+ nfct_perror("missing value for this timeout");
+ return -1;
+ }
- seq = time(NULL);
- nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_NEW,
- NLM_F_CREATE | NLM_F_ACK, seq);
- nfct_timeout_nlmsg_build_payload(nlh, t);
+ return 0;
+}
- nfct_timeout_free(t);
+int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[])
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ uint32_t portid, seq;
+ struct nfct_timeout *t;
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
+ if (argc < 6) {
+ nfct_perror("missing parameters\n"
+ "syntax: nfct add timeout name family protocol state1 timeout1 ...");
return -1;
}
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
+ t = nfct_timeout_alloc();
+ if (t == NULL) {
+ nfct_perror("OOM");
return -1;
}
- portid = mnl_socket_get_portid(nl);
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
+ nfct_timeout_attr_set(t, NFCT_TIMEOUT_ATTR_NAME, argv[3]);
+
+ if (nfct_cmd_timeout_parse(t, argc-4, &argv[4]) < 0)
return -1;
- }
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
+ seq = time(NULL);
+ nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_NEW,
+ NLM_F_CREATE | NLM_F_ACK, seq);
+ nfct_timeout_nlmsg_build_payload(nlh, t);
+
+ nfct_timeout_free(t);
+
+ portid = mnl_socket_get_portid(nl);
+ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- mnl_socket_close(nl);
return 0;
}
-int nfct_cmd_timeout_delete(int argc, char *argv[])
+int nfct_cmd_timeout_delete(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
struct nfct_timeout *t;
- int ret;
if (argc < 4) {
nfct_perror("missing timeout policy name");
@@ -335,48 +329,21 @@ int nfct_cmd_timeout_delete(int argc, char *argv[])
nfct_timeout_free(t);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
- return -1;
- }
-
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
portid = mnl_socket_get_portid(nl);
-
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
- return -1;
- }
-
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
+ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- mnl_socket_close(nl);
-
return 0;
}
-int nfct_cmd_timeout_get(int argc, char *argv[])
+int nfct_cmd_timeout_get(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
struct nfct_timeout *t;
- int ret;
if (argc < 4) {
nfct_perror("missing timeout policy name");
@@ -401,86 +368,132 @@ int nfct_cmd_timeout_get(int argc, char *argv[])
nfct_timeout_free(t);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
+ portid = mnl_socket_get_portid(nl);
+ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
- return -1;
- }
- portid = mnl_socket_get_portid(nl);
+ return 0;
+}
+
+int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[])
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ uint32_t portid, seq;
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
+ if (argc > 3) {
+ nfct_perror("too many arguments");
return -1;
}
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, nfct_timeout_cb, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
+ seq = time(NULL);
+ nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE,
+ NLM_F_ACK, seq);
+
+ portid = mnl_socket_get_portid(nl);
+ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- mnl_socket_close(nl);
return 0;
}
-int nfct_cmd_timeout_flush(int argc, char *argv[])
+static int
+nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *argv[])
{
- struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t portid, seq;
- int ret;
+ struct nfct_timeout *t;
- if (argc > 3) {
- nfct_perror("too many arguments");
+ if (argc < 6) {
+ nfct_perror("missing parameters\n"
+ "syntax: nfct default-set timeout family protocol state1 timeout1...");
return -1;
}
+ t = nfct_timeout_alloc();
+ if (t == NULL)
+ return -1;
+
+ if (nfct_cmd_timeout_parse(t, argc-3, &argv[3]) < 0)
+ return -1;
+
seq = time(NULL);
- nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE,
+ nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DEFAULT_SET,
NLM_F_ACK, seq);
+ nfct_timeout_nlmsg_build_payload(nlh, t);
+ nfct_timeout_free(t);
- nl = mnl_socket_open(NETLINK_NETFILTER);
- if (nl == NULL) {
- nfct_perror("mnl_socket_open");
+ portid = mnl_socket_get_portid(nl);
+ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) {
+ nfct_perror("netlink error");
return -1;
}
- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
- nfct_perror("mnl_socket_bind");
+ return 0;
+}
+
+static int
+nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[])
+{
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ struct nlmsghdr *nlh;
+ uint32_t portid, seq;
+ struct nfct_timeout *t;
+ int l3proto, l4proto;
+
+ if (argc < 5) {
+ nfct_perror("missing parameters\n"
+ "syntax: nfct default-get timeout family protocol");
return -1;
}
- portid = mnl_socket_get_portid(nl);
- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
- nfct_perror("mnl_socket_send");
+ t = nfct_timeout_alloc();
+ if (t == NULL)
return -1;
- }
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- while (ret > 0) {
- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
- if (ret <= 0)
- break;
- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
- }
- if (ret == -1) {
- nfct_perror("error");
+ argc-=3;
+ argv+=3;
+
+ l3proto = nfct_cmd_get_l3proto(argv);
+ if (l3proto < 0)
return -1;
- }
- mnl_socket_close(nl);
+ nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO, l3proto);
+ argc--;
+ argv++;
+
+ l4proto = nfct_cmd_get_l4proto(argv);
+ if (l4proto < 0)
+ return -1;
+
+ nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO, l4proto);
+
+ seq = time(NULL);
+ nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DEFAULT_GET,
+ NLM_F_ACK, seq);
+ nfct_timeout_nlmsg_build_payload(nlh, t);
+ nfct_timeout_free(t);
+
+ portid = mnl_socket_get_portid(nl);
+ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) {
+ nfct_perror("netlink error");
+ return -1;
+ }
return 0;
}
+
+static struct nfct_extension timeout = {
+ .type = NFCT_SUBSYS_TIMEOUT,
+ .parse_params = nfct_timeout_parse_params,
+};
+
+static void __init timeout_init(void)
+{
+ nfct_extension_register(&timeout);
+}
diff --git a/src/nfct.c b/src/nfct.c
index b5c9654..3331e5b 100644
--- a/src/nfct.c
+++ b/src/nfct.c
@@ -22,9 +22,8 @@
#include <errno.h>
#include <libmnl/libmnl.h>
-#include <linux/netfilter/nfnetlink_cttimeout.h>
-#include <libnetfilter_cttimeout/libnetfilter_cttimeout.h>
+#include "linux_list.h"
#include "nfct.h"
static int nfct_cmd_version(int argc, char *argv[]);
@@ -32,7 +31,7 @@ static int nfct_cmd_help(int argc, char *argv[]);
static void usage(char *argv[])
{
- fprintf(stderr, "Usage: %s subsystem command [parameters]...\n",
+ fprintf(stderr, "Usage: %s command subsystem [parameters]...\n",
argv[0]);
}
@@ -46,42 +45,134 @@ void nfct_perror(const char *msg)
}
}
+static LIST_HEAD(nfct_extension_list);
+
+void nfct_extension_register(struct nfct_extension *ext)
+{
+ list_add(&ext->head, &nfct_extension_list);
+}
+
+static struct nfct_extension *nfct_extension_lookup(int type)
+{
+ struct nfct_extension *ext;
+
+ list_for_each_entry(ext, &nfct_extension_list, head) {
+ if (ext->type == type)
+ return ext;
+ }
+ return NULL;
+}
+
+static const char *nfct_cmd_array[NFCT_CMD_MAX] = {
+ [NFCT_CMD_LIST] = "list",
+ [NFCT_CMD_ADD] = "add",
+ [NFCT_CMD_DELETE] = "delete",
+ [NFCT_CMD_GET] = "get",
+ [NFCT_CMD_FLUSH] = "flush",
+ [NFCT_CMD_DISABLE] = "disable",
+ [NFCT_CMD_DEFAULT_SET] = "default-set",
+ [NFCT_CMD_DEFAULT_GET] = "default-get",
+};
+
+static int nfct_cmd_parse(const char *cmdstr)
+{
+ int i;
+
+ for (i = 1; i < NFCT_CMD_MAX; i++) {
+ if (strncmp(nfct_cmd_array[i], cmdstr, strlen(cmdstr)) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int nfct_cmd_error(char *argv[])
+{
+ fprintf(stderr, "nfct v%s: Unknown command: %s\n", VERSION, argv[1]);
+ usage(argv);
+
+ return EXIT_FAILURE;
+}
+
+static const char *nfct_subsys_array[NFCT_SUBSYS_MAX] = {
+ [NFCT_SUBSYS_TIMEOUT] = "timeout",
+ [NFCT_SUBSYS_HELPER] = "helper",
+ [NFCT_SUBSYS_VERSION] = "version",
+ [NFCT_SUBSYS_HELP] = "help",
+};
+
+static int nfct_subsys_parse(const char *cmdstr)
+{
+ int i;
+
+ for (i = 1; i < NFCT_SUBSYS_MAX; i++) {
+ if (strncmp(nfct_subsys_array[i], cmdstr, strlen(cmdstr)) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static int nfct_subsys_error(char *argv[])
+{
+ fprintf(stderr, "nfct v%s: Unknown subsystem: %s\n", VERSION, argv[1]);
+ usage(argv);
+
+ return EXIT_FAILURE;
+}
+
int main(int argc, char *argv[])
{
- int subsys = NFCT_SUBSYS_NONE, ret = 0;
+ int subsys, cmd, ret = 0;
+ struct nfct_extension *ext;
+ struct mnl_socket *nl;
- if (argc < 2) {
+ if (argc < 3) {
usage(argv);
exit(EXIT_FAILURE);
}
- if (strncmp(argv[1], "timeout", strlen(argv[1])) == 0) {
- subsys = NFCT_SUBSYS_TIMEOUT;
- } else if (strncmp(argv[1], "helper", strlen(argv[1])) == 0) {
- subsys = NFCT_SUBSYS_HELPER;
- } else if (strncmp(argv[1], "version", strlen(argv[1])) == 0)
- subsys = NFCT_SUBSYS_VERSION;
- else if (strncmp(argv[1], "help", strlen(argv[1])) == 0)
- subsys = NFCT_SUBSYS_HELP;
- else {
- fprintf(stderr, "nfct v%s: Unknown subsystem: %s\n",
- VERSION, argv[1]);
- usage(argv);
- exit(EXIT_FAILURE);
+
+ cmd = nfct_cmd_parse(argv[1]);
+ if (cmd < 0) {
+ /* Workaround not to break backward compatibility and to get
+ * the syntax in sync with nft. Old nfct versions allow to
+ * specify the subsystem before the command.
+ */
+ subsys = nfct_subsys_parse(argv[1]);
+ if (subsys < 0)
+ return nfct_subsys_error(argv);
+
+ cmd = nfct_cmd_parse(argv[2]);
+ if (cmd < 0)
+ return nfct_cmd_error(argv);
+ } else {
+ subsys = nfct_subsys_parse(argv[2]);
+ if (subsys < 0)
+ return nfct_subsys_error(argv);
}
- switch(subsys) {
- case NFCT_SUBSYS_TIMEOUT:
- ret = nfct_cmd_timeout_parse_params(argc, argv);
- break;
- case NFCT_SUBSYS_HELPER:
- ret = nfct_cmd_helper_parse_params(argc, argv);
- break;
+ switch (subsys) {
case NFCT_SUBSYS_VERSION:
ret = nfct_cmd_version(argc, argv);
break;
case NFCT_SUBSYS_HELP:
ret = nfct_cmd_help(argc, argv);
break;
+ default:
+ ext = nfct_extension_lookup(subsys);
+ if (ext == NULL) {
+ fprintf(stderr, "nfct v%s: subsystem %s not supported\n",
+ VERSION, argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ nl = nfct_mnl_open();
+ if (nl == NULL) {
+ nfct_perror("cannot open netlink");
+ return -1;
+ }
+
+ ret = ext->parse_params(nl, argc, argv, cmd);
+ mnl_socket_close(nl);
+ break;
}
return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
@@ -120,3 +211,42 @@ static int nfct_cmd_help(int argc, char *argv[])
printf(help_msg, VERSION, argv[0]);
return 0;
}
+
+int nfct_mnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh,
+ uint32_t seq, uint32_t portid,
+ int (*cb)(const struct nlmsghdr *nlh, void *data),
+ void *data)
+{
+ int ret;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+
+ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
+ return -1;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ while (ret > 0) {
+ ret = mnl_cb_run(buf, ret, seq, portid, cb, data);
+ if (ret <= 0)
+ break;
+
+ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
+ }
+ if (ret == -1)
+ return -1;
+
+ return 0;
+}
+
+struct mnl_socket *nfct_mnl_open(void)
+{
+ struct mnl_socket *nl;
+
+ nl = mnl_socket_open(NETLINK_NETFILTER);
+ if (nl == NULL)
+ return NULL;
+
+ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0)
+ return NULL;
+
+ return nl;
+}
diff --git a/src/parse.c b/src/parse.c
index f3ec6ac..919d36c 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -297,7 +297,7 @@ int msg2ct(struct nf_conntrack *ct, struct nethdr *net, size_t remain)
return -1;
if (attr->nta_len < NTA_LENGTH(0))
return -1;
- if (attr->nta_attr > NTA_MAX)
+ if (attr->nta_attr >= NTA_MAX)
return -1;
if (h[attr->nta_attr].size &&
attr->nta_len != h[attr->nta_attr].size)
@@ -510,7 +510,7 @@ int msg2exp(struct nf_expect *exp, struct nethdr *net, size_t remain)
ATTR_NETWORK2HOST(attr);
if (attr->nta_len > len)
goto err;
- if (attr->nta_attr > NTA_MAX)
+ if (attr->nta_attr >= NTA_EXP_MAX)
goto err;
if (attr->nta_len < NTA_LENGTH(0))
goto err;
@@ -524,13 +524,15 @@ int msg2exp(struct nf_expect *exp, struct nethdr *net, size_t remain)
attr = NTA_NEXT(attr, len);
continue;
}
- switch(exp_h[attr->nta_attr].exp_attr) {
+ switch (exp_h[attr->nta_attr].exp_attr) {
case ATTR_EXP_MASTER:
exp_h[attr->nta_attr].parse(master, attr->nta_attr,
NTA_DATA(attr));
+ break;
case ATTR_EXP_EXPECTED:
exp_h[attr->nta_attr].parse(expected, attr->nta_attr,
NTA_DATA(attr));
+ break;
case ATTR_EXP_MASK:
exp_h[attr->nta_attr].parse(mask, attr->nta_attr,
NTA_DATA(attr));
diff --git a/src/process.c b/src/process.c
index 7f0a395..3ddad5f 100644
--- a/src/process.c
+++ b/src/process.c
@@ -48,6 +48,8 @@ int fork_process_new(int type, int flags, void (*cb)(void *data), void *data)
if (c->pid > 0)
list_add(&c->head, &process_list);
+ else
+ free(c);
return pid;
}
diff --git a/src/read_config_lex.l b/src/read_config_lex.l
index b4d11d4..8350069 100644
--- a/src/read_config_lex.l
+++ b/src/read_config_lex.l
@@ -47,7 +47,7 @@ ip6_part {hex_255}":"?
ip6_form1 {ip6_part}{0,7}"::"{ip6_part}{0,7}
ip6_form2 ({hex_255}":"){0,7}{hex_255}
ip6 {ip6_form1}{ip6_cidr}?|{ip6_form2}{ip6_cidr}?
-string [a-zA-Z][a-zA-Z0-9\.\-]*
+string [a-zA-Z][a-zA-Z0-9\.\-\_]*
persistent [P|p][E|e][R|r][S|s][I|i][S|s][T|t][E|e][N|n][T|T]
nack [N|n][A|a][C|c][K|k]
alarm [A|a][L|l][A|a][R|r][M|m]
diff --git a/src/read_config_yy.y b/src/read_config_yy.y
index b824150..73fabbf 100644
--- a/src/read_config_yy.y
+++ b/src/read_config_yy.y
@@ -1612,12 +1612,17 @@ helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}'
exit(EXIT_FAILURE);
}
- /* XXX use configure.ac definitions. */
- helper = helper_find("/usr/lib/conntrack-tools", $2, l4proto, RTLD_NOW);
+#ifdef BUILD_CTHELPER
+ helper = helper_find(CONNTRACKD_LIB_DIR, $2, l4proto, RTLD_NOW);
if (helper == NULL) {
print_err(CTD_CFG_ERROR, "Unknown `%s' helper", $2);
exit(EXIT_FAILURE);
}
+#else
+ print_err(CTD_CFG_ERROR, "Helper support is disabled, recompile "
+ "conntrackd");
+ exit(EXIT_FAILURE);
+#endif
helper_inst = calloc(1, sizeof(struct ctd_helper_instance));
if (helper_inst == NULL)
diff --git a/src/run.c b/src/run.c
index 7fa6889..a9d4862 100644
--- a/src/run.c
+++ b/src/run.c
@@ -55,9 +55,10 @@ void killer(int signo)
if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE))
ctnl_kill();
+#ifdef BUILD_CTHELPER
if (CONFIG(flags) & CTD_HELPER)
cthelper_kill();
-
+#endif
destroy_fds(STATE(fds));
unlink(CONFIG(lockfile));
dlog(LOG_NOTICE, "---- shutdown received ----");
@@ -205,9 +206,10 @@ static int local_handler(int fd, void *data)
if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE))
return ctnl_local(fd, type, data);
+#ifdef BUILD_CTHELPER
if (CONFIG(flags) & CTD_HELPER)
return cthelper_local(fd, type, data);
-
+#endif
return ret;
}
@@ -259,11 +261,12 @@ init(void)
if (ctnl_init() < 0)
return -1;
+#ifdef BUILD_CTHELPER
if (CONFIG(flags) & CTD_HELPER) {
if (cthelper_init() < 0)
return -1;
}
-
+#endif
time(&STATE(stats).daemon_start_time);
dlog(LOG_NOTICE, "initialization completed");
diff --git a/src/sync-notrack.c b/src/sync-notrack.c
index a7df4e7..c810bbb 100644
--- a/src/sync-notrack.c
+++ b/src/sync-notrack.c
@@ -99,7 +99,7 @@ static int kernel_resync_cb(enum nf_conntrack_msg_type type,
static void kernel_resync(void)
{
struct nfct_handle *h;
- u_int32_t family = AF_UNSPEC;
+ uint32_t family = AF_UNSPEC;
int ret;
h = nfct_open(CONFIG(netlink).subsys_id, 0);
diff --git a/src/tcp.c b/src/tcp.c
index af27c46..c8f2544 100644
--- a/src/tcp.c
+++ b/src/tcp.c
@@ -247,13 +247,11 @@ int tcp_accept(struct tcp_sock *m)
/* the other peer wants to connect ... */
ret = accept(m->fd, NULL, NULL);
if (ret == -1) {
- if (errno != EAGAIN) {
- /* unexpected error. Give us another try. */
- m->state = TCP_SERVER_ACCEPTING;
- } else {
- /* waiting for new connections. */
- m->state = TCP_SERVER_ACCEPTING;
- }
+ /* unexpected error: Give us another try. Or we have hit
+ * -EAGAIN, in that case we remain in the accepting connections
+ * state.
+ */
+ m->state = TCP_SERVER_ACCEPTING;
} else {
/* the peer finally got connected. */
if (fcntl(ret, F_SETFL, O_NONBLOCK) == -1) {
diff --git a/src/udp.c b/src/udp.c
index ecaa46e..d0a7f5b 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -136,14 +136,18 @@ struct udp_sock *udp_client_create(struct udp_conf *conf)
m->addr.ipv4.sin_family = AF_INET;
m->addr.ipv4.sin_port = htons(conf->port);
m->addr.ipv4.sin_addr = conf->client.inet_addr;
- m->sockaddr_len = sizeof(struct sockaddr_in);
+ m->sockaddr_len = sizeof(struct sockaddr_in);
break;
case AF_INET6:
m->addr.ipv6.sin6_family = AF_INET6;
m->addr.ipv6.sin6_port = htons(conf->port);
memcpy(&m->addr.ipv6.sin6_addr, &conf->client.inet_addr6,
sizeof(struct in6_addr));
- m->sockaddr_len = sizeof(struct sockaddr_in6);
+ m->sockaddr_len = sizeof(struct sockaddr_in6);
+ /* Bind the sender side to the same interface that we use to
+ * receive sync messages.
+ */
+ m->addr.ipv6.sin6_scope_id = conf->server.ipv6.scope_id;
break;
default:
ret = -1;