From aa925010951e79a860d0c1e4365f72d68eedf02d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 14 Feb 2012 03:17:56 +0100 Subject: conntrack: allow to filter by mark from kernel-space This patch uses the new infrastructure that allows us to filter by mark from kernel-space. This change ensures backward compatibility with kernels with no support for filtering by mark (Linux kernel <= 3.4.x). This requires lastest libnetfilter_conntrack library. Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index 31beba5..b065211 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1,5 +1,6 @@ /* - * (C) 2005-2008 by Pablo Neira Ayuso + * (C) 2005-2012 by Pablo Neira Ayuso + * (C) 2012 by Intra2net AG * * 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 @@ -72,6 +73,9 @@ static struct { /* Allows filtering/setting specific bits in the ctmark */ struct u32_mask mark; + + /* Allow to filter by mark from kernel-space. */ + struct nfct_filter_dump_mark filter_mark_kernel; } tmpl; static int alloc_tmpl_objects(void) @@ -1632,6 +1636,8 @@ int main(int argc, char *argv[]) case 'm': options |= opt2type[c]; parse_u32_mask(optarg, &tmpl.mark); + tmpl.filter_mark_kernel.val = tmpl.mark.value; + tmpl.filter_mark_kernel.mask = tmpl.mark.mask; break; case 'a': fprintf(stderr, "WARNING: ignoring -%c, " @@ -1705,6 +1711,7 @@ int main(int argc, char *argv[]) h->final_check(l4flags, cmd, tmpl.ct); switch(command) { + struct nfct_filter_dump *filter_dump; case CT_LIST: cth = nfct_open(CONNTRACK, 0); @@ -1718,10 +1725,23 @@ int main(int argc, char *argv[]) nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct); + filter_dump = nfct_filter_dump_create(); + if (filter_dump == NULL) + exit_error(OTHER_PROBLEM, "OOM"); + + nfct_filter_dump_set_attr(filter_dump, NFCT_FILTER_DUMP_MARK, + &tmpl.filter_mark_kernel); + nfct_filter_dump_set_attr_u8(filter_dump, + NFCT_FILTER_DUMP_L3NUM, + family); + if (options & CT_OPT_ZERO) - res = nfct_query(cth, NFCT_Q_DUMP_RESET, &family); + res = nfct_query(cth, NFCT_Q_DUMP_FILTER_RESET, + filter_dump); else - res = nfct_query(cth, NFCT_Q_DUMP, &family); + res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump); + + nfct_filter_dump_destroy(filter_dump); if (dump_xml_header_done == 0) { printf("\n"); @@ -1800,7 +1820,20 @@ int main(int argc, char *argv[]) nfct_callback_register(cth, NFCT_T_ALL, delete_cb, tmpl.ct); - res = nfct_query(cth, NFCT_Q_DUMP, &family); + filter_dump = nfct_filter_dump_create(); + if (filter_dump == NULL) + exit_error(OTHER_PROBLEM, "OOM"); + + nfct_filter_dump_set_attr(filter_dump, NFCT_FILTER_DUMP_MARK, + &tmpl.filter_mark_kernel); + nfct_filter_dump_set_attr_u8(filter_dump, + NFCT_FILTER_DUMP_L3NUM, + family); + + res = nfct_query(cth, NFCT_Q_DUMP_FILTER, filter_dump); + + nfct_filter_dump_destroy(filter_dump); + nfct_close(ith); nfct_close(cth); break; -- cgit v1.2.3 From 2a022fb7174939a80d51936325f6af562ad5a85f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 21 Feb 2012 15:59:00 +0100 Subject: conntrackd: allow using lower/upper case in ExpectationSync You can use: ExpectationSync { ftp ras q.931 sip } or: ExpectationSync { FTP RAS Q.931 SIP } no matter lower/upper case. Signed-off-by: Pablo Neira Ayuso --- src/filter.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/filter.c b/src/filter.c index afefbfa..39dd4ca 100644 --- a/src/filter.c +++ b/src/filter.c @@ -1,6 +1,7 @@ /* - * (C) 2006-2008 by Pablo Neira Ayuso - * + * (C) 2006-2012 by Pablo Neira Ayuso + * (C) 2011-2012 by Vyatta Inc + * * 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 @@ -449,7 +450,7 @@ int exp_filter_add(struct exp_filter *f, const char *helper_name) return -1; list_for_each_entry(item, &f->list, head) { - if (strncmp(item->helper_name, helper_name, + if (strncasecmp(item->helper_name, helper_name, NFCT_HELPER_NAME_MAX) == 0) { return -1; } @@ -475,7 +476,7 @@ int exp_filter_find(struct exp_filter *f, const struct nf_expect *exp) const char *name = nfexp_get_attr(exp, ATTR_EXP_HELPER_NAME); /* we allow partial matching to support things like sip-PORT. */ - if (strncmp(item->helper_name, name, + if (strncasecmp(item->helper_name, name, strlen(item->helper_name)) == 0) { return 1; } -- cgit v1.2.3 From bb2534c7aaf0bdb6521371b8a31af6333d3a6a2d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 21 Feb 2012 16:03:21 +0100 Subject: doc: add ras, q.931 and h.245 to examples configuration file Now it includes: ExpectationSync { ... ras q.931 h.245 } Which are the set of helpers for h.323. Signed-off-by: Pablo Neira Ayuso --- doc/sync/alarm/conntrackd.conf | 4 +++- doc/sync/ftfw/conntrackd.conf | 4 +++- doc/sync/notrack/conntrackd.conf | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/doc/sync/alarm/conntrackd.conf b/doc/sync/alarm/conntrackd.conf index deed291..b9520fb 100644 --- a/doc/sync/alarm/conntrackd.conf +++ b/doc/sync/alarm/conntrackd.conf @@ -198,7 +198,9 @@ Sync { # # ExpectationSync { # ftp - # h323 + # ras + # q.931 + # h.245 # sip # } # diff --git a/doc/sync/ftfw/conntrackd.conf b/doc/sync/ftfw/conntrackd.conf index 0304f0f..53a7d0f 100644 --- a/doc/sync/ftfw/conntrackd.conf +++ b/doc/sync/ftfw/conntrackd.conf @@ -221,7 +221,9 @@ Sync { # # ExpectationSync { # ftp - # h323 + # ras + # q.931 + # h.245 # sip # } # diff --git a/doc/sync/notrack/conntrackd.conf b/doc/sync/notrack/conntrackd.conf index 34e7b32..11f022e 100644 --- a/doc/sync/notrack/conntrackd.conf +++ b/doc/sync/notrack/conntrackd.conf @@ -260,7 +260,9 @@ Sync { # # ExpectationSync { # ftp - # h323 + # ras + # q.931 + # h.245 # sip # } # -- cgit v1.2.3 From 1de3034f8c4f597cbe4be35b2f84e2848e46e64e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 5 Mar 2012 23:13:12 +0100 Subject: doc: fix example on how to filter events via iptables CT target You have to use this: iptables -I PREROUTING -t raw -j CT --ctevents assured,destroy instead of: iptables -I PREROUTING -t raw -j CT --ctevents assured Otherwise, conntrackd cache gets full since no destroy events are delivered. Reported-by: Kerin Millar Signed-off-by: Pablo Neira Ayuso --- doc/manual/conntrack-tools.tmpl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index 4936a76..dbf836d 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -641,10 +641,11 @@ Sync { broken. The following example shows how to only generate the - assured event: + assured and destroy + events: - # iptables -I PREROUTING -t raw -j CT --ctevents assured + # iptables -I PREROUTING -t raw -j CT --ctevents assured,destroy Assured flows -- cgit v1.2.3 From 36da3b40b821452d2d4607928ce54ced707541df Mon Sep 17 00:00:00 2001 From: Adrian Bridgett Date: Tue, 20 Mar 2012 13:02:54 +0100 Subject: src: manpage and help display improvements This patch adds missing information regarding several conntrackd options to the manpage and the help info that is displayed in the command line. Signed-off-by: Pablo Neira Ayuso --- conntrack.8 | 6 +++--- conntrackd.8 | 10 ++++++++++ src/main.c | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/conntrack.8 b/conntrack.8 index 6525123..a411fd4 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -6,17 +6,17 @@ .SH NAME conntrack \- command line interface for netfilter connection tracking .SH SYNOPSIS -.BR "conntrack -L [table] [-z]" +.BR "conntrack -L [table] [options] [-z]" .br .BR "conntrack -G [table] parameters" .br -.BR "conntrack -D [table] paramaters" +.BR "conntrack -D [table] parameters" .br .BR "conntrack -I [table] parameters" .br .BR "conntrack -U [table] parameters" .br -.BR "conntrack -E [table] parameters" +.BR "conntrack -E [table] [options]" .br .BR "conntrack -F [table]" .br diff --git a/conntrackd.8 b/conntrackd.8 index f07ad7a..131a7ac 100644 --- a/conntrackd.8 +++ b/conntrackd.8 @@ -41,10 +41,16 @@ Flush the internal and/or external cache Flush the kernel conntrack table (if you use a Linux kernel >= 2.6.29, this option will not flush your internal and external cache). .TP +.BI "-c " +Commit external cache to conntrack table. +.TP .BI "-B " Force a bulk send to other replica firewalls. With this command, you will ask conntrackd to send the state-entries that it owns to others. .TP +.BI "-n " +Request resync with other node (only FT-FW and NOTRACK modes). +.TP .BI "-k " Kill the daemon .TP @@ -69,6 +75,10 @@ Display version information. .TP .BI "-h " Display help information. +.TP +.BI "-C config file" +Configuration file path. +.TP .SH DIAGNOSTICS The exit code is 0 for correct function. Errors cause an exit code of 1. .SH EXAMPLES diff --git a/src/main.c b/src/main.c index 0850a29..f7803fd 100644 --- a/src/main.c +++ b/src/main.c @@ -49,6 +49,7 @@ static const char usage_client_commands[] = "dump statistics\n" " -R [ct|expect], resync with kernel conntrack table\n" " -n, request resync with other node (only FT-FW and NOTRACK modes)\n" + " -B, force a bulk send to other replica firewalls\n" " -x, dump cache in XML format (requires -i or -e)\n" " -t, reset the kernel timeout (see PurgeTimeout clause)\n" " -v, display conntrackd version\n" -- cgit v1.2.3 From 405c4f7dbfb16e69d75df5b631f195f5b9da29e4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 27 Mar 2012 11:18:32 +0200 Subject: icmp[v6]: --icmp[v6]-[type|code] are optional for updates and deletes Signed-off-by: Pablo Neira Ayuso --- extensions/libct_proto_icmp.c | 4 ++-- extensions/libct_proto_icmpv6.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/libct_proto_icmp.c b/extensions/libct_proto_icmp.c index 3a346ed..d04397f 100644 --- a/extensions/libct_proto_icmp.c +++ b/extensions/libct_proto_icmp.c @@ -43,8 +43,8 @@ static char icmp_commands_v_options[NUMBER_OF_CMD][ICMP_NUMBER_OF_OPT] = /* 1 2 3 */ /*CT_LIST*/ {2,2,2}, /*CT_CREATE*/ {1,1,2}, -/*CT_UPDATE*/ {1,1,2}, -/*CT_DELETE*/ {1,1,2}, +/*CT_UPDATE*/ {2,2,2}, +/*CT_DELETE*/ {2,2,2}, /*CT_GET*/ {1,1,2}, /*CT_FLUSH*/ {0,0,0}, /*CT_EVENT*/ {2,2,2}, diff --git a/extensions/libct_proto_icmpv6.c b/extensions/libct_proto_icmpv6.c index 070eb7f..f8c2c68 100644 --- a/extensions/libct_proto_icmpv6.c +++ b/extensions/libct_proto_icmpv6.c @@ -46,8 +46,8 @@ static char icmpv6_commands_v_options[NUMBER_OF_CMD][ICMPV6_NUMBER_OF_OPT] = /* 1 2 3 */ /*CT_LIST*/ {2,2,2}, /*CT_CREATE*/ {1,1,2}, -/*CT_UPDATE*/ {1,1,2}, -/*CT_DELETE*/ {1,1,2}, +/*CT_UPDATE*/ {2,2,2}, +/*CT_DELETE*/ {2,2,2}, /*CT_GET*/ {1,1,2}, /*CT_FLUSH*/ {0,0,0}, /*CT_EVENT*/ {2,2,2}, -- cgit v1.2.3 From 867b5b6496a3296078146ba3d06616eda3b0717e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 18 May 2012 01:36:49 +0000 Subject: conntrack: flush stdout for each expectation event, too else, piping "conntrack -E expect" output will be buffered/delayed, which is not what users expect. Normal conntrack events are already flushed. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/conntrack.c b/src/conntrack.c index b065211..0920bc5 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1380,6 +1380,7 @@ static int event_exp_cb(enum nf_conntrack_msg_type type, nfexp_snprintf(buf,sizeof(buf), exp, type, op_type, op_flags); printf("%s\n", buf); + fflush(stdout); counter++; return NFCT_CB_CONTINUE; -- cgit v1.2.3 From d2e942c76f87ea061d5e8643007f1d4c3ed39694 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 10 May 2012 10:15:09 +0200 Subject: src: integrate nfct into the conntrack-tools tree I'll need for the upcoming cthelper infrastructure. Moreover, we avoid more fragmentation in the netfilter user-space utilities. And the plan is that `nfct' will replace `conntrack' at some point. Signed-off-by: Pablo Neira Ayuso --- Make_global.am | 5 +- configure.ac | 4 +- include/Makefile.am | 3 +- include/linux/Makefile.am | 1 + include/linux/netfilter/Makefile.am | 1 + include/linux/netfilter/nfnetlink.h | 94 +++++ include/linux/netfilter/nfnetlink_cttimeout.h | 114 ++++++ include/nfct.h | 29 ++ src/Makefile.am | 8 +- src/nfct-extensions/timeout.c | 486 ++++++++++++++++++++++++++ src/nfct.c | 116 ++++++ 11 files changed, 856 insertions(+), 5 deletions(-) create mode 100644 include/linux/Makefile.am create mode 100644 include/linux/netfilter/Makefile.am create mode 100644 include/linux/netfilter/nfnetlink.h create mode 100644 include/linux/netfilter/nfnetlink_cttimeout.h create mode 100644 include/nfct.h create mode 100644 src/nfct-extensions/timeout.c create mode 100644 src/nfct.c diff --git a/Make_global.am b/Make_global.am index e8f603a..7b5342d 100644 --- a/Make_global.am +++ b/Make_global.am @@ -2,5 +2,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = -std=gnu99 -W -Wall \ -Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \ - -Wno-unused-parameter ${LIBNFNETLINK_CFLAGS} \ - ${LIBNETFILTER_CONNTRACK_CFLAGS} + -Wno-unused-parameter ${LIBNFNETLINK_CFLAGS} ${LIBMNL_CFLAGS} \ + ${LIBNETFILTER_CONNTRACK_CFLAGS} \ + ${LIBNETFILTER_CTTIMEOUT_CFLAGS} diff --git a/configure.ac b/configure.ac index 26a7e02..5c4ac58 100644 --- a/configure.ac +++ b/configure.ac @@ -52,7 +52,9 @@ else fi PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.0]) +PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.0]) PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.0]) +PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) AC_CHECK_HEADERS([linux/capability.h],, [AC_MSG_ERROR([Cannot find linux/capabibility.h])]) @@ -112,5 +114,5 @@ dnl debug/src/Makefile dnl extensions/Makefile dnl src/Makefile]) -AC_CONFIG_FILES([Makefile src/Makefile include/Makefile extensions/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile]) AC_OUTPUT diff --git a/include/Makefile.am b/include/Makefile.am index cbbca6b..138005d 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,8 +1,9 @@ +SUBDIRS = linux noinst_HEADERS = alarm.h jhash.h cache.h linux_list.h linux_rbtree.h \ sync.h conntrackd.h local.h udp.h tcp.h \ debug.h log.h hash.h mcast.h conntrack.h \ network.h filter.h queue.h vector.h cidr.h \ traffic_stats.h netlink.h fds.h event.h bitops.h channel.h \ - process.h origin.h internal.h external.h date.h + process.h origin.h internal.h external.h date.h nfct.h diff --git a/include/linux/Makefile.am b/include/linux/Makefile.am new file mode 100644 index 0000000..38eb109 --- /dev/null +++ b/include/linux/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = netfilter diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am new file mode 100644 index 0000000..84315e3 --- /dev/null +++ b/include/linux/netfilter/Makefile.am @@ -0,0 +1 @@ +noinst_HEADERS = nfnetlink.h nfnetlink_cttimeout.h diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h new file mode 100644 index 0000000..b64454c --- /dev/null +++ b/include/linux/netfilter/nfnetlink.h @@ -0,0 +1,94 @@ +#ifndef _NFNETLINK_H +#define _NFNETLINK_H +#include +#include + +enum nfnetlink_groups { + NFNLGRP_NONE, +#define NFNLGRP_NONE NFNLGRP_NONE + NFNLGRP_CONNTRACK_NEW, +#define NFNLGRP_CONNTRACK_NEW NFNLGRP_CONNTRACK_NEW + NFNLGRP_CONNTRACK_UPDATE, +#define NFNLGRP_CONNTRACK_UPDATE NFNLGRP_CONNTRACK_UPDATE + NFNLGRP_CONNTRACK_DESTROY, +#define NFNLGRP_CONNTRACK_DESTROY NFNLGRP_CONNTRACK_DESTROY + NFNLGRP_CONNTRACK_EXP_NEW, +#define NFNLGRP_CONNTRACK_EXP_NEW NFNLGRP_CONNTRACK_EXP_NEW + NFNLGRP_CONNTRACK_EXP_UPDATE, +#define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE + NFNLGRP_CONNTRACK_EXP_DESTROY, +#define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY + __NFNLGRP_MAX, +}; +#define NFNLGRP_MAX (__NFNLGRP_MAX - 1) + +/* General form of address family dependent message. + */ +struct nfgenmsg { + __u8 nfgen_family; /* AF_xxx */ + __u8 version; /* nfnetlink version */ + __be16 res_id; /* resource id */ +}; + +#define NFNETLINK_V0 0 + +/* netfilter netlink message types are split in two pieces: + * 8 bit subsystem, 8bit operation. + */ + +#define NFNL_SUBSYS_ID(x) ((x & 0xff00) >> 8) +#define NFNL_MSG_TYPE(x) (x & 0x00ff) + +/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS() + * won't work anymore */ +#define NFNL_SUBSYS_NONE 0 +#define NFNL_SUBSYS_CTNETLINK 1 +#define NFNL_SUBSYS_CTNETLINK_EXP 2 +#define NFNL_SUBSYS_QUEUE 3 +#define NFNL_SUBSYS_ULOG 4 +#define NFNL_SUBSYS_OSF 5 +#define NFNL_SUBSYS_IPSET 6 +#define NFNL_SUBSYS_ACCT 7 +#define NFNL_SUBSYS_COUNT 8 + +#ifdef __KERNEL__ + +#include +#include +#include + +struct nfnl_callback { + int (*call)(struct sock *nl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]); + int (*call_rcu)(struct sock *nl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]); + const struct nla_policy *policy; /* netlink attribute policy */ + const u_int16_t attr_count; /* number of nlattr's */ +}; + +struct nfnetlink_subsystem { + const char *name; + __u8 subsys_id; /* nfnetlink subsystem ID */ + __u8 cb_count; /* number of callbacks */ + const struct nfnl_callback *cb; /* callback for individual types */ +}; + +extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); +extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); + +extern int nfnetlink_has_listeners(struct net *net, unsigned int group); +extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group, + int echo, gfp_t flags); +extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); +extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); + +extern void nfnl_lock(void); +extern void nfnl_unlock(void); + +#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ + MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) + +#endif /* __KERNEL__ */ +#endif /* _NFNETLINK_H */ diff --git a/include/linux/netfilter/nfnetlink_cttimeout.h b/include/linux/netfilter/nfnetlink_cttimeout.h new file mode 100644 index 0000000..a2810a7 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_cttimeout.h @@ -0,0 +1,114 @@ +#ifndef _CTTIMEOUT_NETLINK_H +#define _CTTIMEOUT_NETLINK_H +#include + +enum ctnl_timeout_msg_types { + IPCTNL_MSG_TIMEOUT_NEW, + IPCTNL_MSG_TIMEOUT_GET, + IPCTNL_MSG_TIMEOUT_DELETE, + + IPCTNL_MSG_TIMEOUT_MAX +}; + +enum ctattr_timeout { + CTA_TIMEOUT_UNSPEC, + CTA_TIMEOUT_NAME, + CTA_TIMEOUT_L3PROTO, + CTA_TIMEOUT_L4PROTO, + CTA_TIMEOUT_DATA, + CTA_TIMEOUT_USE, + __CTA_TIMEOUT_MAX +}; +#define CTA_TIMEOUT_MAX (__CTA_TIMEOUT_MAX - 1) + +enum ctattr_timeout_generic { + CTA_TIMEOUT_GENERIC_UNSPEC, + CTA_TIMEOUT_GENERIC_TIMEOUT, + __CTA_TIMEOUT_GENERIC_MAX +}; +#define CTA_TIMEOUT_GENERIC_MAX (__CTA_TIMEOUT_GENERIC_MAX - 1) + +enum ctattr_timeout_tcp { + CTA_TIMEOUT_TCP_UNSPEC, + CTA_TIMEOUT_TCP_SYN_SENT, + CTA_TIMEOUT_TCP_SYN_RECV, + CTA_TIMEOUT_TCP_ESTABLISHED, + CTA_TIMEOUT_TCP_FIN_WAIT, + CTA_TIMEOUT_TCP_CLOSE_WAIT, + CTA_TIMEOUT_TCP_LAST_ACK, + CTA_TIMEOUT_TCP_TIME_WAIT, + CTA_TIMEOUT_TCP_CLOSE, + CTA_TIMEOUT_TCP_SYN_SENT2, + CTA_TIMEOUT_TCP_RETRANS, + CTA_TIMEOUT_TCP_UNACK, + __CTA_TIMEOUT_TCP_MAX +}; +#define CTA_TIMEOUT_TCP_MAX (__CTA_TIMEOUT_TCP_MAX - 1) + +enum ctattr_timeout_udp { + CTA_TIMEOUT_UDP_UNSPEC, + CTA_TIMEOUT_UDP_UNREPLIED, + CTA_TIMEOUT_UDP_REPLIED, + __CTA_TIMEOUT_UDP_MAX +}; +#define CTA_TIMEOUT_UDP_MAX (__CTA_TIMEOUT_UDP_MAX - 1) + +enum ctattr_timeout_udplite { + CTA_TIMEOUT_UDPLITE_UNSPEC, + CTA_TIMEOUT_UDPLITE_UNREPLIED, + CTA_TIMEOUT_UDPLITE_REPLIED, + __CTA_TIMEOUT_UDPLITE_MAX +}; +#define CTA_TIMEOUT_UDPLITE_MAX (__CTA_TIMEOUT_UDPLITE_MAX - 1) + +enum ctattr_timeout_icmp { + CTA_TIMEOUT_ICMP_UNSPEC, + CTA_TIMEOUT_ICMP_TIMEOUT, + __CTA_TIMEOUT_ICMP_MAX +}; +#define CTA_TIMEOUT_ICMP_MAX (__CTA_TIMEOUT_ICMP_MAX - 1) + +enum ctattr_timeout_dccp { + CTA_TIMEOUT_DCCP_UNSPEC, + CTA_TIMEOUT_DCCP_REQUEST, + CTA_TIMEOUT_DCCP_RESPOND, + CTA_TIMEOUT_DCCP_PARTOPEN, + CTA_TIMEOUT_DCCP_OPEN, + CTA_TIMEOUT_DCCP_CLOSEREQ, + CTA_TIMEOUT_DCCP_CLOSING, + CTA_TIMEOUT_DCCP_TIMEWAIT, + __CTA_TIMEOUT_DCCP_MAX +}; +#define CTA_TIMEOUT_DCCP_MAX (__CTA_TIMEOUT_DCCP_MAX - 1) + +enum ctattr_timeout_sctp { + CTA_TIMEOUT_SCTP_UNSPEC, + CTA_TIMEOUT_SCTP_CLOSED, + CTA_TIMEOUT_SCTP_COOKIE_WAIT, + CTA_TIMEOUT_SCTP_COOKIE_ECHOED, + CTA_TIMEOUT_SCTP_ESTABLISHED, + CTA_TIMEOUT_SCTP_SHUTDOWN_SENT, + CTA_TIMEOUT_SCTP_SHUTDOWN_RECD, + CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT, + __CTA_TIMEOUT_SCTP_MAX +}; +#define CTA_TIMEOUT_SCTP_MAX (__CTA_TIMEOUT_SCTP_MAX - 1) + +enum ctattr_timeout_icmpv6 { + CTA_TIMEOUT_ICMPV6_UNSPEC, + CTA_TIMEOUT_ICMPV6_TIMEOUT, + __CTA_TIMEOUT_ICMPV6_MAX +}; +#define CTA_TIMEOUT_ICMPV6_MAX (__CTA_TIMEOUT_ICMPV6_MAX - 1) + +enum ctattr_timeout_gre { + CTA_TIMEOUT_GRE_UNSPEC, + CTA_TIMEOUT_GRE_UNREPLIED, + CTA_TIMEOUT_GRE_REPLIED, + __CTA_TIMEOUT_GRE_MAX +}; +#define CTA_TIMEOUT_GRE_MAX (__CTA_TIMEOUT_GRE_MAX - 1) + +#define CTNL_TIMEOUT_NAME_MAX 32 + +#endif diff --git a/include/nfct.h b/include/nfct.h new file mode 100644 index 0000000..d6271cf --- /dev/null +++ b/include/nfct.h @@ -0,0 +1,29 @@ +#ifndef _NFCT_H_ +#define _NFCT_H_ + +enum { + NFCT_SUBSYS_NONE = 0, + NFCT_SUBSYS_TIMEOUT, + NFCT_SUBSYS_VERSION, + NFCT_SUBSYS_HELP, +}; + +enum { + NFCT_CMD_NONE = 0, + NFCT_CMD_LIST, + NFCT_CMD_ADD, + NFCT_CMD_DELETE, + NFCT_CMD_GET, + NFCT_CMD_FLUSH, +}; + +void nfct_perror(const char *msg); + +int nfct_cmd_timeout_parse_params(int argc, char *argv[]); +int nfct_cmd_timeout_list(int argc, char *argv[]); +int nfct_cmd_timeout_add(int argc, char *argv[]); +int nfct_cmd_timeout_delete(int argc, char *argv[]); +int nfct_cmd_timeout_get(int argc, char *argv[]); +int nfct_cmd_timeout_flush(int argc, char *argv[]); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 7d7b2ac..5dbdef3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,11 +4,17 @@ AM_YFLAGS = -d CLEANFILES = read_config_yy.c read_config_lex.c -sbin_PROGRAMS = conntrack conntrackd +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} +nfct_SOURCES = nfct.c \ + nfct-extensions/timeout.c +nfct_LDADD = ${LIBMNL_LIBS} \ + ${LIBNETFILTER_CONNTRACK_LIBS} \ + ${LIBNETFILTER_CTTIMEOUT_LIBS} + conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ local.c log.c mcast.c udp.c netlink.c vector.c \ filter.c fds.c event.c process.c origin.c date.c \ diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c new file mode 100644 index 0000000..a69537d --- /dev/null +++ b/src/nfct-extensions/timeout.c @@ -0,0 +1,486 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Vyatta Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nfct.h" + +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]); +} + +int nfct_cmd_timeout_parse_params(int argc, char *argv[]) +{ + int cmd = NFCT_CMD_NONE, ret = 0; + + 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]); + nfct_cmd_timeout_usage(argv); + return -1; + } + switch(cmd) { + case NFCT_CMD_LIST: + ret = nfct_cmd_timeout_list(argc, argv); + break; + case NFCT_CMD_ADD: + ret = nfct_cmd_timeout_add(argc, argv); + break; + case NFCT_CMD_DELETE: + ret = nfct_cmd_timeout_delete(argc, argv); + break; + case NFCT_CMD_GET: + ret = nfct_cmd_timeout_get(argc, argv); + break; + case NFCT_CMD_FLUSH: + ret = nfct_cmd_timeout_flush(argc, argv); + break; + } + + return 0; +} + +static int nfct_timeout_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nfct_timeout *t; + char buf[4096]; + + t = nfct_timeout_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + goto err; + } + + if (nfct_timeout_nlmsg_parse_payload(nlh, t) < 0) { + nfct_perror("nfct_timeout_nlmsg_parse_payload"); + goto err_free; + } + + nfct_timeout_snprintf(buf, sizeof(buf), t, 0); + printf("%s\n", buf); + +err_free: + nfct_timeout_free(t); +err: + return MNL_CB_OK; +} + +int nfct_cmd_timeout_list(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"); + return -1; + } + + seq = time(NULL); + 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"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + +static uint32_t nfct_timeout_attr_max[IPPROTO_MAX] = { + [IPPROTO_ICMP] = NFCT_TIMEOUT_ATTR_ICMP_MAX, + [IPPROTO_TCP] = NFCT_TIMEOUT_ATTR_TCP_MAX, + [IPPROTO_UDP] = NFCT_TIMEOUT_ATTR_UDP_MAX, + [IPPROTO_UDPLITE] = NFCT_TIMEOUT_ATTR_UDPLITE_MAX, + [IPPROTO_SCTP] = NFCT_TIMEOUT_ATTR_SCTP_MAX, + [IPPROTO_DCCP] = NFCT_TIMEOUT_ATTR_DCCP_MAX, + [IPPROTO_ICMPV6] = NFCT_TIMEOUT_ATTR_ICMPV6_MAX, + [IPPROTO_GRE] = NFCT_TIMEOUT_ATTR_GRE_MAX, + [IPPROTO_RAW] = NFCT_TIMEOUT_ATTR_GENERIC_MAX, +}; + +int nfct_cmd_timeout_add(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; + uint16_t l3proto; + uint8_t l4proto; + int ret, i; + unsigned int j; + + 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) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + 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"); + return -1; + } + nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO, l4proto); + + for (i=6; i= argc) { + nfct_perror("missing value for this timeout"); + return -1; + } + nfct_timeout_policy_attr_set_u32(t, matching, + atoi(argv[i+1])); + matching = -1; + } else { + fprintf(stderr, "nfct v%s: Wrong state name: `%s' " + "for protocol `%s'\n", + VERSION, argv[i], argv[5]); + 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); + + 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"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_timeout_delete(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"); + return -1; + } else if (argc > 4) { + nfct_perror("too many arguments"); + 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]); + + seq = time(NULL); + nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE, + 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"); + 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"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_timeout_get(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"); + return -1; + } else if (argc > 4) { + nfct_perror("too many arguments"); + 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]); + + seq = time(NULL); + nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_GET, + 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"); + 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"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_timeout_flush(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"); + return -1; + } + + seq = time(NULL); + nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE, + 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"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} diff --git a/src/nfct.c b/src/nfct.c new file mode 100644 index 0000000..db629e7 --- /dev/null +++ b/src/nfct.c @@ -0,0 +1,116 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Vyatta Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nfct.h" + +static int nfct_cmd_version(int argc, char *argv[]); +static int nfct_cmd_help(int argc, char *argv[]); + +static void usage(char *argv[]) +{ + fprintf(stderr, "Usage: %s subsystem command [parameters]...\n", + argv[0]); +} + +void nfct_perror(const char *msg) +{ + if (errno == 0) { + fprintf(stderr, "nfct v%s: %s\n", VERSION, msg); + } else { + fprintf(stderr, "nfct v%s: %s: %s\n", + VERSION, msg, strerror(errno)); + } +} + +int main(int argc, char *argv[]) +{ + int subsys = NFCT_SUBSYS_NONE, ret = 0; + + if (argc < 2) { + usage(argv); + exit(EXIT_FAILURE); + } + if (strncmp(argv[1], "timeout", strlen(argv[1])) == 0) { + subsys = NFCT_SUBSYS_TIMEOUT; + } 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); + } + + switch(subsys) { + case NFCT_SUBSYS_TIMEOUT: + ret = nfct_cmd_timeout_parse_params(argc, argv); + break; + case NFCT_SUBSYS_VERSION: + ret = nfct_cmd_version(argc, argv); + break; + case NFCT_SUBSYS_HELP: + ret = nfct_cmd_help(argc, argv); + break; + } + return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} + +static const char version_msg[] = + "nfct v%s: utility for the Netfilter's Connection Tracking System\n" + "Copyright (C) 2012 Pablo Neira Ayuso \n" + "This program comes with ABSOLUTELY NO WARRANTY.\n" + "This is free software, and you are welcome to redistribute it under " + "certain \nconditions; see LICENSE file distributed in this package " + "for details.\n"; + +static int nfct_cmd_version(int argc, char *argv[]) +{ + printf(version_msg, VERSION); + return 0; +} + +static const char help_msg[] = + "nfct v%s: utility for the Netfilter's Connection Tracking System\n" + "Usage: %s command [parameters]...\n\n" + "Subsystem:\n" + " timeout\t\tAllows definition of fine-grain timeout policies\n" + " version\t\tDisplay version and disclaimer\n" + " help\t\t\tDisplay this help message\n" + "Commands:\n" + " list [reset]\t\tList the accounting object table (and reset)\n" + " add object-name\tAdd new accounting object to table\n" + " delete object-name\tDelete existing accounting object\n" + " get object-name\tGet existing accounting object\n" + " flush\t\t\tFlush accounting object table\n"; + +static int nfct_cmd_help(int argc, char *argv[]) +{ + printf(help_msg, VERSION, argv[0]); + return 0; +} -- cgit v1.2.3 From 5b6f524eea1ea8d2f0ecb2e17abfba7df708732f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 15:46:52 +0200 Subject: tests: add nfct tests for cttimeout This patch adds the automated tests for the cttimeout infrastructure. Signed-off-by: Pablo Neira Ayuso --- tests/nfct/run-test.sh | 20 +++++++++ tests/nfct/test-live.sh | 73 +++++++++++++++++++++++++++++++ tests/nfct/test.c | 100 +++++++++++++++++++++++++++++++++++++++++++ tests/nfct/timeout/00tcp | 16 +++++++ tests/nfct/timeout/01udp | 16 +++++++ tests/nfct/timeout/02generic | 16 +++++++ tests/nfct/timeout/03udplite | 16 +++++++ tests/nfct/timeout/04icmp | 16 +++++++ tests/nfct/timeout/05icmpv6 | 16 +++++++ tests/nfct/timeout/06sctp | 16 +++++++ tests/nfct/timeout/07dccp | 16 +++++++ tests/nfct/timeout/08gre | 16 +++++++ 12 files changed, 337 insertions(+) create mode 100644 tests/nfct/run-test.sh create mode 100644 tests/nfct/test-live.sh create mode 100644 tests/nfct/test.c create mode 100644 tests/nfct/timeout/00tcp create mode 100644 tests/nfct/timeout/01udp create mode 100644 tests/nfct/timeout/02generic create mode 100644 tests/nfct/timeout/03udplite create mode 100644 tests/nfct/timeout/04icmp create mode 100644 tests/nfct/timeout/05icmpv6 create mode 100644 tests/nfct/timeout/06sctp create mode 100644 tests/nfct/timeout/07dccp create mode 100644 tests/nfct/timeout/08gre diff --git a/tests/nfct/run-test.sh b/tests/nfct/run-test.sh new file mode 100644 index 0000000..9bcad0d --- /dev/null +++ b/tests/nfct/run-test.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +UID=`id -u` +if [ $UID -ne 0 ] +then + echo "Run this test as root" + exit 1 +fi + +gcc test.c -o test +# +# XXX: module auto-load not support by nfnetlink_cttimeout yet :-( +# +modprobe nf_conntrack_ipv4 +modprobe nf_conntrack_ipv6 +modprobe nf_conntrack_proto_udplite +modprobe nf_conntrack_proto_sctp +modprobe nf_conntrack_proto_dccp +modprobe nf_conntrack_proto_gre +./test timeout diff --git a/tests/nfct/test-live.sh b/tests/nfct/test-live.sh new file mode 100644 index 0000000..c338e63 --- /dev/null +++ b/tests/nfct/test-live.sh @@ -0,0 +1,73 @@ +#!/bin/sh +# +# simple testing for cttimeout infrastructure using one single computer +# + +WAIT_BETWEEN_TESTS=10 + +# flush cttimeout table +nfct timeout flush + +# flush the conntrack table +conntrack -F + +# +# No.1: test generic timeout policy +# + +echo "---- test no. 1 ----" + +conntrack -E -p 13 & + +nfct timeout add test-generic inet generic timeout 100 +iptables -I OUTPUT -t raw -p all -j CT --timeout test-generic +hping3 -c 1 -V -I eth0 -0 8.8.8.8 -H 13 + +killall -15 conntrack + +echo "---- end test no. 1 ----" + +sleep $WAIT_BETWEEN_TESTS + +iptables -D OUTPUT -t raw -p all -j CT --timeout test-generic +nfct timeout del test-generic + +# +# No.2: test TCP timeout policy +# + +echo "---- test no. 2 ----" + +conntrack -E -p tcp & + +nfct timeout add test-tcp inet tcp syn_sent 100 +iptables -I OUTPUT -t raw -p tcp -j CT --timeout test-tcp +hping3 -V -S -p 80 -s 5050 8.8.8.8 -c 1 + +sleep $WAIT_BETWEEN_TESTS + +iptables -D OUTPUT -t raw -p tcp -j CT --timeout test-tcp +nfct timeout del test-tcp + +killall -15 conntrack + +echo "---- end test no. 2 ----" + +# +# No. 3: test ICMP timeout policy +# + +echo "---- test no. 3 ----" + +conntrack -E -p icmp & + +nfct timeout add test-icmp inet icmp timeout 50 +iptables -I OUTPUT -t raw -p icmp -j CT --timeout test-icmp +hping3 -1 8.8.8.8 -c 2 + +iptables -D OUTPUT -t raw -p icmp -j CT --timeout test-icmp +nfct timeout del test-icmp + +killall -15 conntrack + +echo "---- end test no. 3 ----" diff --git a/tests/nfct/test.c b/tests/nfct/test.c new file mode 100644 index 0000000..a833dcc --- /dev/null +++ b/tests/nfct/test.c @@ -0,0 +1,100 @@ +/* + * (c) 2012 by Pablo Neira Ayuso + * + * Extremely simple test utility for the command line tools. + * + * Based on test-conntrack.c + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PATH "/usr/sbin" + +int main(int argc, char *argv[]) +{ + int ret, ok = 0, bad = 0, line; + FILE *fp; + DIR *d; + char buf[1024]; + struct dirent *dent; + char file[1024]; + + if (argc < 2) { + fprintf(stderr, "Usage: %s directory\n", argv[0]); + exit(EXIT_FAILURE); + } + + d = opendir(argv[1]); + if (d == NULL) { + perror("opendir"); + exit(EXIT_FAILURE); + } + + setenv("PATH", PATH, 1); + + while ((dent = readdir(d)) != NULL) { + + sprintf(file, "%s/%s", argv[1], dent->d_name); + + line = 0; + + fp = fopen(file, "r"); + if (fp == NULL) { + perror("cannot find testsuite file"); + exit(EXIT_FAILURE); + } + + while (fgets(buf, sizeof(buf), fp)) { + char *res; + + line++; + + if (buf[0] == '#' || buf[0] == ' ') + continue; + + res = strchr(buf, ';'); + if (!res) { + printf("malformed file %s at line %d\n", + dent->d_name, line); + exit(EXIT_FAILURE); + } + *res = '\0'; + res+=2; + + printf("(%d) Executing: %s\n", line, buf); + + ret = system(buf); + + if (WIFEXITED(ret) && + WEXITSTATUS(ret) == EXIT_SUCCESS) { + if (res[0] == 'O' && + res[1] == 'K') + ok++; + else { + bad++; + printf("^----- BAD\n"); + } + } else { + if (res[0] == 'B' && + res[1] == 'A' && + res[2] == 'D') + ok++; + else { + bad++; + printf("^----- BAD\n"); + } + } + printf("=====\n"); + } + fclose(fp); + } + closedir(d); + + fprintf(stdout, "OK: %d BAD: %d\n", ok, bad); +} diff --git a/tests/nfct/timeout/00tcp b/tests/nfct/timeout/00tcp new file mode 100644 index 0000000..c9d7d24 --- /dev/null +++ b/tests/nfct/timeout/00tcp @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet tcp established 100 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet tcp syn_sent 1 syn_recv 2 established 3 fin_wait 4 close_wait 5 last_ack 6 time_wait 7 close 8 syn_sent2 9 retrans 10 unacknowledged 11 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK diff --git a/tests/nfct/timeout/01udp b/tests/nfct/timeout/01udp new file mode 100644 index 0000000..952526c --- /dev/null +++ b/tests/nfct/timeout/01udp @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet udp unreplied 10 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet udp unreplied 1 replied 2 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK diff --git a/tests/nfct/timeout/02generic b/tests/nfct/timeout/02generic new file mode 100644 index 0000000..b6ca699 --- /dev/null +++ b/tests/nfct/timeout/02generic @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet generic timeout 10 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet generic timeout 1 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK diff --git a/tests/nfct/timeout/03udplite b/tests/nfct/timeout/03udplite new file mode 100644 index 0000000..69dda15 --- /dev/null +++ b/tests/nfct/timeout/03udplite @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet udplite unreplied 10 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet udplite unreplied 1 replied 2 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK diff --git a/tests/nfct/timeout/04icmp b/tests/nfct/timeout/04icmp new file mode 100644 index 0000000..606e8b9 --- /dev/null +++ b/tests/nfct/timeout/04icmp @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet icmp timeout 10 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet icmp timeout 1 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK diff --git a/tests/nfct/timeout/05icmpv6 b/tests/nfct/timeout/05icmpv6 new file mode 100644 index 0000000..16541f5 --- /dev/null +++ b/tests/nfct/timeout/05icmpv6 @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet6 icmpv6 timeout 10 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet6 icmpv6 timeout 1 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK diff --git a/tests/nfct/timeout/06sctp b/tests/nfct/timeout/06sctp new file mode 100644 index 0000000..f475215 --- /dev/null +++ b/tests/nfct/timeout/06sctp @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet sctp established 100 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet sctp closed 1 cookie_wait 2 cookie_echoed 3 established 4 shutdown_sent 5 shutdown_recd 6 shutdown_ack_sent 7 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK diff --git a/tests/nfct/timeout/07dccp b/tests/nfct/timeout/07dccp new file mode 100644 index 0000000..1bd4fa5 --- /dev/null +++ b/tests/nfct/timeout/07dccp @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet dccp request 100 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet dccp request 1 respond 2 partopen 3 open 4 closereq 5 closing 6 timewait 7 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK diff --git a/tests/nfct/timeout/08gre b/tests/nfct/timeout/08gre new file mode 100644 index 0000000..7ef4bdb --- /dev/null +++ b/tests/nfct/timeout/08gre @@ -0,0 +1,16 @@ +# add policy object `test' +nfct timeout add test inet gre unreplied 10 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK +# get unexistent policy object `dummy' +nfct timeout get test ; BAD +# delete policy object `test', however, it does not exists anymore +nfct timeout delete test ; BAD +# add policy object `test' +nfct timeout add test inet gre unreplied 1 replied 2 ; OK +# get policy object `test' +nfct timeout get test ; OK +# delete policy object `test' +nfct timeout delete test ; OK -- cgit v1.2.3 From 790d922771acf3a1dc7a2bd8cdbf27ac5ed83401 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 15:51:09 +0200 Subject: build: bump version to 1.2.0 Major milestone including the new `nfct' utility and the expectation support for conntrackd. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5c4ac58..18d73c9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(conntrack-tools, 1.0.1, pablo@netfilter.org) +AC_INIT(conntrack-tools, 1.2.0, pablo@netfilter.org) AC_CONFIG_AUX_DIR([build-aux]) AC_CANONICAL_HOST -- cgit v1.2.3 From 658ef8980f09e12ee8c4d9a52afdf94fd097b47b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 15:53:28 +0200 Subject: nfct: fix compilation warning in cttimeout support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CC nfct-extensions/timeout.o ../../src/nfct-extensions/timeout.c: In function ‘nfct_cmd_timeout_parse_params’: ../../src/nfct-extensions/timeout.c:40:27: warning: variable ‘ret’ set but not used [-Wunused-but-set-variable] Signed-off-by: Pablo Neira Ayuso --- src/nfct-extensions/timeout.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index a69537d..a1a5c52 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -37,7 +37,7 @@ nfct_cmd_timeout_usage(char *argv[]) int nfct_cmd_timeout_parse_params(int argc, char *argv[]) { - int cmd = NFCT_CMD_NONE, ret = 0; + int cmd = NFCT_CMD_NONE, ret; if (argc < 3) { nfct_cmd_timeout_usage(argv); @@ -77,7 +77,7 @@ int nfct_cmd_timeout_parse_params(int argc, char *argv[]) break; } - return 0; + return ret; } static int nfct_timeout_cb(const struct nlmsghdr *nlh, void *data) -- cgit v1.2.3 From 1975dc432a57a78880e28aadceb3d7bcf923fe8b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 17:39:30 +0200 Subject: build: update dependencies with libnetfilter_conntrack (>= 1.0.1) libnetfilter_conntrack 1.0.1 includes important updates for the expectation side, which is used in this major milestone release. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 18d73c9..580e078 100644 --- a/configure.ac +++ b/configure.ac @@ -53,7 +53,7 @@ fi PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.0]) PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.0]) -PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.0]) +PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.1]) PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) AC_CHECK_HEADERS([linux/capability.h],, [AC_MSG_ERROR([Cannot find linux/capabibility.h])]) -- cgit v1.2.3 From 0e1ce4f491e2134d6207f55c4a5f52e157a54707 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 17:43:49 +0200 Subject: move qa directory to tests/conntrack/ All automated testing for the conntrack-tools will now reside under the test directory. Signed-off-by: Pablo Neira Ayuso --- qa/test-conntrack.c | 94 -------------------------------------- qa/testsuite/00create | 20 -------- qa/testsuite/01delete | 6 --- qa/testsuite/02filter | 23 ---------- qa/testsuite/03nat | 40 ---------------- qa/testsuite/04zone | 8 ---- qa/testsuite/05mark | 27 ----------- qa/testsuite/06update | 8 ---- tests/conntrack/test-conntrack.c | 94 ++++++++++++++++++++++++++++++++++++++ tests/conntrack/testsuite/00create | 20 ++++++++ tests/conntrack/testsuite/01delete | 6 +++ tests/conntrack/testsuite/02filter | 23 ++++++++++ tests/conntrack/testsuite/03nat | 40 ++++++++++++++++ tests/conntrack/testsuite/04zone | 8 ++++ tests/conntrack/testsuite/05mark | 27 +++++++++++ tests/conntrack/testsuite/06update | 8 ++++ 16 files changed, 226 insertions(+), 226 deletions(-) delete mode 100644 qa/test-conntrack.c delete mode 100644 qa/testsuite/00create delete mode 100644 qa/testsuite/01delete delete mode 100644 qa/testsuite/02filter delete mode 100644 qa/testsuite/03nat delete mode 100644 qa/testsuite/04zone delete mode 100644 qa/testsuite/05mark delete mode 100644 qa/testsuite/06update create mode 100644 tests/conntrack/test-conntrack.c create mode 100644 tests/conntrack/testsuite/00create create mode 100644 tests/conntrack/testsuite/01delete create mode 100644 tests/conntrack/testsuite/02filter create mode 100644 tests/conntrack/testsuite/03nat create mode 100644 tests/conntrack/testsuite/04zone create mode 100644 tests/conntrack/testsuite/05mark create mode 100644 tests/conntrack/testsuite/06update diff --git a/qa/test-conntrack.c b/qa/test-conntrack.c deleted file mode 100644 index c9097b6..0000000 --- a/qa/test-conntrack.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Very simple test-tool for the command line tool `conntrack'. - * This code is released under GPLv2 or any later at your option. - * - * gcc test-conntrack.c -o test - * - * Do not forget that you need *root* or CAP_NET_ADMIN capabilities ;-) - * - * (c) 2008 Pablo Neira Ayuso - */ - -#include -#include -#include -#include -#include -#include -#include - -#define CT_PROG "/usr/sbin/conntrack" - -int main() -{ - int ret, ok = 0, bad = 0, line; - FILE *fp; - DIR *d; - char buf[1024]; - struct dirent *dent; - char file[1024]; - - d = opendir("testsuite"); - - while ((dent = readdir(d)) != NULL) { - - sprintf(file, "testsuite/%s", dent->d_name); - - line = 0; - - fp = fopen(file, "r"); - if (fp == NULL) { - perror("cannot find testsuite file"); - exit(EXIT_FAILURE); - } - - while (fgets(buf, sizeof(buf), fp)) { - char tmp[1024] = CT_PROG, *res; - tmp[strlen(CT_PROG)] = ' '; - - line++; - - if (buf[0] == '#' || buf[0] == ' ') - continue; - - res = strchr(buf, ';'); - if (!res) { - printf("malformed file %s at line %d\n", - dent->d_name, line); - exit(EXIT_FAILURE); - } - *res = '\0'; - res+=2; - - strcpy(tmp + strlen(CT_PROG) + 1, buf); - printf("(%d) Executing: %s\n", line, tmp); - - ret = system(tmp); - - if (WIFEXITED(ret) && - WEXITSTATUS(ret) == EXIT_SUCCESS) { - if (res[0] == 'O' && - res[1] == 'K') - ok++; - else { - bad++; - printf("^----- BAD\n"); - } - } else { - if (res[0] == 'B' && - res[1] == 'A' && - res[2] == 'D') - ok++; - else { - bad++; - printf("^----- BAD\n"); - } - } - printf("=====\n"); - } - fclose(fp); - } - closedir(d); - - fprintf(stdout, "OK: %d BAD: %d\n", ok, bad); -} diff --git a/qa/testsuite/00create b/qa/testsuite/00create deleted file mode 100644 index 40e2c19..0000000 --- a/qa/testsuite/00create +++ /dev/null @@ -1,20 +0,0 @@ -#missing destination --I -s 1.1.1.1 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD -#missing source --I -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD -#missing protocol --I -s 1.1.1.1 -d 2.2.2.2 --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD -#missing source port --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD -#missing timeout --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY ; BAD -# create a conntrack --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK -# create again --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD -# delete --D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK -# create from reply --I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK -# delete reverse --D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK diff --git a/qa/testsuite/01delete b/qa/testsuite/01delete deleted file mode 100644 index 3c38ac5..0000000 --- a/qa/testsuite/01delete +++ /dev/null @@ -1,6 +0,0 @@ -# create dummy --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK -# delete bad source --D -s 2.2.2.2 -p tcp --sport 10 --dport 20 ; BAD -# delete by source --D -s 1.1.1.1 ; OK diff --git a/qa/testsuite/02filter b/qa/testsuite/02filter deleted file mode 100644 index 204c4e8..0000000 --- a/qa/testsuite/02filter +++ /dev/null @@ -1,23 +0,0 @@ -# create dummy -conntrack -I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK -# filter by source -conntrack -L -s 1.1.1.1 ; OK -# filter by destination -conntrack -L -d 2.2.2.2 ; OK -# filter by protocol -conntrack -L -p tcp ; OK -# filter by status -conntrack -L -u SEEN_REPLY ; OK -# filter by TCP protocol state -conntrack -L -p tcp --state LISTEN ; OK -# update mark of dummy conntrack -conntrack -U -s 1.1.1.1 -m 1 ; OK -# filter by mark -conntrack -L -m 1 ; OK -# filter by layer 3 protocol -conntrack -L -f ipv4 ; OK -# filter by mark -conntrack -L --mark 0 ; OK -conntrack -L --mark 0/0xffffffff; OK -# delete dummy -conntrack -D -d 2.2.2.2 ; OK diff --git a/qa/testsuite/03nat b/qa/testsuite/03nat deleted file mode 100644 index f94e8ff..0000000 --- a/qa/testsuite/03nat +++ /dev/null @@ -1,40 +0,0 @@ -# create dummy --I -s 1.1.1.1 -d 2.2.2.2 --dst-nat 3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK -# show --L --dst-nat ; OK -# show --L --dst-nat 3.3.3.3 ; OK -# show --L --src-nat ; OK -# delete --D -s 1.1.1.1 ; OK -# create dummy again --I -s 1.1.1.1 -d 2.2.2.2 --src-nat 3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK -# show --L --src-nat ; OK -# show --L --src-nat 3.3.3.3 ; OK -# show --L --dst-nat ; OK -# show any-nat --L --any-nat ; OK -# delete --D -s 1.1.1.1 ; OK -# bad combination --L --dst-nat --any-nat ; BAD -# bad combination --L --src-nat --any-nat ; BAD -# bad combination --L --src-nat --dst-nat --any-nat ; BAD -# create --I -s 1.1.1.1 -d 2.2.2.2 --dst-nat 3.3.3.3:80 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK -# show --L --dst-nat 3.3.3.3:80 ; OK -# show --L --any-nat 3.3.3.3:80 ; OK -# show --L --dst-nat 3.3.3.3:81 ; OK -# show --L --dst-nat 1.1.1.1:80 ; OK -# delete --D -s 1.1.1.1 ; OK diff --git a/qa/testsuite/04zone b/qa/testsuite/04zone deleted file mode 100644 index 4ff3d34..0000000 --- a/qa/testsuite/04zone +++ /dev/null @@ -1,8 +0,0 @@ -# create dummy --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --zone 1; OK -# display dummy --L --zone 1; OK -# display dummy --L --zone 0; OK -# delete dummy --D --zone 1; OK diff --git a/qa/testsuite/05mark b/qa/testsuite/05mark deleted file mode 100644 index 4d99dea..0000000 --- a/qa/testsuite/05mark +++ /dev/null @@ -1,27 +0,0 @@ -# create with a mark --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 42 ; OK -# find it again using mark --L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; OK --L --mark 42; OK -# ct already exists --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 42/0xffffffff ; BAD -# delete by mark --D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0xffffffff ; OK -# try again after del --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 417889/0xffffffff ; OK -# delete by mark --D --mark 417889 ; OK --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 0xffffffff ; OK -# zap top 16. --U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 0/0xffff0000 ; OK --L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 0x0000ffff ; OK --U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0xffff ; OK --L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0x0000ffff ; OK --L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/42 ; OK --L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/2 ; OK --L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/3 ; OK -# OK, but no flow entries should be shown here: --L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/0xf ; OK -# BAD, because no updates done (mark is already 42). --U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; BAD --D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; OK diff --git a/qa/testsuite/06update b/qa/testsuite/06update deleted file mode 100644 index 0408303..0000000 --- a/qa/testsuite/06update +++ /dev/null @@ -1,8 +0,0 @@ -# create dummy flow --I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state SYN_RECV -u SEEN_REPLY,ASSURED -t 50 ; OK -# find it again using mark --L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK -# set fixed timeout --U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 -u FIXED_TIMEOUT; OK -# delete it --D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20; OK diff --git a/tests/conntrack/test-conntrack.c b/tests/conntrack/test-conntrack.c new file mode 100644 index 0000000..c9097b6 --- /dev/null +++ b/tests/conntrack/test-conntrack.c @@ -0,0 +1,94 @@ +/* + * Very simple test-tool for the command line tool `conntrack'. + * This code is released under GPLv2 or any later at your option. + * + * gcc test-conntrack.c -o test + * + * Do not forget that you need *root* or CAP_NET_ADMIN capabilities ;-) + * + * (c) 2008 Pablo Neira Ayuso + */ + +#include +#include +#include +#include +#include +#include +#include + +#define CT_PROG "/usr/sbin/conntrack" + +int main() +{ + int ret, ok = 0, bad = 0, line; + FILE *fp; + DIR *d; + char buf[1024]; + struct dirent *dent; + char file[1024]; + + d = opendir("testsuite"); + + while ((dent = readdir(d)) != NULL) { + + sprintf(file, "testsuite/%s", dent->d_name); + + line = 0; + + fp = fopen(file, "r"); + if (fp == NULL) { + perror("cannot find testsuite file"); + exit(EXIT_FAILURE); + } + + while (fgets(buf, sizeof(buf), fp)) { + char tmp[1024] = CT_PROG, *res; + tmp[strlen(CT_PROG)] = ' '; + + line++; + + if (buf[0] == '#' || buf[0] == ' ') + continue; + + res = strchr(buf, ';'); + if (!res) { + printf("malformed file %s at line %d\n", + dent->d_name, line); + exit(EXIT_FAILURE); + } + *res = '\0'; + res+=2; + + strcpy(tmp + strlen(CT_PROG) + 1, buf); + printf("(%d) Executing: %s\n", line, tmp); + + ret = system(tmp); + + if (WIFEXITED(ret) && + WEXITSTATUS(ret) == EXIT_SUCCESS) { + if (res[0] == 'O' && + res[1] == 'K') + ok++; + else { + bad++; + printf("^----- BAD\n"); + } + } else { + if (res[0] == 'B' && + res[1] == 'A' && + res[2] == 'D') + ok++; + else { + bad++; + printf("^----- BAD\n"); + } + } + printf("=====\n"); + } + fclose(fp); + } + closedir(d); + + fprintf(stdout, "OK: %d BAD: %d\n", ok, bad); +} diff --git a/tests/conntrack/testsuite/00create b/tests/conntrack/testsuite/00create new file mode 100644 index 0000000..40e2c19 --- /dev/null +++ b/tests/conntrack/testsuite/00create @@ -0,0 +1,20 @@ +#missing destination +-I -s 1.1.1.1 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD +#missing source +-I -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD +#missing protocol +-I -s 1.1.1.1 -d 2.2.2.2 --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD +#missing source port +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD +#missing timeout +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY ; BAD +# create a conntrack +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK +# create again +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; BAD +# delete +-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK +# create from reply +-I -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 --state LISTEN -u SEEN_REPLY -t 50 ; OK +# delete reverse +-D -r 2.2.2.2 -q 1.1.1.1 -p tcp --reply-port-src 11 --reply-port-dst 21 ; OK diff --git a/tests/conntrack/testsuite/01delete b/tests/conntrack/testsuite/01delete new file mode 100644 index 0000000..3c38ac5 --- /dev/null +++ b/tests/conntrack/testsuite/01delete @@ -0,0 +1,6 @@ +# create dummy +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK +# delete bad source +-D -s 2.2.2.2 -p tcp --sport 10 --dport 20 ; BAD +# delete by source +-D -s 1.1.1.1 ; OK diff --git a/tests/conntrack/testsuite/02filter b/tests/conntrack/testsuite/02filter new file mode 100644 index 0000000..204c4e8 --- /dev/null +++ b/tests/conntrack/testsuite/02filter @@ -0,0 +1,23 @@ +# create dummy +conntrack -I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK +# filter by source +conntrack -L -s 1.1.1.1 ; OK +# filter by destination +conntrack -L -d 2.2.2.2 ; OK +# filter by protocol +conntrack -L -p tcp ; OK +# filter by status +conntrack -L -u SEEN_REPLY ; OK +# filter by TCP protocol state +conntrack -L -p tcp --state LISTEN ; OK +# update mark of dummy conntrack +conntrack -U -s 1.1.1.1 -m 1 ; OK +# filter by mark +conntrack -L -m 1 ; OK +# filter by layer 3 protocol +conntrack -L -f ipv4 ; OK +# filter by mark +conntrack -L --mark 0 ; OK +conntrack -L --mark 0/0xffffffff; OK +# delete dummy +conntrack -D -d 2.2.2.2 ; OK diff --git a/tests/conntrack/testsuite/03nat b/tests/conntrack/testsuite/03nat new file mode 100644 index 0000000..f94e8ff --- /dev/null +++ b/tests/conntrack/testsuite/03nat @@ -0,0 +1,40 @@ +# create dummy +-I -s 1.1.1.1 -d 2.2.2.2 --dst-nat 3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK +# show +-L --dst-nat ; OK +# show +-L --dst-nat 3.3.3.3 ; OK +# show +-L --src-nat ; OK +# delete +-D -s 1.1.1.1 ; OK +# create dummy again +-I -s 1.1.1.1 -d 2.2.2.2 --src-nat 3.3.3.3 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK +# show +-L --src-nat ; OK +# show +-L --src-nat 3.3.3.3 ; OK +# show +-L --dst-nat ; OK +# show any-nat +-L --any-nat ; OK +# delete +-D -s 1.1.1.1 ; OK +# bad combination +-L --dst-nat --any-nat ; BAD +# bad combination +-L --src-nat --any-nat ; BAD +# bad combination +-L --src-nat --dst-nat --any-nat ; BAD +# create +-I -s 1.1.1.1 -d 2.2.2.2 --dst-nat 3.3.3.3:80 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 ; OK +# show +-L --dst-nat 3.3.3.3:80 ; OK +# show +-L --any-nat 3.3.3.3:80 ; OK +# show +-L --dst-nat 3.3.3.3:81 ; OK +# show +-L --dst-nat 1.1.1.1:80 ; OK +# delete +-D -s 1.1.1.1 ; OK diff --git a/tests/conntrack/testsuite/04zone b/tests/conntrack/testsuite/04zone new file mode 100644 index 0000000..4ff3d34 --- /dev/null +++ b/tests/conntrack/testsuite/04zone @@ -0,0 +1,8 @@ +# create dummy +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --zone 1; OK +# display dummy +-L --zone 1; OK +# display dummy +-L --zone 0; OK +# delete dummy +-D --zone 1; OK diff --git a/tests/conntrack/testsuite/05mark b/tests/conntrack/testsuite/05mark new file mode 100644 index 0000000..4d99dea --- /dev/null +++ b/tests/conntrack/testsuite/05mark @@ -0,0 +1,27 @@ +# create with a mark +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 42 ; OK +# find it again using mark +-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; OK +-L --mark 42; OK +# ct already exists +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 42/0xffffffff ; BAD +# delete by mark +-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0xffffffff ; OK +# try again after del +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 417889/0xffffffff ; OK +# delete by mark +-D --mark 417889 ; OK +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --mark 0xffffffff ; OK +# zap top 16. +-U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 0/0xffff0000 ; OK +-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 0x0000ffff ; OK +-U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0xffff ; OK +-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/0x0000ffff ; OK +-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42/42 ; OK +-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/2 ; OK +-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/3 ; OK +# OK, but no flow entries should be shown here: +-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 2/0xf ; OK +# BAD, because no updates done (mark is already 42). +-U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; BAD +-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --mark 42 ; OK diff --git a/tests/conntrack/testsuite/06update b/tests/conntrack/testsuite/06update new file mode 100644 index 0000000..0408303 --- /dev/null +++ b/tests/conntrack/testsuite/06update @@ -0,0 +1,8 @@ +# create dummy flow +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state SYN_RECV -u SEEN_REPLY,ASSURED -t 50 ; OK +# find it again using mark +-L -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 ; OK +# set fixed timeout +-U -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 -u FIXED_TIMEOUT; OK +# delete it +-D -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20; OK -- cgit v1.2.3 From 5e4ce59027bf7170c865388d3d703086f187ce59 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 17:47:15 +0200 Subject: tests: conntrack: add run-test.sh script For automated testing of the conntrack utility. Signed-off-by: Pablo Neira Ayuso --- tests/conntrack/run-test.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/conntrack/run-test.sh diff --git a/tests/conntrack/run-test.sh b/tests/conntrack/run-test.sh new file mode 100644 index 0000000..2b7b6f2 --- /dev/null +++ b/tests/conntrack/run-test.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +UID=`id -u` +if [ $UID -ne 0 ] +then + echo "Run this test as root" + exit 1 +fi + +gcc test-conntrack.c -o test +# +# XXX: module auto-load not support by nfnetlink_cttimeout yet :-( +# +modprobe nf_conntrack_ipv4 +modprobe nf_conntrack_ipv6 +modprobe nf_conntrack_proto_udplite +modprobe nf_conntrack_proto_sctp +modprobe nf_conntrack_proto_dccp +modprobe nf_conntrack_proto_gre +./test testcases -- cgit v1.2.3 From 7276986d8a2d539fde3835e00e217f52f6e595ac Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 18:02:12 +0200 Subject: add nfct(8) manpage Signed-off-by: Pablo Neira Ayuso --- Makefile.am | 2 +- nfct.8 | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 nfct.8 diff --git a/Makefile.am b/Makefile.am index afb4595..bd366bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ include Make_global.am ACLOCAL_AMFLAGS = -I m4 -man_MANS = conntrack.8 conntrackd.8 +man_MANS = conntrack.8 conntrackd.8 nfct.8 EXTRA_DIST = $(man_MANS) Make_global.am doc m4 SUBDIRS = extensions src diff --git a/nfct.8 b/nfct.8 new file mode 100644 index 0000000..6f5190a --- /dev/null +++ b/nfct.8 @@ -0,0 +1,64 @@ +.TH NFCT 8 "Feb 29, 2012" "" "" + +.\" Man page written by Pablo Neira Ayuso (Feb 2012) + +.SH NAME +nfct \- command line tool to interact with the connection tracking system +.SH SYNOPSIS +.BR "nfct subsystem command [parameters]" +.SH DESCRIPTION +.B nfct +is the command line tool that allows you Netfilter's manipulate Connection Tracking System. +.SH SUBSYS +By the time this manpage has been written, the supported subsystem are +.B timeout +.TP +.BI "timeout " +The timeout subsystem allows you to define fine-grain timeout policies. +.TP +.BI "version " +Displays the version information. +.TP +.BI "help " +Displays the help message. +.SH TIMEOUT SUBSYSTEM +.TP +.BI "list " +List the existing timeout policies. +.TP +.BI "add " +Add new timeout policy. +.TP +.BI "delete " +Delete timeout policy. +.TP +.BI "get " +Get existing timeout policy. +.SH EXAMPLE +.TP +.B nfct timeout add test-tcp inet tcp established 100 close 10 close_wait 10 +.TP +This creates a timeout policy for tcp using 100 seconds for the ESTABLISHED state, 10 seconds for CLOSE state and 10 seconds for the CLOSE_WAIT state. +.TP +Then, you can attach the timeout policy with the iptables CT target: +.TP +.B iptables -I PREROUTING -t raw -p tcp -j CT --timeout test-tcp +.TP +.B iptables -I OUTPUT -t raw -p tcp -j CT --timeout test-tcp +.TP +You can test that the timeout policy with: +.TP +.B conntrack -E -p tcp +.TP +It should display: +.TP +.B [UPDATE] tcp 6 100 ESTABLISHED src=192.168.39.100 dst=57.126.1.20 sport=56463 dport=80 src=57.126.1.20 dst=192.168.39.100 sport=80 dport=56463 [ASSURED] +.SH SEE ALSO +.BR iptables (8), conntrack (8) +.SH BUGS +Please, report them to netfilter-devel@vger.kernel.org or file a bug in +Netfilter's bugzilla (https://bugzilla.netfilter.org). +.SH AUTHORS +Pablo Neira Ayuso wrote and maintains the nfct tool. +.PP +Man page written by Pablo Neira Ayuso . -- cgit v1.2.3 From 318756cd4df1cb7760bf32d3e1d3756c41d1858b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 18:03:32 +0200 Subject: add README.nfct This files includes some short description on `nfct'. Signed-off-by: Pablo Neira Ayuso --- README.nfct | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 README.nfct diff --git a/README.nfct b/README.nfct new file mode 100644 index 0000000..4d8e6cc --- /dev/null +++ b/README.nfct @@ -0,0 +1,62 @@ += nfct: command line tool to interact with the Connection Tracking System = + +This tool only supports the cttimeout infrastructure by now. However, +the plan is that it will replace `conntrack' with a syntax that looks +more similar to `ip' and `nftables' tools (in the long run!). + +== cttimeout: fine-grain timeout tuning for the Connection Tracking System == + +The `nfct' command line tool allows you to define custom timeout +policies: + +# nfct timeout add custom-tcp-policy1 inet tcp established 100 + +You can also retrieve the existing timeout policies with: + +# nfct timeout list +.tcp-policy = { + .l3proto = 2, + .l4proto = 6, + .policy = { + .SYN_SENT = 120, + .SYN_RECV = 60, + .ESTABLISHED = 100, + .FIN_WAIT = 120, + .CLOSE_WAIT = 60, + .LAST_ACK = 30, + .TIME_WAIT = 120, + .CLOSE = 10, + .SYN_SENT2 = 120, + .RETRANS = 300, + .UNACKNOWLEDGED = 300, + }, +}; + +Then, you can use the timeout policy with iptables: + +# iptables -I PREROUTING -t raw -s 1.1.1.1 -d 2.2.2.2 -p tcp \ + -j CT --timeout custom-tcp-policy1 + +You can define policies for other protocols as well, eg: + +# nfct timeout add custom-udp-policy1 inet udp unreplied 10 replied 20 + +And attach them via iptables: + +# iptables -I PREROUTING -t raw -s 1.1.1.1 -d 2.2.2.2 -p udp \ + -j CT --timeout custom-udp-policy1 + +== Compilation & Installation == + +This tool requires libmnl and libnetfilter_cttimeout. You also require +nfnetlink_cttimeout support in the Linux kernel. + +If you obtain a working copy from git, you have to run: + +$ autoreconf -fi # this is the lingo that replaces old autogen.sh scripts +$ ./configure --prefix=/usr +$ make +$ sudo make install + +-o- +(c) 2012 by Pablo Neira Ayuso -- cgit v1.2.3 From 18efbc802479ce042378dc0cccc7577e1c6afa41 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 20:58:16 +0200 Subject: nfct: fix compilation of timeout extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nfct-extensions/timeout.c: In function ‘nfct_timeout_cb’: nfct-extensions/timeout.c:99:2: warning: passing argument 4 of ‘nfct_timeout_snprintf’ makes integer from pointer without a cast [enabled by default] /usr/include/libnetfilter_cttimeout/libnetfilter_cttimeout.h:114:5: note: expected ‘unsigned int’ but argument is of type ‘struct nfct_timeout *’ Signed-off-by: Pablo Neira Ayuso --- src/nfct-extensions/timeout.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index a1a5c52..5b32023 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -96,7 +96,7 @@ static int nfct_timeout_cb(const struct nlmsghdr *nlh, void *data) goto err_free; } - nfct_timeout_snprintf(buf, sizeof(buf), t, 0); + nfct_timeout_snprintf(buf, sizeof(buf), t, NFCT_TIMEOUT_O_DEFAULT, 0); printf("%s\n", buf); err_free: -- cgit v1.2.3 From 202a92db668aa4e9847d99ec5e0d5b3b0224c340 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 26 May 2012 21:05:18 +0200 Subject: bump version to 1.2.1 this release fixes a compilation issue in 1.2.0, sorry. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 580e078..07a1fa1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(conntrack-tools, 1.2.0, pablo@netfilter.org) +AC_INIT(conntrack-tools, 1.2.1, pablo@netfilter.org) AC_CONFIG_AUX_DIR([build-aux]) AC_CANONICAL_HOST -- cgit v1.2.3 From c5e0c17653274e9cc21f677e6fe3bd822a569a00 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 26 May 2012 22:18:35 +0200 Subject: update .gitignore --- .gitignore | 1 + src/.gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 928e44b..f7a5fc7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .deps/ .libs/ +.dirstamp Makefile Makefile.in *.o diff --git a/src/.gitignore b/src/.gitignore index 6e6763d..55a0d27 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,5 +1,6 @@ /conntrack /conntrackd +/nfct /read_config_lex.c /read_config_yy.c -- cgit v1.2.3 From fcd6f78d277113628205789c8aba9ab1f5152fc4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 May 2012 12:28:40 +0200 Subject: conntrackd: simplify TCP connection handling logic Before this patch, we called accept() to likely return EAGAIN. This is not required as select() will tell us that we're ready to accept. Therefore, that early accept() invocation complicates the whole handling just to get the connection accepted a bit before. Signed-off-by: Pablo Neira Ayuso --- src/tcp.c | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/src/tcp.c b/src/tcp.c index c551c54..f6b05ef 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -27,7 +27,7 @@ struct tcp_sock *tcp_server_create(struct tcp_conf *c) { - int yes = 1, ret; + int yes = 1; struct tcp_sock *m; socklen_t socklen = sizeof(int); @@ -109,30 +109,7 @@ struct tcp_sock *tcp_server_create(struct tcp_conf *c) return NULL; } - /* now we accept new connections ... */ - ret = accept(m->fd, NULL, NULL); - if (ret == -1) { - if (errno != EAGAIN) { - /* unexpected error, give up. */ - close(m->fd); - free(m); - m = NULL; - } else { - /* still in progress ... we'll do it in tcp_recv() */ - m->state = TCP_SERVER_ACCEPTING; - } - } else { - /* very unlikely at this stage. */ - if (fcntl(ret, F_SETFL, O_NONBLOCK) == -1) { - /* unexpected error, give up. */ - close(m->fd); - free(m); - return NULL; - } - m->client_fd = ret; - m->state = TCP_SERVER_CONNECTED; - register_fd(m->client_fd, STATE(fds)); - } + m->state = TCP_SERVER_ACCEPTING; return m; } @@ -367,7 +344,6 @@ ssize_t tcp_recv(struct tcp_sock *m, void *data, int size) close(m->client_fd); m->client_fd = -1; m->state = TCP_SERVER_ACCEPTING; - tcp_accept(m); } else if (errno != EAGAIN) { m->stats.error++; } @@ -377,7 +353,6 @@ ssize_t tcp_recv(struct tcp_sock *m, void *data, int size) close(m->client_fd); m->client_fd = -1; m->state = TCP_SERVER_ACCEPTING; - tcp_accept(m); } if (ret >= 0) { -- cgit v1.2.3 From d44489648c1a56d543a84bbebe455227bb25cf34 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 28 May 2012 14:06:48 +0200 Subject: conntrackd: fix compilation in src/parse.c Making all in src make[1]: Entering directory `/home/oden/RPM/BUILD/conntrack-tools-1.2.0/src' CC parse.o parse.c: In function 'msg2ct': parse.c:258:34: error: 'NULL' undeclared (first use in this function) parse.c:258:34: note: each undeclared identifier is reported only once for each function it appears in parse.c: In function 'msg2exp': parse.c:438:16: error: 'NULL' undeclared (first use in this function) Reported-by: Oden Eriksson Signed-off-by: Pablo Neira Ayuso --- src/parse.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parse.c b/src/parse.c index 732bc44..1b83f81 100644 --- a/src/parse.c +++ b/src/parse.c @@ -19,6 +19,7 @@ #include "network.h" +#include #include #ifndef ssizeof -- cgit v1.2.3 From c88266b35ba130e804422ce2fe0da6704d620bd6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 16 Jun 2012 17:42:28 +0200 Subject: doc: fix documentation on ExpectationSync and H.323 helper The H.323 helper is actually composed of three helpers: ras q.931 h.245 We have to specify those in the configuration file since h.323 is not any known helper itself. Signed-off-by: Pablo Neira Ayuso --- doc/manual/conntrack-tools.tmpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index dbf836d..47e6f84 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -689,7 +689,9 @@ Sync { ExpectationSync { ftp sip - h323 + ras # for H.323 + q.931 # for H.323 + h.245 # for H.323 } } } -- cgit v1.2.3 From 8648ae6d08bb84030c2c3519454532f6e04e31d9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 5 Jul 2012 00:17:48 +0200 Subject: conntrackd: add bugtrap notice in case of flush while commit in progress Flushing the external cache, ie. conntrackd -f, while commit is in progress is not allowed anymore, ie. conntrackd -c. Note that conntrackd -c is synchronous. Thus, it returns control to the caller once the commit has finished. Signed-off-by: Pablo Neira Ayuso --- src/sync-mode.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/sync-mode.c b/src/sync-mode.c index 10fdb9e..6f8eb04 100644 --- a/src/sync-mode.c +++ b/src/sync-mode.c @@ -608,6 +608,12 @@ static int local_handler_sync(int fd, int type, void *data) } break; case CT_FLUSH_CACHE: + /* if we're still committing, abort this command */ + if (STATE_SYNC(commit).clientfd != -1) { + dlog(LOG_ERR, "ignoring flush command, " + "commit still in progress"); + break; + } /* inmediate flush, remove pending flush scheduled if any */ del_alarm(&STATE_SYNC(reset_cache_alarm)); dlog(LOG_NOTICE, "flushing caches"); @@ -621,6 +627,12 @@ static int local_handler_sync(int fd, int type, void *data) STATE(mode)->internal->ct.flush(); break; case CT_FLUSH_EXT_CACHE: + /* if we're still committing, abort this command */ + if (STATE_SYNC(commit).clientfd != -1) { + dlog(LOG_ERR, "ignoring flush command, " + "commit still in progress"); + break; + } dlog(LOG_NOTICE, "flushing external cache"); STATE_SYNC(external)->ct.flush(); break; @@ -687,6 +699,12 @@ static int local_handler_sync(int fd, int type, void *data) local_commit(fd); break; case ALL_FLUSH_CACHE: + /* if we're still committing, abort this command */ + if (STATE_SYNC(commit).clientfd != -1) { + dlog(LOG_ERR, "ignoring flush command, " + "commit still in progress"); + break; + } dlog(LOG_NOTICE, "flushing caches"); STATE(mode)->internal->ct.flush(); STATE_SYNC(external)->ct.flush(); -- cgit v1.2.3 From 7eb63b5872f07903d952aa5cfd6ad0e7647a066a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 5 Jul 2012 00:42:38 +0200 Subject: conntrackd: fix commit operation, needs to be synchronous While adding the expectation support for conntrackd, I accidentally broke synchrony in 'conntrackd -c' command. Basically, conntrackd -c should not return control to the shell until the cache has been committed. Signed-off-by: Pablo Neira Ayuso --- src/sync-mode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sync-mode.c b/src/sync-mode.c index 6f8eb04..7fb3eba 100644 --- a/src/sync-mode.c +++ b/src/sync-mode.c @@ -696,7 +696,7 @@ static int local_handler_sync(int fd, int type, void *data) dlog(LOG_NOTICE, "committing expectation cache"); STATE_SYNC(commit).rq[0].cb = STATE_SYNC(external)->exp.commit; STATE_SYNC(commit).rq[1].cb = NULL; - local_commit(fd); + ret = local_commit(fd); break; case ALL_FLUSH_CACHE: /* if we're still committing, abort this command */ @@ -722,7 +722,7 @@ static int local_handler_sync(int fd, int type, void *data) } else { STATE_SYNC(commit).rq[1].cb = NULL; } - local_commit(fd); + ret = local_commit(fd); break; case EXP_DUMP_INT_XML: if (fork_process_new(CTD_PROC_ANY, 0, NULL, NULL) == 0) { -- cgit v1.2.3 From 3cd88a6d9c66360dad983578259ab92ba083fca8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 30 Jul 2012 02:22:58 +0200 Subject: conntrackd: implement selective flushing for `-t' and `-F' commands This patch changes the current behaviour of `-t' and `-F' commands, that results in flushing the kernel conntrack table. With this patch, the entries that match the Filter clauses in conntrackd.conf are ignored. This fixes the situation in which some local ssh connection to the firewall is lost during the failover (since `-t' is invoked from the primary-backup.sh script). Note that the Filter clause tells what entries have to be ignored, ie. the entries that do not need to be replicated. It makes sense not to flush entries that are not replicated (usually traffic to the local firewall). Reported-by: Gaurav Sinha Signed-off-by: Pablo Neira Ayuso --- include/netlink.h | 2 +- src/internal_bypass.c | 2 +- src/netlink.c | 37 +++++++++++++++++++++++++++++++++++-- src/run.c | 2 +- src/sync-mode.c | 2 +- 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/include/netlink.h b/include/netlink.h index 3bde30c..9a33083 100644 --- a/include/netlink.h +++ b/include/netlink.h @@ -12,7 +12,7 @@ struct nlif_handle *nl_init_interface_handler(void); int nl_send_resync(struct nfct_handle *h); void nl_resize_socket_buffer(struct nfct_handle *h); int nl_dump_conntrack_table(struct nfct_handle *h); -int nl_flush_conntrack_table(struct nfct_handle *h); +int nl_flush_conntrack_table_selective(void); int nl_get_conntrack(struct nfct_handle *h, const struct nf_conntrack *ct); int nl_create_conntrack(struct nfct_handle *h, const struct nf_conntrack *ct, int timeout); int nl_update_conntrack(struct nfct_handle *h, const struct nf_conntrack *ct, int timeout); diff --git a/src/internal_bypass.c b/src/internal_bypass.c index 5c83c21..1194339 100644 --- a/src/internal_bypass.c +++ b/src/internal_bypass.c @@ -67,7 +67,7 @@ static void internal_bypass_ct_dump(int fd, int type) static void internal_bypass_ct_flush(void) { - nl_flush_conntrack_table(STATE(flush)); + nl_flush_conntrack_table_selective(); } struct { diff --git a/src/netlink.c b/src/netlink.c index fe979e3..bd38d99 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -151,9 +151,42 @@ int nl_dump_conntrack_table(struct nfct_handle *h) return nfct_query(h, NFCT_Q_DUMP, &CONFIG(family)); } -int nl_flush_conntrack_table(struct nfct_handle *h) +static int +nl_flush_selective_cb(enum nf_conntrack_msg_type type, + struct nf_conntrack *ct, void *data) { - return nfct_query(h, NFCT_Q_FLUSH, &CONFIG(family)); + /* don't delete this conntrack, it's in the ignore filter */ + if (ct_filter_conntrack(ct, 1)) + return NFCT_CB_CONTINUE; + + switch(type) { + case NFCT_T_UPDATE: + nl_destroy_conntrack(STATE(flush), ct); + break; + default: + STATE(stats).nl_dump_unknown_type++; + break; + } + return NFCT_CB_CONTINUE; +} + +int nl_flush_conntrack_table_selective(void) +{ + struct nfct_handle *h; + int ret; + + h = nfct_open(CONNTRACK, 0); + if (h == NULL) { + dlog(LOG_ERR, "cannot open handle"); + return -1; + } + nfct_callback_register(h, NFCT_T_ALL, nl_flush_selective_cb, NULL); + + ret = nfct_query(h, NFCT_Q_DUMP, &CONFIG(family)); + + nfct_close(h); + + return ret; } int nl_send_resync(struct nfct_handle *h) diff --git a/src/run.c b/src/run.c index 26c1783..421ff41 100644 --- a/src/run.c +++ b/src/run.c @@ -196,7 +196,7 @@ static void local_flush_master(void) * meanwhile the parent process handles events. */ if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL, NULL, NULL) == 0) { - nl_flush_conntrack_table(STATE(flush)); + nl_flush_conntrack_table_selective(); exit(EXIT_SUCCESS); } } diff --git a/src/sync-mode.c b/src/sync-mode.c index 7fb3eba..17f866a 100644 --- a/src/sync-mode.c +++ b/src/sync-mode.c @@ -304,7 +304,7 @@ static void do_reset_cache_alarm(struct alarm_block *a, void *data) * meanwhile the parent process handles events. */ if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL, NULL, NULL) == 0) { - nl_flush_conntrack_table(STATE(flush)); + nl_flush_conntrack_table_selective(); exit(EXIT_SUCCESS); } /* this is not required if events don't get lost */ -- cgit v1.2.3 From bbfd467280d15c9bcd56da43bf49049ab6334e64 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 31 Jul 2012 13:11:07 +0200 Subject: bump version to 1.2.2 Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 07a1fa1..8cd5626 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(conntrack-tools, 1.2.1, pablo@netfilter.org) +AC_INIT(conntrack-tools, 1.2.2, pablo@netfilter.org) AC_CONFIG_AUX_DIR([build-aux]) AC_CANONICAL_HOST -- cgit v1.2.3 From 9aecd75541aab51f5add2884df5105bda87fc05a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 27 Jun 2012 16:25:05 +0200 Subject: conntrack: add support for stats dumping via ctnetlink Since Linux kernel >= 3.6.x, we can dump the conntrack statistics via ctnetlink instead of using the /proc interface: conntrack -S cpu=0 searched=9177 found=387086 new=250451 invalid=1 ignore=4 delete=254093 delete_list=5467 insert=1825 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0 cpu=1 searched=390 found=37493 new=1531 invalid=0 ignore=0 delete=345 delete_list=345 insert=1531 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0 cpu=2 searched=333 found=68061 new=1895 invalid=0 ignore=1 delete=607 delete_list=607 insert=1896 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0 cpu=3 searched=71 found=13364 new=1254 invalid=0 ignore=0 delete=75 delete_list=75 insert=1254 insert_failed=0 drop=0 early_drop=0 error=0 search_restart=0 conntrack -S exp cpu=0 expect_new=9177 expect_create=387284 expect_delete=251141 cpu=1 expect_new=390 expect_create=37496 expect_delete=1531 cpu=2 expect_new=333 expect_create=68117 expect_delete=1895 cpu=3 expect_new=71 expect_create=13366 expect_delete=1255 Note that the output is not backward-compatible, but we fail back to previous output in case that ctnetlink stats dumping is not available. Signed-off-by: Pablo Neira Ayuso --- include/conntrack.h | 2 +- src/Makefile.am | 2 +- src/conntrack.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 215 insertions(+), 9 deletions(-) diff --git a/include/conntrack.h b/include/conntrack.h index 3882de7..fd6126b 100644 --- a/include/conntrack.h +++ b/include/conntrack.h @@ -9,7 +9,7 @@ #include -#define NUMBER_OF_CMD 18 +#define NUMBER_OF_CMD 19 #define NUMBER_OF_OPT 24 struct ctproto_handler { diff --git a/src/Makefile.am b/src/Makefile.am index 5dbdef3..93c035c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -7,7 +7,7 @@ CLEANFILES = read_config_yy.c read_config_lex.c 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} +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} nfct_SOURCES = nfct.c \ nfct-extensions/timeout.c diff --git a/src/conntrack.c b/src/conntrack.c index 0920bc5..6d0f301 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -34,6 +34,8 @@ * Ported to the new libnetfilter_conntrack API * 2008-04-13 Pablo Neira Ayuso : * Way more flexible update and delete operations + * + * Part of this code has been funded by Sophos Astaro */ #include "conntrack.h" @@ -57,6 +59,7 @@ #include #include #include +#include #include struct u32_mask { @@ -157,8 +160,11 @@ enum ct_command { EXP_COUNT_BIT = 16, EXP_COUNT = (1 << EXP_COUNT_BIT), - X_STATS_BIT = 17, - X_STATS = (1 << X_STATS_BIT), + CT_STATS_BIT = 17, + CT_STATS = (1 << CT_STATS_BIT), + + EXP_STATS_BIT = 18, + EXP_STATS = (1 << EXP_STATS_BIT), }; /* If you add a new command, you have to update NUMBER_OF_CMD in conntrack.h */ @@ -352,7 +358,8 @@ static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = /*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}, /*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}, /*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}, -/*X_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}, +/*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}, +/*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}, }; static const int cmd2type[][2] = { @@ -365,6 +372,7 @@ static const int cmd2type[][2] = { ['V'] = { CT_VERSION, CT_VERSION }, ['h'] = { CT_HELP, CT_HELP }, ['C'] = { CT_COUNT, EXP_COUNT }, + ['S'] = { CT_STATS, EXP_STATS }, }; static const int opt2type[] = { @@ -1462,6 +1470,171 @@ out_err: return ret; } +static struct nfct_mnl_socket { + struct mnl_socket *mnl; + uint32_t portid; +} sock; + +static int nfct_mnl_socket_open(void) +{ + sock.mnl = mnl_socket_open(NETLINK_NETFILTER); + if (sock.mnl == NULL) { + perror("mnl_socket_open"); + return -1; + } + if (mnl_socket_bind(sock.mnl, 0, MNL_SOCKET_AUTOPID) < 0) { + perror("mnl_socket_bind"); + return -1; + } + sock.portid = mnl_socket_get_portid(sock.mnl); + + return 0; +} + +static struct nlmsghdr * +nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (subsys << 8) | type; + nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; + nlh->nlmsg_seq = time(NULL); + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); + nfh->nfgen_family = AF_INET; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + + return nlh; +} + +static void nfct_mnl_socket_close(void) +{ + mnl_socket_close(sock.mnl); +} + +static int +nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int res; + + nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type); + + res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len); + if (res < 0) + return res; + + res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf)); + while (res > 0) { + res = mnl_cb_run(buf, res, nlh->nlmsg_seq, sock.portid, + cb, NULL); + if (res <= MNL_CB_STOP) + break; + + res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf)); + } + + return res; +} + +static int nfct_stats_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_STATS_MAX) < 0) + return MNL_CB_OK; + + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nfct_stats_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTA_STATS_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + const char *attr2name[CTA_STATS_MAX+1] = { + [CTA_STATS_SEARCHED] = "searched", + [CTA_STATS_FOUND] = "found", + [CTA_STATS_NEW] = "new", + [CTA_STATS_INVALID] = "invalid", + [CTA_STATS_IGNORE] = "ignore", + [CTA_STATS_DELETE] = "delete", + [CTA_STATS_DELETE_LIST] = "delete_list", + [CTA_STATS_INSERT] = "insert", + [CTA_STATS_INSERT_FAILED] = "insert_failed", + [CTA_STATS_DROP] = "drop", + [CTA_STATS_EARLY_DROP] = "early_drop", + [CTA_STATS_ERROR] = "error", + [CTA_STATS_SEARCH_RESTART] = "search_restart", + }; + int i; + + mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_attr_cb, tb); + + printf("cpu=%-4u\t", ntohs(nfg->res_id)); + + for (i=0; ires_id)); + + for (i=0; i= 0) + break; + + goto try_proc; + + case EXP_STATS: + /* If we fail with netlink, fall back to /proc to ensure + * backward compatibility. + */ + if (nfct_mnl_socket_open() < 0) + goto try_proc; + + res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK_EXP, + IPCTNL_MSG_EXP_GET_STATS_CPU, + nfexp_stats_cb); + + nfct_mnl_socket_close(); + + /* don't look at /proc, we got the information via ctnetlink */ + if (res >= 0) + break; +try_proc: if (display_proc_conntrack_stats() < 0) exit_error(OTHER_PROBLEM, "Can't open /proc interface"); break; -- cgit v1.2.3 From ec4c0ff33746fb2c663194f27dd41deee83f870f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 27 Jun 2012 16:47:56 +0200 Subject: conntrack: -C uses ctnetlink instead of /proc/sys/net/netfilter/nf_conntrack_count Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/conntrack.c b/src/conntrack.c index 6d0f301..7451a80 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1541,6 +1541,26 @@ nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb) return res; } +static int +nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int res; + + nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type); + + res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len); + if (res < 0) + return res; + + res = mnl_socket_recvfrom(sock.mnl, buf, sizeof(buf)); + if (res < 0) + return res; + + return mnl_cb_run(buf, res, nlh->nlmsg_seq, sock.portid, cb, NULL); +} + static int nfct_stats_attr_cb(const struct nlattr *attr, void *data) { const struct nlattr **tb = data; @@ -1635,6 +1655,37 @@ static int nfexp_stats_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } +static int nfct_stats_global_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTA_STATS_GLOBAL_MAX) < 0) + return MNL_CB_OK; + + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { + perror("mnl_attr_validate"); + return MNL_CB_ERROR; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int nfct_global_stats_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[CTA_STATS_GLOBAL_MAX+1] = {}; + struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + + mnl_attr_parse(nlh, sizeof(*nfg), nfct_stats_global_attr_cb, tb); + + if (tb[CTA_STATS_GLOBAL_ENTRIES]) { + printf("%d\n", + ntohl(mnl_attr_get_u32(tb[CTA_STATS_GLOBAL_ENTRIES]))); + } + return MNL_CB_OK; +} + static struct ctproto_handler *h; int main(int argc, char *argv[]) @@ -2136,7 +2187,25 @@ int main(int argc, char *argv[]) res = nfexp_catch(cth); nfct_close(cth); break; - case CT_COUNT: { + case CT_COUNT: + /* If we fail with netlink, fall back to /proc to ensure + * backward compatibility. + */ + if (nfct_mnl_socket_open() < 0) + goto try_proc_count; + + res = nfct_mnl_get(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_STATS, + nfct_global_stats_cb); + + nfct_mnl_socket_close(); + + /* don't look at /proc, we got the information via ctnetlink */ + if (res >= 0) + break; + +try_proc_count: + { #define NF_CONNTRACK_COUNT_PROC "/proc/sys/net/netfilter/nf_conntrack_count" FILE *fd; int count; -- cgit v1.2.3 From c712ccebc993cad3f73000bbe9e4788ebeb95ca2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 24 Apr 2012 10:55:33 +0200 Subject: conntrackd: generalize file descriptor infrastructure This patch generalizes the select-based file descriptor infrastructure by allowing you to register file descriptors and its callbacks. Instead of hardcoding the descriptors that needs to be checked. Now, struct fds_item contains a callback and pointer to data that is passed to it: struct fds_item { struct list_head head; int fd; + void (*cb)(void *data); + void *data; }; Then, we check which ones are active in the select_main_step() function: list_for_each_entry(cur, &STATE(fds)->list, head) { if (FD_ISSET(cur->fd, &readfds)) cur->cb(cur->data); } And it invoked the corresponding callback. I had to slightly modify the channel infrastructure to fit it into the changes. This modularity is required for the upcoming cthelper support. Signed-off-by: Pablo Neira Ayuso --- include/channel.h | 11 ++- include/conntrackd.h | 3 +- include/fds.h | 4 +- src/channel.c | 5 + src/channel_tcp.c | 1 + src/fds.c | 61 ++++++++++++- src/main.c | 2 +- src/multichannel.c | 7 +- src/run.c | 251 +++++++++++++++++++-------------------------------- src/stats-mode.c | 1 - src/sync-mode.c | 146 +++++++++++++++--------------- src/tcp.c | 1 - 12 files changed, 249 insertions(+), 244 deletions(-) diff --git a/include/channel.h b/include/channel.h index 9b5fad8..46a354f 100644 --- a/include/channel.h +++ b/include/channel.h @@ -35,7 +35,8 @@ struct tcp_channel { #define CHANNEL_F_BUFFERED (1 << 1) #define CHANNEL_F_STREAM (1 << 2) #define CHANNEL_F_ERRORS (1 << 3) -#define CHANNEL_F_MAX (1 << 4) +#define CHANNEL_F_ACCEPT (1 << 4) +#define CHANNEL_F_MAX (1 << 5) union channel_type_conf { struct mcast_conf mcast; @@ -52,8 +53,12 @@ struct channel_conf { struct nlif_handle; +#define CHANNEL_T_DATAGRAM 0 +#define CHANNEL_T_STREAM 1 + struct channel_ops { int headersiz; + int type; void * (*open)(void *conf); void (*close)(void *channel); int (*send)(void *channel, const void *data, int len); @@ -97,6 +102,8 @@ void channel_stats(struct channel *c, int fd); void channel_stats_extended(struct channel *c, int active, struct nlif_handle *h, int fd); +int channel_type(struct channel *c); + #define MULTICHANNEL_MAX 4 struct multichannel { @@ -119,6 +126,6 @@ void multichannel_stats_extended(struct multichannel *m, int multichannel_get_ifindex(struct multichannel *m, int i); int multichannel_get_current_ifindex(struct multichannel *m); void multichannel_set_current_channel(struct multichannel *m, int i); -void multichannel_change_current_channel(struct multichannel *m, int i); +void multichannel_change_current_channel(struct multichannel *m, struct channel *c); #endif /* _CHANNEL_H_ */ diff --git a/include/conntrackd.h b/include/conntrackd.h index 9359dfa..0e203e7 100644 --- a/include/conntrackd.h +++ b/include/conntrackd.h @@ -264,7 +264,6 @@ extern struct ct_general_state st; struct ct_mode { struct internal_handler *internal; int (*init)(void); - void (*run)(fd_set *readfds); int (*local)(int fd, int type, void *data); void (*kill)(void); }; @@ -278,7 +277,7 @@ extern struct ct_mode stats_mode; /* These live in run.c */ void killer(int foo); int init(void); -void run(void); +void select_main_loop(void); /* from read_config_yy.c */ int diff --git a/include/fds.h b/include/fds.h index f3728d7..ed0c8be 100644 --- a/include/fds.h +++ b/include/fds.h @@ -12,11 +12,13 @@ struct fds { struct fds_item { struct list_head head; int fd; + void (*cb)(void *data); + void *data; }; struct fds *create_fds(void); void destroy_fds(struct fds *); -int register_fd(int fd, struct fds *fds); +int register_fd(int fd, void (*cb)(void *data), void *data, struct fds *fds); int unregister_fd(int fd, struct fds *fds); #endif diff --git a/src/channel.c b/src/channel.c index 818bb01..8b7c319 100644 --- a/src/channel.c +++ b/src/channel.c @@ -310,3 +310,8 @@ int channel_accept(struct channel *c) { return c->ops->accept(c); } + +int channel_type(struct channel *c) +{ + return c->ops->type; +} diff --git a/src/channel_tcp.c b/src/channel_tcp.c index f132840..a84603c 100644 --- a/src/channel_tcp.c +++ b/src/channel_tcp.c @@ -137,6 +137,7 @@ channel_tcp_accept(struct channel *c) struct channel_ops channel_tcp = { .headersiz = 40, /* IP header (20 bytes) + TCP header 20 (bytes) */ + .type = CHANNEL_T_STREAM, .open = channel_tcp_open, .close = channel_tcp_close, .send = channel_tcp_send, diff --git a/src/fds.c b/src/fds.c index 347eee1..0b95437 100644 --- a/src/fds.c +++ b/src/fds.c @@ -1,5 +1,5 @@ /* - * (C) 2006-2008 by Pablo Neira Ayuso + * (C) 2006-2012 by Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,9 +14,16 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Part of this code has been sponsored by Vyatta Inc. */ #include #include +#include +#include + +#include "conntrackd.h" +#include "date.h" #include "fds.h" struct fds *create_fds(void) @@ -44,7 +51,7 @@ void destroy_fds(struct fds *fds) free(fds); } -int register_fd(int fd, struct fds *fds) +int register_fd(int fd, void (*cb)(void *data), void *data, struct fds *fds) { struct fds_item *item; @@ -58,7 +65,10 @@ int register_fd(int fd, struct fds *fds) return -1; item->fd = fd; - list_add(&item->head, &fds->list); + item->cb = cb; + item->data = data; + /* Order matters: the descriptors are served in FIFO basis. */ + list_add_tail(&item->head, &fds->list); return 0; } @@ -92,3 +102,48 @@ int unregister_fd(int fd, struct fds *fds) return 0; } +static void select_main_step(struct timeval *next_alarm) +{ + int ret; + fd_set readfds = STATE(fds)->readfds; + struct fds_item *cur, *tmp; + + ret = select(STATE(fds)->maxfd + 1, &readfds, NULL, NULL, next_alarm); + if (ret == -1) { + /* interrupted syscall, retry */ + if (errno == EINTR) + return; + + STATE(stats).select_failed++; + return; + } + + /* signals are racy */ + sigprocmask(SIG_BLOCK, &STATE(block), NULL); + + list_for_each_entry_safe(cur, tmp, &STATE(fds)->list, head) { + if (FD_ISSET(cur->fd, &readfds)) + cur->cb(cur->data); + } + + sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); +} + +void __attribute__((noreturn)) select_main_loop(void) +{ + struct timeval next_alarm; + struct timeval *next = NULL; + + while(1) { + do_gettimeofday(); + + sigprocmask(SIG_BLOCK, &STATE(block), NULL); + if (next != NULL && !timerisset(next)) + next = do_alarm_run(&next_alarm); + else + next = get_next_alarm_run(&next_alarm); + sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); + + select_main_step(next); + } +} diff --git a/src/main.c b/src/main.c index f7803fd..26f6c14 100644 --- a/src/main.c +++ b/src/main.c @@ -406,6 +406,6 @@ int main(int argc, char *argv[]) /* * run main process */ - run(); + select_main_loop(); return 0; } diff --git a/src/multichannel.c b/src/multichannel.c index de69d5c..952b567 100644 --- a/src/multichannel.c +++ b/src/multichannel.c @@ -109,8 +109,9 @@ void multichannel_set_current_channel(struct multichannel *m, int i) m->current = m->channel[i]; } -void multichannel_change_current_channel(struct multichannel *m, int i) +void +multichannel_change_current_channel(struct multichannel *m, struct channel *c) { - if (m->current != m->channel[i]) - m->current = m->channel[i]; + if (m->current != c) + m->current = c; } diff --git a/src/run.c b/src/run.c index 421ff41..f94e853 100644 --- a/src/run.c +++ b/src/run.c @@ -1,5 +1,5 @@ /* - * (C) 2006-2011 by Pablo Neira Ayuso + * (C) 2006-2012 by Pablo Neira Ayuso * (C) 2011 by Vyatta Inc. * * This program is free software; you can redistribute it and/or modify @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * Description: run and init functions + * Part of this code has been sponsored by Vyatta Inc. */ #include "conntrackd.h" @@ -460,6 +460,87 @@ static int exp_get_handler(enum nf_conntrack_msg_type type, return NFCT_CB_CONTINUE; } +/* order received via UNIX socket */ +static void local_cb(void *data) +{ + do_local_server_step(&STATE(local), NULL, local_handler); +} + +/* we have received an event from ctnetlink */ +static void event_cb(void *data) +{ + int ret; + + ret = nfct_catch(STATE(event)); + /* reset event iteration limit counter */ + STATE(event_iterations_limit) = CONFIG(event_iterations_limit); + if (ret == -1) { + switch(errno) { + case ENOBUFS: + /* We have hit ENOBUFS, it's likely that we are + * losing events. Two possible situations may + * trigger this error: + * + * 1) The netlink receiver buffer is too small: + * increasing the netlink buffer size should + * be enough. However, some event messages + * got lost. We have to resync ourselves + * with the kernel table conntrack table to + * resolve the inconsistency. + * + * 2) The receiver is too slow to process the + * netlink messages so that the queue gets + * full quickly. This generally happens + * if the system is under heavy workload + * (busy CPU). In this case, increasing the + * size of the netlink receiver buffer + * would not help anymore since we would + * be delaying the overrun. Moreover, we + * should avoid resynchronizations. We + * should do our best here and keep + * replicating as much states as possible. + * If workload lowers at some point, + * we resync ourselves. + */ + nl_resize_socket_buffer(STATE(event)); + if (CONFIG(nl_overrun_resync) > 0 && + STATE(mode)->internal->flags & INTERNAL_F_RESYNC) { + add_alarm(&STATE(resync_alarm), + CONFIG(nl_overrun_resync),0); + } + STATE(stats).nl_catch_event_failed++; + STATE(stats).nl_overrun++; + break; + case ENOENT: + /* + * We received a message from another + * netfilter subsystem that we are not + * interested in. Just ignore it. + */ + break; + case EAGAIN: + /* No more events to receive, try later. */ + break; + default: + STATE(stats).nl_catch_event_failed++; + break; + } + } +} + +/* we previously requested a resync due to buffer overrun. */ +static void resync_cb(void *data) +{ + nfct_catch(STATE(resync)); + if (STATE(mode)->internal->ct.purge) + STATE(mode)->internal->ct.purge(); +} + +static void poll_cb(void *data) +{ + nfct_catch(STATE(resync)); +} + int init(void) { @@ -493,7 +574,7 @@ init(void) dlog(LOG_ERR, "can't open unix socket!"); return -1; } - register_fd(STATE(local).fd, STATE(fds)); + register_fd(STATE(local).fd, local_cb, NULL, STATE(fds)); /* resynchronize (like 'dump' socket) but it also purges old entries */ STATE(resync) = nfct_open(CONFIG(netlink).subsys_id, 0); @@ -507,7 +588,13 @@ init(void) NFCT_T_ALL, STATE(mode)->internal->ct.resync, NULL); - register_fd(nfct_fd(STATE(resync)), STATE(fds)); + if (CONFIG(flags) & CTD_POLL) { + register_fd(nfct_fd(STATE(resync)), poll_cb, + NULL, STATE(fds)); + } else { + register_fd(nfct_fd(STATE(resync)), resync_cb, + NULL, STATE(fds)); + } fcntl(nfct_fd(STATE(resync)), F_SETFL, O_NONBLOCK); if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { @@ -590,7 +677,7 @@ init(void) nfexp_callback_register2(STATE(event), NFCT_T_ALL, exp_event_handler, NULL); } - register_fd(nfct_fd(STATE(event)), STATE(fds)); + register_fd(nfct_fd(STATE(event)), event_cb, NULL, STATE(fds)); } /* Signals handling */ @@ -618,157 +705,3 @@ init(void) return 0; } - -static void run_events(struct timeval *next_alarm) -{ - int ret; - fd_set readfds = STATE(fds)->readfds; - - ret = select(STATE(fds)->maxfd + 1, &readfds, NULL, NULL, next_alarm); - if (ret == -1) { - /* interrupted syscall, retry */ - if (errno == EINTR) - return; - - STATE(stats).select_failed++; - return; - } - - /* signals are racy */ - sigprocmask(SIG_BLOCK, &STATE(block), NULL); - - /* order received via UNIX socket */ - if (FD_ISSET(STATE(local).fd, &readfds)) - do_local_server_step(&STATE(local), NULL, local_handler); - - /* we have receive an event from ctnetlink */ - if (FD_ISSET(nfct_fd(STATE(event)), &readfds)) { - ret = nfct_catch(STATE(event)); - /* reset event iteration limit counter */ - STATE(event_iterations_limit) = CONFIG(event_iterations_limit); - if (ret == -1) { - switch(errno) { - case ENOBUFS: - /* We have hit ENOBUFS, it's likely that we are - * losing events. Two possible situations may - * trigger this error: - * - * 1) The netlink receiver buffer is too small: - * increasing the netlink buffer size should - * be enough. However, some event messages - * got lost. We have to resync ourselves - * with the kernel table conntrack table to - * resolve the inconsistency. - * - * 2) The receiver is too slow to process the - * netlink messages so that the queue gets - * full quickly. This generally happens - * if the system is under heavy workload - * (busy CPU). In this case, increasing the - * size of the netlink receiver buffer - * would not help anymore since we would - * be delaying the overrun. Moreover, we - * should avoid resynchronizations. We - * should do our best here and keep - * replicating as much states as possible. - * If workload lowers at some point, - * we resync ourselves. - */ - nl_resize_socket_buffer(STATE(event)); - if (CONFIG(nl_overrun_resync) > 0 && - STATE(mode)->internal->flags & INTERNAL_F_RESYNC) { - add_alarm(&STATE(resync_alarm), - CONFIG(nl_overrun_resync),0); - } - STATE(stats).nl_catch_event_failed++; - STATE(stats).nl_overrun++; - break; - case ENOENT: - /* - * We received a message from another - * netfilter subsystem that we are not - * interested in. Just ignore it. - */ - break; - case EAGAIN: - /* No more events to receive, try later. */ - break; - default: - STATE(stats).nl_catch_event_failed++; - break; - } - } - } - /* we previously requested a resync due to buffer overrun. */ - if (FD_ISSET(nfct_fd(STATE(resync)), &readfds)) { - nfct_catch(STATE(resync)); - if (STATE(mode)->internal->ct.purge) - STATE(mode)->internal->ct.purge(); - } - - if (STATE(mode)->run) - STATE(mode)->run(&readfds); - - sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); -} - -static void run_polling(struct timeval *next_alarm) -{ - int ret; - fd_set readfds = STATE(fds)->readfds; - - ret = select(STATE(fds)->maxfd + 1, &readfds, NULL, NULL, next_alarm); - if (ret == -1) { - /* interrupted syscall, retry */ - if (errno == EINTR) - return; - - STATE(stats).select_failed++; - return; - } - - /* signals are racy */ - sigprocmask(SIG_BLOCK, &STATE(block), NULL); - - /* order received via UNIX socket */ - if (FD_ISSET(STATE(local).fd, &readfds)) - do_local_server_step(&STATE(local), NULL, local_handler); - - /* we requested a dump from the kernel via polling_alarm */ - if (FD_ISSET(nfct_fd(STATE(resync)), &readfds)) - nfct_catch(STATE(resync)); - - if (STATE(mode)->run) - STATE(mode)->run(&readfds); - - sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); -} - -static void __attribute__((noreturn)) -do_run(void (*run_step)(struct timeval *next_alarm)) -{ - struct timeval next_alarm; - struct timeval *next = NULL; - - while(1) { - do_gettimeofday(); - - sigprocmask(SIG_BLOCK, &STATE(block), NULL); - if (next != NULL && !timerisset(next)) - next = do_alarm_run(&next_alarm); - else - next = get_next_alarm_run(&next_alarm); - sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); - - run_step(next); - } -} - -void run(void) -{ - if (CONFIG(flags) & CTD_POLL) { - do_run(run_polling); - } else { - do_run(run_events); - } -} diff --git a/src/stats-mode.c b/src/stats-mode.c index b768033..6b7f08d 100644 --- a/src/stats-mode.c +++ b/src/stats-mode.c @@ -201,7 +201,6 @@ static struct internal_handler internal_cache_stats = { struct ct_mode stats_mode = { .init = init_stats, - .run = NULL, .local = local_handler_stats, .kill = kill_stats, .internal = &internal_cache_stats, diff --git a/src/sync-mode.c b/src/sync-mode.c index 17f866a..be6366d 100644 --- a/src/sync-mode.c +++ b/src/sync-mode.c @@ -1,5 +1,5 @@ /* - * (C) 2006-2011 by Pablo Neira Ayuso + * (C) 2006-2012 by Pablo Neira Ayuso * (C) 2011 by Vyatta Inc. * * This program is free software; you can redistribute it and/or modify @@ -78,7 +78,7 @@ static struct nf_expect *msg2exp_alloc(struct nethdr *net, size_t remain) } static void -do_channel_handler_step(int i, struct nethdr *net, size_t remain) +do_channel_handler_step(struct channel *c, struct nethdr *net, size_t remain) { struct nf_conntrack *ct = NULL; struct nf_expect *exp = NULL; @@ -91,10 +91,10 @@ do_channel_handler_step(int i, struct nethdr *net, size_t remain) switch (STATE_SYNC(sync)->recv(net)) { case MSG_DATA: - multichannel_change_current_channel(STATE_SYNC(channel), i); + multichannel_change_current_channel(STATE_SYNC(channel), c); break; case MSG_CTL: - multichannel_change_current_channel(STATE_SYNC(channel), i); + multichannel_change_current_channel(STATE_SYNC(channel), c); return; case MSG_BAD: STATE_SYNC(error).msg_rcv_malformed++; @@ -175,7 +175,7 @@ static int channel_stream(struct channel *m, const char *ptr, ssize_t remain) } /* handler for messages received */ -static int channel_handler_routine(struct channel *m, int i) +static int channel_handler_routine(struct channel *m) { ssize_t numbytes; ssize_t remain, pending = cur - __net; @@ -242,7 +242,7 @@ static int channel_handler_routine(struct channel *m, int i) HDR_NETWORK2HOST(net); - do_channel_handler_step(i, net, remain); + do_channel_handler_step(m, net, remain); ptr += net->len; remain -= net->len; } @@ -250,12 +250,13 @@ static int channel_handler_routine(struct channel *m, int i) } /* handler for messages received */ -static void channel_handler(struct channel *m, int i) +static void channel_handler(void *data) { + struct channel *c = data; int k; for (k=0; kinternal->ct.flush(); } +static void commit_cb(void *data) +{ + int ret; + + read_evfd(STATE_SYNC(commit).evfd); + + ret = STATE_SYNC(commit).rq[0].cb(STATE_SYNC(commit).h, 0); + if (ret == 0) { + /* we still have things in the callback queue. */ + if (STATE_SYNC(commit).rq[1].cb) { + int fd = STATE_SYNC(commit).clientfd; + + STATE_SYNC(commit).rq[0].cb = + STATE_SYNC(commit).rq[1].cb; + + STATE_SYNC(commit).rq[1].cb = NULL; + + STATE_SYNC(commit).clientfd = -1; + STATE_SYNC(commit).rq[0].cb(STATE_SYNC(commit).h, fd); + } else { + /* Close the client socket now, we're done. */ + close(STATE_SYNC(commit).clientfd); + STATE_SYNC(commit).clientfd = -1; + } + } +} + +static void channel_accept_cb(void *data) +{ + struct channel *c = data; + int fd; + + fd = channel_accept(data); + if (fd < 0) + return; + + register_fd(fd, channel_handler, c, STATE(fds)); +} + +static void tx_queue_cb(void *data) +{ + STATE_SYNC(sync)->xmit(); + + /* flush pending messages */ + multichannel_send_flush(STATE_SYNC(channel)); +} + static int init_sync(void) { int i; @@ -370,8 +418,19 @@ static int init_sync(void) for (i=0; ichannel_num; i++) { int fd = channel_get_fd(STATE_SYNC(channel)->channel[i]); fcntl(fd, F_SETFL, O_NONBLOCK); - if (register_fd(fd, STATE(fds)) == -1) - return -1; + + switch(channel_type(STATE_SYNC(channel)->channel[i])) { + case CHANNEL_T_STREAM: + register_fd(fd, channel_accept_cb, + STATE_SYNC(channel)->channel[i], + STATE(fds)); + break; + case CHANNEL_T_DATAGRAM: + register_fd(fd, channel_handler, + STATE_SYNC(channel)->channel[i], + STATE(fds)); + break; + } } STATE_SYNC(interface) = nl_init_interface_handler(); @@ -379,7 +438,8 @@ static int init_sync(void) dlog(LOG_ERR, "can't open interface watcher"); return -1; } - if (register_fd(nlif_fd(STATE_SYNC(interface)), STATE(fds)) == -1) + if (register_fd(nlif_fd(STATE_SYNC(interface)), + interface_handler, NULL, STATE(fds)) == -1) return -1; STATE_SYNC(tx_queue) = queue_create("txqueue", INT_MAX, QUEUE_F_EVFD); @@ -387,8 +447,8 @@ static int init_sync(void) dlog(LOG_ERR, "cannot create tx queue"); return -1; } - if (register_fd(queue_get_eventfd(STATE_SYNC(tx_queue)), - STATE(fds)) == -1) + if (register_fd(queue_get_eventfd(STATE_SYNC(tx_queue)), + tx_queue_cb, NULL, STATE(fds)) == -1) return -1; STATE_SYNC(commit).h = nfct_open(CONFIG(netlink).subsys_id, 0); @@ -404,7 +464,7 @@ static int init_sync(void) return -1; } if (register_fd(get_read_evfd(STATE_SYNC(commit).evfd), - STATE(fds)) == -1) { + commit_cb, NULL, STATE(fds)) == -1) { return -1; } STATE_SYNC(commit).clientfd = -1; @@ -417,61 +477,6 @@ static int init_sync(void) return 0; } -static void channel_check(struct channel *c, int i, fd_set *readfds) -{ - /* In case that this channel is connection-oriented. */ - if (channel_accept_isset(c, readfds)) - channel_accept(c); - - /* For data handling. */ - if (channel_isset(c, readfds)) - channel_handler(c, i); -} - -static void run_sync(fd_set *readfds) -{ - int i; - - for (i=0; ichannel_num; i++) - channel_check(STATE_SYNC(channel)->channel[i], i, readfds); - - if (FD_ISSET(queue_get_eventfd(STATE_SYNC(tx_queue)), readfds)) - STATE_SYNC(sync)->xmit(); - - if (FD_ISSET(nlif_fd(STATE_SYNC(interface)), readfds)) - interface_handler(); - - if (FD_ISSET(get_read_evfd(STATE_SYNC(commit).evfd), readfds)) { - int ret; - - read_evfd(STATE_SYNC(commit).evfd); - - ret = STATE_SYNC(commit).rq[0].cb(STATE_SYNC(commit).h, 0); - if (ret == 0) { - /* we still have things in the callback queue. */ - if (STATE_SYNC(commit).rq[1].cb) { - int fd = STATE_SYNC(commit).clientfd; - - STATE_SYNC(commit).rq[0].cb = - STATE_SYNC(commit).rq[1].cb; - - STATE_SYNC(commit).rq[1].cb = NULL; - - STATE_SYNC(commit).clientfd = -1; - STATE_SYNC(commit).rq[0].cb( - STATE_SYNC(commit).h, fd); - } else { - /* Close the client socket now, we're done. */ - close(STATE_SYNC(commit).clientfd); - STATE_SYNC(commit).clientfd = -1; - } - } - } - - /* flush pending messages */ - multichannel_send_flush(STATE_SYNC(channel)); -} - static void kill_sync(void) { STATE(mode)->internal->close(); @@ -747,7 +752,6 @@ static int local_handler_sync(int fd, int type, void *data) struct ct_mode sync_mode = { .init = init_sync, - .run = run_sync, .local = local_handler_sync, .kill = kill_sync, /* the internal handler is set in run-time. */ diff --git a/src/tcp.c b/src/tcp.c index f6b05ef..af27c46 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -264,7 +264,6 @@ int tcp_accept(struct tcp_sock *m) m->client_fd = ret; m->state = TCP_SERVER_CONNECTED; - register_fd(m->client_fd, STATE(fds)); } return m->client_fd; } -- cgit v1.2.3 From 5a0d0ecf30fb1686cfb10aaa852fee9c8ed4360a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 24 Apr 2012 11:56:00 +0200 Subject: conntrackd: move ctnetlink code to ctnl.c (removed from run.c) This patch moves the specific ctnetlink code to ctnl.c to prepare the introduction of the cthelper infrastructure. Signed-off-by: Pablo Neira Ayuso --- include/conntrackd.h | 7 +- src/Makefile.am | 1 + src/ctnl.c | 522 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/run.c | 477 ++-------------------------------------------- src/sync-mode.c | 3 - 5 files changed, 543 insertions(+), 467 deletions(-) create mode 100644 src/ctnl.c diff --git a/include/conntrackd.h b/include/conntrackd.h index 0e203e7..ec720ec 100644 --- a/include/conntrackd.h +++ b/include/conntrackd.h @@ -268,7 +268,12 @@ struct ct_mode { void (*kill)(void); }; -/* conntrackd modes */ +/* basic ctnl functions */ +void ctnl_kill(void); +int ctnl_local(int fd, int type, void *data); +int ctnl_init(void); + +/* conntrackd ctnl modes */ extern struct ct_mode sync_mode; extern struct ct_mode stats_mode; diff --git a/src/Makefile.am b/src/Makefile.am index 93c035c..fa4ab75 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,7 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ 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 \ sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \ traffic_stats.c stats-mode.c \ network.c cidr.c \ diff --git a/src/ctnl.c b/src/ctnl.c new file mode 100644 index 0000000..107cd5d --- /dev/null +++ b/src/ctnl.c @@ -0,0 +1,522 @@ +/* + * (C) 2006-2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Part of this code has been sponsored by Vyatta Inc. + */ + +#include "conntrackd.h" +#include "netlink.h" +#include "filter.h" +#include "log.h" +#include "alarm.h" +#include "fds.h" +#include "traffic_stats.h" +#include "process.h" +#include "origin.h" +#include "date.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +void ctnl_kill(void) +{ + if (!(CONFIG(flags) & CTD_POLL)) + nfct_close(STATE(event)); + + nfct_close(STATE(resync)); + nfct_close(STATE(get)); + origin_unregister(STATE(flush)); + nfct_close(STATE(flush)); + + if (STATE(us_filter)) + ct_filter_destroy(STATE(us_filter)); + STATE(mode)->kill(); + + if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { + nfct_close(STATE(dump)); + } +} + +static void local_flush_master(void) +{ + STATE(stats).nl_kernel_table_flush++; + dlog(LOG_NOTICE, "flushing kernel conntrack table"); + + /* fork a child process that performs the flush operation, + * meanwhile the parent process handles events. */ + if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL, + NULL, NULL) == 0) { + nl_flush_conntrack_table_selective(); + exit(EXIT_SUCCESS); + } +} + +static void local_resync_master(void) +{ + if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { + STATE(stats).nl_kernel_table_resync++; + dlog(LOG_NOTICE, "resync with master conntrack table"); + nl_dump_conntrack_table(STATE(dump)); + } else { + dlog(LOG_NOTICE, "resync is unsupported in this mode"); + } +} + +static void local_exp_flush_master(void) +{ + if (!(CONFIG(flags) & CTD_EXPECT)) + return; + + STATE(stats).nl_kernel_table_flush++; + dlog(LOG_NOTICE, "flushing kernel expect table"); + + /* fork a child process that performs the flush operation, + * meanwhile the parent process handles events. */ + if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL, + NULL, NULL) == 0) { + nl_flush_expect_table(STATE(flush)); + exit(EXIT_SUCCESS); + } +} + +static void local_exp_resync_master(void) +{ + if (!(CONFIG(flags) & CTD_EXPECT)) + return; + + if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { + STATE(stats).nl_kernel_table_resync++; + dlog(LOG_NOTICE, "resync with master expect table"); + nl_dump_expect_table(STATE(dump)); + } else { + dlog(LOG_NOTICE, "resync is unsupported in this mode"); + } +} + +int ctnl_local(int fd, int type, void *data) +{ + int ret = LOCAL_RET_OK; + + switch(type) { + case CT_FLUSH_MASTER: + local_flush_master(); + break; + case CT_RESYNC_MASTER: + local_resync_master(); + break; + case EXP_FLUSH_MASTER: + local_exp_flush_master(); + break; + case EXP_RESYNC_MASTER: + local_exp_resync_master(); + break; + case ALL_FLUSH_MASTER: + local_flush_master(); + local_exp_flush_master(); + break; + case ALL_RESYNC_MASTER: + local_resync_master(); + local_exp_resync_master(); + break; + } + + ret = STATE(mode)->local(fd, type, data); + if (ret == LOCAL_RET_ERROR) { + STATE(stats).local_unknown_request++; + return LOCAL_RET_ERROR; + } + return ret; +} + +static void do_overrun_resync_alarm(struct alarm_block *a, void *data) +{ + nl_send_resync(STATE(resync)); + STATE(stats).nl_kernel_table_resync++; +} + +static void do_polling_alarm(struct alarm_block *a, void *data) +{ + if (STATE(mode)->internal->ct.purge) + STATE(mode)->internal->ct.purge(); + + if (STATE(mode)->internal->exp.purge) + STATE(mode)->internal->exp.purge(); + + nl_send_resync(STATE(resync)); + nl_send_expect_resync(STATE(resync)); + add_alarm(&STATE(polling_alarm), CONFIG(poll_kernel_secs), 0); +} + +static int event_handler(const struct nlmsghdr *nlh, + enum nf_conntrack_msg_type type, + struct nf_conntrack *ct, + void *data) +{ + int origin_type; + + STATE(stats).nl_events_received++; + + /* skip user-space filtering if already do it in the kernel */ + if (ct_filter_conntrack(ct, !CONFIG(filter_from_kernelspace))) { + STATE(stats).nl_events_filtered++; + goto out; + } + + origin_type = origin_find(nlh); + + switch(type) { + case NFCT_T_NEW: + STATE(mode)->internal->ct.new(ct, origin_type); + break; + case NFCT_T_UPDATE: + STATE(mode)->internal->ct.upd(ct, origin_type); + break; + case NFCT_T_DESTROY: + if (STATE(mode)->internal->ct.del(ct, origin_type)) + update_traffic_stats(ct); + break; + default: + STATE(stats).nl_events_unknown_type++; + break; + } + +out: + /* we reset the iteration limiter in the main select loop. */ + if (STATE(event_iterations_limit)-- <= 0) + return NFCT_CB_STOP; + else + return NFCT_CB_CONTINUE; +} + +static int exp_event_handler(const struct nlmsghdr *nlh, + enum nf_conntrack_msg_type type, + struct nf_expect *exp, + void *data) +{ + int origin_type; + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); + + STATE(stats).nl_events_received++; + + if (!exp_filter_find(STATE(exp_filter), exp)) { + STATE(stats).nl_events_filtered++; + goto out; + } + if (ct_filter_conntrack(master, 1)) + return NFCT_CB_CONTINUE; + + origin_type = origin_find(nlh); + + switch(type) { + case NFCT_T_NEW: + STATE(mode)->internal->exp.new(exp, origin_type); + break; + case NFCT_T_UPDATE: + STATE(mode)->internal->exp.upd(exp, origin_type); + break; + case NFCT_T_DESTROY: + STATE(mode)->internal->exp.del(exp, origin_type); + break; + default: + STATE(stats).nl_events_unknown_type++; + break; + } + +out: + /* we reset the iteration limiter in the main select loop. */ + if (STATE(event_iterations_limit)-- <= 0) + return NFCT_CB_STOP; + else + return NFCT_CB_CONTINUE; +} + +static int dump_handler(enum nf_conntrack_msg_type type, + struct nf_conntrack *ct, + void *data) +{ + if (ct_filter_conntrack(ct, 1)) + return NFCT_CB_CONTINUE; + + switch(type) { + case NFCT_T_UPDATE: + STATE(mode)->internal->ct.populate(ct); + break; + default: + STATE(stats).nl_dump_unknown_type++; + break; + } + return NFCT_CB_CONTINUE; +} + +static int exp_dump_handler(enum nf_conntrack_msg_type type, + struct nf_expect *exp, void *data) +{ + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); + + if (!exp_filter_find(STATE(exp_filter), exp)) + return NFCT_CB_CONTINUE; + + if (ct_filter_conntrack(master, 1)) + return NFCT_CB_CONTINUE; + + switch(type) { + case NFCT_T_UPDATE: + STATE(mode)->internal->exp.populate(exp); + break; + default: + STATE(stats).nl_dump_unknown_type++; + break; + } + return NFCT_CB_CONTINUE; +} + +static int get_handler(enum nf_conntrack_msg_type type, + struct nf_conntrack *ct, + void *data) +{ + if (ct_filter_conntrack(ct, 1)) + return NFCT_CB_CONTINUE; + + STATE(get_retval) = 1; + return NFCT_CB_CONTINUE; +} + +static int exp_get_handler(enum nf_conntrack_msg_type type, + struct nf_expect *exp, void *data) +{ + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); + + if (!exp_filter_find(STATE(exp_filter), exp)) + return NFCT_CB_CONTINUE; + + if (ct_filter_conntrack(master, 1)) + return NFCT_CB_CONTINUE; + + STATE(get_retval) = 1; + return NFCT_CB_CONTINUE; +} + +/* we have received an event from ctnetlink */ +static void event_cb(void *data) +{ + int ret; + + ret = nfct_catch(STATE(event)); + /* reset event iteration limit counter */ + STATE(event_iterations_limit) = CONFIG(event_iterations_limit); + if (ret == -1) { + switch(errno) { + case ENOBUFS: + /* We have hit ENOBUFS, it's likely that we are + * losing events. Two possible situations may + * trigger this error: + * + * 1) The netlink receiver buffer is too small: + * increasing the netlink buffer size should + * be enough. However, some event messages + * got lost. We have to resync ourselves + * with the kernel table conntrack table to + * resolve the inconsistency. + * + * 2) The receiver is too slow to process the + * netlink messages so that the queue gets + * full quickly. This generally happens + * if the system is under heavy workload + * (busy CPU). In this case, increasing the + * size of the netlink receiver buffer + * would not help anymore since we would + * be delaying the overrun. Moreover, we + * should avoid resynchronizations. We + * should do our best here and keep + * replicating as much states as possible. + * If workload lowers at some point, + * we resync ourselves. + */ + nl_resize_socket_buffer(STATE(event)); + if (CONFIG(nl_overrun_resync) > 0 && + STATE(mode)->internal->flags & INTERNAL_F_RESYNC) { + add_alarm(&STATE(resync_alarm), + CONFIG(nl_overrun_resync),0); + } + STATE(stats).nl_catch_event_failed++; + STATE(stats).nl_overrun++; + break; + case ENOENT: + /* + * We received a message from another + * netfilter subsystem that we are not + * interested in. Just ignore it. + */ + break; + case EAGAIN: + /* No more events to receive, try later. */ + break; + default: + STATE(stats).nl_catch_event_failed++; + break; + } + } +} + +/* we previously requested a resync due to buffer overrun. */ +static void resync_cb(void *data) +{ + nfct_catch(STATE(resync)); + if (STATE(mode)->internal->ct.purge) + STATE(mode)->internal->ct.purge(); +} + +static void poll_cb(void *data) +{ + nfct_catch(STATE(resync)); +} + +int ctnl_init(void) +{ + if (CONFIG(flags) & CTD_STATS_MODE) + STATE(mode) = &stats_mode; + else if (CONFIG(flags) & CTD_SYNC_MODE) + STATE(mode) = &sync_mode; + else { + fprintf(stderr, "WARNING: No running mode specified. " + "Defaulting to statistics mode.\n"); + CONFIG(flags) |= CTD_STATS_MODE; + STATE(mode) = &stats_mode; + } + + /* Initialization */ + if (STATE(mode)->init() == -1) { + dlog(LOG_ERR, "initialization failed"); + return -1; + } + + /* resynchronize (like 'dump' socket) but it also purges old entries */ + STATE(resync) = nfct_open(CONFIG(netlink).subsys_id, 0); + if (STATE(resync)== NULL) { + dlog(LOG_ERR, "can't open netlink handler: %s", + strerror(errno)); + dlog(LOG_ERR, "no ctnetlink kernel support?"); + return -1; + } + nfct_callback_register(STATE(resync), + NFCT_T_ALL, + STATE(mode)->internal->ct.resync, + NULL); + if (CONFIG(flags) & CTD_POLL) { + register_fd(nfct_fd(STATE(resync)), poll_cb, + NULL, STATE(fds)); + } else { + register_fd(nfct_fd(STATE(resync)), resync_cb, + NULL, STATE(fds)); + } + fcntl(nfct_fd(STATE(resync)), F_SETFL, O_NONBLOCK); + + if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { + STATE(dump) = nfct_open(CONFIG(netlink).subsys_id, 0); + if (STATE(dump) == NULL) { + dlog(LOG_ERR, "can't open netlink handler: %s", + strerror(errno)); + dlog(LOG_ERR, "no ctnetlink kernel support?"); + return -1; + } + nfct_callback_register(STATE(dump), NFCT_T_ALL, + dump_handler, NULL); + + if (CONFIG(flags) & CTD_EXPECT) { + nfexp_callback_register(STATE(dump), NFCT_T_ALL, + exp_dump_handler, NULL); + } + + if (nl_dump_conntrack_table(STATE(dump)) == -1) { + dlog(LOG_ERR, "can't get kernel conntrack table"); + return -1; + } + + if (CONFIG(flags) & CTD_EXPECT) { + if (nl_dump_expect_table(STATE(dump)) == -1) { + dlog(LOG_ERR, "can't get kernel " + "expect table"); + return -1; + } + } + } + + STATE(get) = nfct_open(CONFIG(netlink).subsys_id, 0); + if (STATE(get) == NULL) { + dlog(LOG_ERR, "can't open netlink handler: %s", + strerror(errno)); + dlog(LOG_ERR, "no ctnetlink kernel support?"); + return -1; + } + nfct_callback_register(STATE(get), NFCT_T_ALL, get_handler, NULL); + + if (CONFIG(flags) & CTD_EXPECT) { + nfexp_callback_register(STATE(get), NFCT_T_ALL, + exp_get_handler, NULL); + } + + STATE(flush) = nfct_open(CONFIG(netlink).subsys_id, 0); + if (STATE(flush) == NULL) { + dlog(LOG_ERR, "cannot open flusher handler"); + return -1; + } + /* register this handler as the origin of a flush operation */ + origin_register(STATE(flush), CTD_ORIGIN_FLUSH); + + if (CONFIG(flags) & CTD_POLL) { + init_alarm(&STATE(polling_alarm), NULL, do_polling_alarm); + add_alarm(&STATE(polling_alarm), CONFIG(poll_kernel_secs), 0); + dlog(LOG_NOTICE, "running in polling mode"); + } else { + init_alarm(&STATE(resync_alarm), NULL, do_overrun_resync_alarm); + /* + * The last nfct handler that we register is the event handler. + * The reason to do this is that we may receive events while + * populating the internal cache. Thus, we hit ENOBUFS + * prematurely. However, if we open the event handler before + * populating the internal cache, we may still lose events + * that have occured during the population. + */ + STATE(event) = nl_init_event_handler(); + if (STATE(event) == NULL) { + dlog(LOG_ERR, "can't open netlink handler: %s", + strerror(errno)); + dlog(LOG_ERR, "no ctnetlink kernel support?"); + return -1; + } + nfct_callback_register2(STATE(event), NFCT_T_ALL, + event_handler, NULL); + + if (CONFIG(flags) & CTD_EXPECT) { + nfexp_callback_register2(STATE(event), NFCT_T_ALL, + exp_event_handler, NULL); + } + register_fd(nfct_fd(STATE(event)), event_cb, NULL, STATE(fds)); + } + + return 0; +} diff --git a/src/run.c b/src/run.c index f94e853..852bec6 100644 --- a/src/run.c +++ b/src/run.c @@ -45,24 +45,12 @@ void killer(int foo) /* no signals while handling signals */ sigprocmask(SIG_BLOCK, &STATE(block), NULL); - if (!(CONFIG(flags) & CTD_POLL)) - nfct_close(STATE(event)); - - nfct_close(STATE(resync)); - nfct_close(STATE(get)); - origin_unregister(STATE(flush)); - nfct_close(STATE(flush)); - - if (STATE(us_filter)) - ct_filter_destroy(STATE(us_filter)); local_server_destroy(&STATE(local)); - STATE(mode)->kill(); - if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { - nfct_close(STATE(dump)); - } - destroy_fds(STATE(fds)); + if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) + ctnl_kill(); + destroy_fds(STATE(fds)); unlink(CONFIG(lockfile)); dlog(LOG_NOTICE, "---- shutdown received ----"); close_log(); @@ -187,62 +175,6 @@ static void dump_stats_runtime(int fd) send(fd, buf, size, 0); } -static void local_flush_master(void) -{ - STATE(stats).nl_kernel_table_flush++; - dlog(LOG_NOTICE, "flushing kernel conntrack table"); - - /* fork a child process that performs the flush operation, - * meanwhile the parent process handles events. */ - if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL, - NULL, NULL) == 0) { - nl_flush_conntrack_table_selective(); - exit(EXIT_SUCCESS); - } -} - -static void local_resync_master(void) -{ - if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { - STATE(stats).nl_kernel_table_resync++; - dlog(LOG_NOTICE, "resync with master conntrack table"); - nl_dump_conntrack_table(STATE(dump)); - } else { - dlog(LOG_NOTICE, "resync is unsupported in this mode"); - } -} - -static void local_exp_flush_master(void) -{ - if (!(CONFIG(flags) & CTD_EXPECT)) - return; - - STATE(stats).nl_kernel_table_flush++; - dlog(LOG_NOTICE, "flushing kernel expect table"); - - /* fork a child process that performs the flush operation, - * meanwhile the parent process handles events. */ - if (fork_process_new(CTD_PROC_FLUSH, CTD_PROC_F_EXCL, - NULL, NULL) == 0) { - nl_flush_expect_table(STATE(flush)); - exit(EXIT_SUCCESS); - } -} - -static void local_exp_resync_master(void) -{ - if (!(CONFIG(flags) & CTD_EXPECT)) - return; - - if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { - STATE(stats).nl_kernel_table_resync++; - dlog(LOG_NOTICE, "resync with master expect table"); - nl_dump_expect_table(STATE(dump)); - } else { - dlog(LOG_NOTICE, "resync is unsupported in this mode"); - } -} - static int local_handler(int fd, void *data) { int ret = LOCAL_RET_OK; @@ -253,26 +185,9 @@ static int local_handler(int fd, void *data) return LOCAL_RET_OK; } switch(type) { - case CT_FLUSH_MASTER: - local_flush_master(); - break; - case CT_RESYNC_MASTER: - local_resync_master(); - break; - case EXP_FLUSH_MASTER: - local_exp_flush_master(); - break; - case EXP_RESYNC_MASTER: - local_exp_resync_master(); - break; - case ALL_FLUSH_MASTER: - local_flush_master(); - local_exp_flush_master(); - break; - case ALL_RESYNC_MASTER: - local_resync_master(); - local_exp_resync_master(); - break; + case KILL: + killer(0); + break; case STATS_RUNTIME: dump_stats_runtime(fd); break; @@ -281,183 +196,10 @@ static int local_handler(int fd, void *data) break; } - ret = STATE(mode)->local(fd, type, data); - if (ret == LOCAL_RET_ERROR) { - STATE(stats).local_unknown_request++; - return LOCAL_RET_ERROR; - } - return ret; -} - -static void do_overrun_resync_alarm(struct alarm_block *a, void *data) -{ - nl_send_resync(STATE(resync)); - STATE(stats).nl_kernel_table_resync++; -} - -static void do_polling_alarm(struct alarm_block *a, void *data) -{ - if (STATE(mode)->internal->ct.purge) - STATE(mode)->internal->ct.purge(); - - if (STATE(mode)->internal->exp.purge) - STATE(mode)->internal->exp.purge(); - - nl_send_resync(STATE(resync)); - nl_send_expect_resync(STATE(resync)); - add_alarm(&STATE(polling_alarm), CONFIG(poll_kernel_secs), 0); -} - -static int event_handler(const struct nlmsghdr *nlh, - enum nf_conntrack_msg_type type, - struct nf_conntrack *ct, - void *data) -{ - int origin_type; - - STATE(stats).nl_events_received++; - - /* skip user-space filtering if already do it in the kernel */ - if (ct_filter_conntrack(ct, !CONFIG(filter_from_kernelspace))) { - STATE(stats).nl_events_filtered++; - goto out; - } - - origin_type = origin_find(nlh); - - switch(type) { - case NFCT_T_NEW: - STATE(mode)->internal->ct.new(ct, origin_type); - break; - case NFCT_T_UPDATE: - STATE(mode)->internal->ct.upd(ct, origin_type); - break; - case NFCT_T_DESTROY: - if (STATE(mode)->internal->ct.del(ct, origin_type)) - update_traffic_stats(ct); - break; - default: - STATE(stats).nl_events_unknown_type++; - break; - } - -out: - /* we reset the iteration limiter in the main select loop. */ - if (STATE(event_iterations_limit)-- <= 0) - return NFCT_CB_STOP; - else - return NFCT_CB_CONTINUE; -} - -static int exp_event_handler(const struct nlmsghdr *nlh, - enum nf_conntrack_msg_type type, - struct nf_expect *exp, - void *data) -{ - int origin_type; - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); - - STATE(stats).nl_events_received++; - - if (!exp_filter_find(STATE(exp_filter), exp)) { - STATE(stats).nl_events_filtered++; - goto out; - } - if (ct_filter_conntrack(master, 1)) - return NFCT_CB_CONTINUE; - - origin_type = origin_find(nlh); - - switch(type) { - case NFCT_T_NEW: - STATE(mode)->internal->exp.new(exp, origin_type); - break; - case NFCT_T_UPDATE: - STATE(mode)->internal->exp.upd(exp, origin_type); - break; - case NFCT_T_DESTROY: - STATE(mode)->internal->exp.del(exp, origin_type); - break; - default: - STATE(stats).nl_events_unknown_type++; - break; - } - -out: - /* we reset the iteration limiter in the main select loop. */ - if (STATE(event_iterations_limit)-- <= 0) - return NFCT_CB_STOP; - else - return NFCT_CB_CONTINUE; -} + if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) + return ctnl_local(fd, type, data); -static int dump_handler(enum nf_conntrack_msg_type type, - struct nf_conntrack *ct, - void *data) -{ - if (ct_filter_conntrack(ct, 1)) - return NFCT_CB_CONTINUE; - - switch(type) { - case NFCT_T_UPDATE: - STATE(mode)->internal->ct.populate(ct); - break; - default: - STATE(stats).nl_dump_unknown_type++; - break; - } - return NFCT_CB_CONTINUE; -} - -static int exp_dump_handler(enum nf_conntrack_msg_type type, - struct nf_expect *exp, void *data) -{ - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); - - if (!exp_filter_find(STATE(exp_filter), exp)) - return NFCT_CB_CONTINUE; - - if (ct_filter_conntrack(master, 1)) - return NFCT_CB_CONTINUE; - - switch(type) { - case NFCT_T_UPDATE: - STATE(mode)->internal->exp.populate(exp); - break; - default: - STATE(stats).nl_dump_unknown_type++; - break; - } - return NFCT_CB_CONTINUE; -} - -static int get_handler(enum nf_conntrack_msg_type type, - struct nf_conntrack *ct, - void *data) -{ - if (ct_filter_conntrack(ct, 1)) - return NFCT_CB_CONTINUE; - - STATE(get_retval) = 1; - return NFCT_CB_CONTINUE; -} - -static int exp_get_handler(enum nf_conntrack_msg_type type, - struct nf_expect *exp, void *data) -{ - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); - - if (!exp_filter_find(STATE(exp_filter), exp)) - return NFCT_CB_CONTINUE; - - if (ct_filter_conntrack(master, 1)) - return NFCT_CB_CONTINUE; - - STATE(get_retval) = 1; - return NFCT_CB_CONTINUE; + return ret; } /* order received via UNIX socket */ @@ -466,109 +208,17 @@ static void local_cb(void *data) do_local_server_step(&STATE(local), NULL, local_handler); } -/* we have received an event from ctnetlink */ -static void event_cb(void *data) -{ - int ret; - - ret = nfct_catch(STATE(event)); - /* reset event iteration limit counter */ - STATE(event_iterations_limit) = CONFIG(event_iterations_limit); - if (ret == -1) { - switch(errno) { - case ENOBUFS: - /* We have hit ENOBUFS, it's likely that we are - * losing events. Two possible situations may - * trigger this error: - * - * 1) The netlink receiver buffer is too small: - * increasing the netlink buffer size should - * be enough. However, some event messages - * got lost. We have to resync ourselves - * with the kernel table conntrack table to - * resolve the inconsistency. - * - * 2) The receiver is too slow to process the - * netlink messages so that the queue gets - * full quickly. This generally happens - * if the system is under heavy workload - * (busy CPU). In this case, increasing the - * size of the netlink receiver buffer - * would not help anymore since we would - * be delaying the overrun. Moreover, we - * should avoid resynchronizations. We - * should do our best here and keep - * replicating as much states as possible. - * If workload lowers at some point, - * we resync ourselves. - */ - nl_resize_socket_buffer(STATE(event)); - if (CONFIG(nl_overrun_resync) > 0 && - STATE(mode)->internal->flags & INTERNAL_F_RESYNC) { - add_alarm(&STATE(resync_alarm), - CONFIG(nl_overrun_resync),0); - } - STATE(stats).nl_catch_event_failed++; - STATE(stats).nl_overrun++; - break; - case ENOENT: - /* - * We received a message from another - * netfilter subsystem that we are not - * interested in. Just ignore it. - */ - break; - case EAGAIN: - /* No more events to receive, try later. */ - break; - default: - STATE(stats).nl_catch_event_failed++; - break; - } - } -} - -/* we previously requested a resync due to buffer overrun. */ -static void resync_cb(void *data) -{ - nfct_catch(STATE(resync)); - if (STATE(mode)->internal->ct.purge) - STATE(mode)->internal->ct.purge(); -} - -static void poll_cb(void *data) -{ - nfct_catch(STATE(resync)); -} - int init(void) { do_gettimeofday(); - if (CONFIG(flags) & CTD_STATS_MODE) - STATE(mode) = &stats_mode; - else if (CONFIG(flags) & CTD_SYNC_MODE) - STATE(mode) = &sync_mode; - else { - fprintf(stderr, "WARNING: No running mode specified. " - "Defaulting to statistics mode.\n"); - CONFIG(flags) |= CTD_STATS_MODE; - STATE(mode) = &stats_mode; - } - STATE(fds) = create_fds(); if (STATE(fds) == NULL) { dlog(LOG_ERR, "can't create file descriptor pool"); return -1; } - /* Initialization */ - if (STATE(mode)->init() == -1) { - dlog(LOG_ERR, "initialization failed"); - return -1; - } - /* local UNIX socket */ if (local_server_create(&STATE(local), &CONFIG(local)) == -1) { dlog(LOG_ERR, "can't open unix socket!"); @@ -576,110 +226,6 @@ init(void) } register_fd(STATE(local).fd, local_cb, NULL, STATE(fds)); - /* resynchronize (like 'dump' socket) but it also purges old entries */ - STATE(resync) = nfct_open(CONFIG(netlink).subsys_id, 0); - if (STATE(resync)== NULL) { - dlog(LOG_ERR, "can't open netlink handler: %s", - strerror(errno)); - dlog(LOG_ERR, "no ctnetlink kernel support?"); - return -1; - } - nfct_callback_register(STATE(resync), - NFCT_T_ALL, - STATE(mode)->internal->ct.resync, - NULL); - if (CONFIG(flags) & CTD_POLL) { - register_fd(nfct_fd(STATE(resync)), poll_cb, - NULL, STATE(fds)); - } else { - register_fd(nfct_fd(STATE(resync)), resync_cb, - NULL, STATE(fds)); - } - fcntl(nfct_fd(STATE(resync)), F_SETFL, O_NONBLOCK); - - if (STATE(mode)->internal->flags & INTERNAL_F_POPULATE) { - STATE(dump) = nfct_open(CONFIG(netlink).subsys_id, 0); - if (STATE(dump) == NULL) { - dlog(LOG_ERR, "can't open netlink handler: %s", - strerror(errno)); - dlog(LOG_ERR, "no ctnetlink kernel support?"); - return -1; - } - nfct_callback_register(STATE(dump), NFCT_T_ALL, - dump_handler, NULL); - - if (CONFIG(flags) & CTD_EXPECT) { - nfexp_callback_register(STATE(dump), NFCT_T_ALL, - exp_dump_handler, NULL); - } - - if (nl_dump_conntrack_table(STATE(dump)) == -1) { - dlog(LOG_ERR, "can't get kernel conntrack table"); - return -1; - } - - if (CONFIG(flags) & CTD_EXPECT) { - if (nl_dump_expect_table(STATE(dump)) == -1) { - dlog(LOG_ERR, "can't get kernel " - "expect table"); - return -1; - } - } - } - - STATE(get) = nfct_open(CONFIG(netlink).subsys_id, 0); - if (STATE(get) == NULL) { - dlog(LOG_ERR, "can't open netlink handler: %s", - strerror(errno)); - dlog(LOG_ERR, "no ctnetlink kernel support?"); - return -1; - } - nfct_callback_register(STATE(get), NFCT_T_ALL, get_handler, NULL); - - if (CONFIG(flags) & CTD_EXPECT) { - nfexp_callback_register(STATE(get), NFCT_T_ALL, - exp_get_handler, NULL); - } - - STATE(flush) = nfct_open(CONFIG(netlink).subsys_id, 0); - if (STATE(flush) == NULL) { - dlog(LOG_ERR, "cannot open flusher handler"); - return -1; - } - /* register this handler as the origin of a flush operation */ - origin_register(STATE(flush), CTD_ORIGIN_FLUSH); - - if (CONFIG(flags) & CTD_POLL) { - init_alarm(&STATE(polling_alarm), NULL, do_polling_alarm); - add_alarm(&STATE(polling_alarm), CONFIG(poll_kernel_secs), 0); - dlog(LOG_NOTICE, "running in polling mode"); - } else { - init_alarm(&STATE(resync_alarm), NULL, do_overrun_resync_alarm); - /* - * The last nfct handler that we register is the event handler. - * The reason to do this is that we may receive events while - * populating the internal cache. Thus, we hit ENOBUFS - * prematurely. However, if we open the event handler before - * populating the internal cache, we may still lose events - * that have occured during the population. - */ - STATE(event) = nl_init_event_handler(); - if (STATE(event) == NULL) { - dlog(LOG_ERR, "can't open netlink handler: %s", - strerror(errno)); - dlog(LOG_ERR, "no ctnetlink kernel support?"); - return -1; - } - nfct_callback_register2(STATE(event), NFCT_T_ALL, - event_handler, NULL); - - if (CONFIG(flags) & CTD_EXPECT) { - nfexp_callback_register2(STATE(event), NFCT_T_ALL, - exp_event_handler, NULL); - } - register_fd(nfct_fd(STATE(event)), event_cb, NULL, STATE(fds)); - } - /* Signals handling */ sigemptyset(&STATE(block)); sigaddset(&STATE(block), SIGTERM); @@ -699,6 +245,11 @@ init(void) if (signal(SIGCHLD, child) == SIG_ERR) return -1; + /* Initialization */ + if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) + if (ctnl_init() < 0) + return -1; + time(&STATE(stats).daemon_start_time); dlog(LOG_NOTICE, "initialization completed"); diff --git a/src/sync-mode.c b/src/sync-mode.c index be6366d..e69ecfe 100644 --- a/src/sync-mode.c +++ b/src/sync-mode.c @@ -641,9 +641,6 @@ static int local_handler_sync(int fd, int type, void *data) dlog(LOG_NOTICE, "flushing external cache"); STATE_SYNC(external)->ct.flush(); break; - case KILL: - killer(0); - break; case STATS: STATE(mode)->internal->ct.stats(fd); STATE_SYNC(external)->ct.stats(fd); -- cgit v1.2.3 From 5e8f64f46cb1dd71b0a94cb7dad87da00b8c5e32 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 15 May 2012 01:51:29 +0200 Subject: conntrackd: add cthelper infrastructure (+ example FTP helper) This patch adds the user-space helper infrastructure. It also contains the implementation of the FTP helper in user-space. There's one example file that you can use to configure conntrackd as user-space connection tracking helper under: doc/helper/conntrackd.conf Signed-off-by: Pablo Neira Ayuso --- configure.ac | 9 +- doc/helper/conntrackd.conf | 82 ++++ include/Makefile.am | 3 +- include/conntrackd.h | 21 +- include/helper.h | 104 +++++ include/linux/netfilter/Makefile.am | 2 +- include/linux/netfilter/nfnetlink_cthelper.h | 55 +++ include/linux/netfilter/nfnetlink_queue.h | 99 +++++ include/myct.h | 43 ++ include/nfct.h | 10 + include/stack.h | 28 ++ src/Makefile.am | 24 +- src/cthelper.c | 521 ++++++++++++++++++++++ src/expect.c | 214 +++++++++ src/helpers.c | 76 ++++ src/helpers/Makefile.am | 9 + src/helpers/ftp.c | 599 ++++++++++++++++++++++++++ src/main.c | 3 +- src/nfct-extensions/helper.c | 619 +++++++++++++++++++++++++++ src/nfct.c | 6 + src/read_config_lex.l | 5 + src/read_config_yy.y | 200 +++++++++ src/run.c | 11 + src/stack.c | 56 +++ src/utils.c | 243 +++++++++++ 25 files changed, 3030 insertions(+), 12 deletions(-) create mode 100644 doc/helper/conntrackd.conf create mode 100644 include/helper.h create mode 100644 include/linux/netfilter/nfnetlink_cthelper.h create mode 100644 include/linux/netfilter/nfnetlink_queue.h create mode 100644 include/myct.h create mode 100644 include/stack.h create mode 100644 src/cthelper.c create mode 100644 src/expect.c create mode 100644 src/helpers.c create mode 100644 src/helpers/Makefile.am create mode 100644 src/helpers/ftp.c create mode 100644 src/nfct-extensions/helper.c create mode 100644 src/stack.c create mode 100644 src/utils.c diff --git a/configure.ac b/configure.ac index 8cd5626..5d1860d 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,9 @@ AM_INIT_AUTOMAKE([-Wall foreign subdir-objects dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AC_SEARCH_LIBS([dlopen], [dl], [libdl_LIBS="$LIBS"; LIBS=""]) +AC_SUBST([libdl_LIBS]) + AC_PROG_CC AC_DISABLE_STATIC AM_PROG_LIBTOOL @@ -52,9 +55,11 @@ else fi PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.0]) -PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.0]) +PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3]) PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.1]) PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) +PKG_CHECK_MODULES([LIBNETFILTER_CTHELPER], [libnetfilter_cthelper >= 1.0.0]) +PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.0]) AC_CHECK_HEADERS([linux/capability.h],, [AC_MSG_ERROR([Cannot find linux/capabibility.h])]) @@ -114,5 +119,5 @@ dnl debug/src/Makefile dnl extensions/Makefile dnl src/Makefile]) -AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile]) +AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile src/helpers/Makefile]) AC_OUTPUT diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf new file mode 100644 index 0000000..711b309 --- /dev/null +++ b/doc/helper/conntrackd.conf @@ -0,0 +1,82 @@ +# +# Helper settings +# + +Helper { + # Before this, you have to make sure you have registered the `ftp' + # user-space helper stub via: + # + # nfct helper add ftp inet tcp + # + Type ftp inet tcp { + # + # Set NFQUEUE number you want to use to receive traffic from + # the kernel. + # + QueueNum 0 + # + # Set the Expectation policy for this helper. + # + Policy ftp { + # + # Maximum number of simultaneous expectations + # + ExpectMax 1 + # + # Maximum living time for one expectation (in seconds). + # + ExpectTimeout 300 + } + } +} + +# +# General settings +# +General { + # + # Set the nice value of the daemon, this value goes from -20 + # (most favorable scheduling) to 19 (least favorable). Using a + # very low value reduces the chances to lose state-change events. + # Default is 0 but this example file sets it to most favourable + # scheduling as this is generally a good idea. See man nice(1) for + # more information. + # + Nice -20 + + # + # Select a different scheduler for the daemon, you can select between + # RR and FIFO and the process priority (minimum is 0, maximum is 99). + # See man sched_setscheduler(2) for more information. Using a RT + # scheduler reduces the chances to overrun the Netlink buffer. + # + # Scheduler { + # Type FIFO + # Priority 99 + # } + + # + # Logfile: on (/var/log/conntrackd.log), off, or a filename + # Default: off + # + LogFile on + + # + # Syslog: on, off or a facility name (daemon (default) or local0..7) + # Default: off + # + #Syslog on + + # + # Lockfile + # + LockFile /var/lock/conntrack.lock + + # + # Unix socket configuration + # + UNIX { + Path /var/run/conntrackd.ctl + Backlog 20 + } +} diff --git a/include/Makefile.am b/include/Makefile.am index 138005d..6bd0f7f 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -5,5 +5,6 @@ noinst_HEADERS = alarm.h jhash.h cache.h linux_list.h linux_rbtree.h \ debug.h log.h hash.h mcast.h conntrack.h \ network.h filter.h queue.h vector.h cidr.h \ traffic_stats.h netlink.h fds.h event.h bitops.h channel.h \ - process.h origin.h internal.h external.h date.h nfct.h + process.h origin.h internal.h external.h date.h nfct.h \ + helper.h myct.h stack.h diff --git a/include/conntrackd.h b/include/conntrackd.h index ec720ec..19e613c 100644 --- a/include/conntrackd.h +++ b/include/conntrackd.h @@ -69,6 +69,7 @@ #define CTD_SYNC_NOTRACK (1UL << 4) #define CTD_POLL (1UL << 5) #define CTD_EXPECT (1UL << 6) +#define CTD_HELPER (1UL << 7) /* FILENAME_MAX is 4096 on my system, perhaps too much? */ #ifndef FILENAME_MAXLEN @@ -134,6 +135,9 @@ struct ct_conf { int syslog_facility; size_t buffer_size; } stats; + struct { + struct list_head list; + } cthelper; }; #define STATE(x) st.x @@ -252,13 +256,21 @@ struct ct_stats_state { struct cache *cache; /* internal events cache (netlink) */ }; -union ct_state { +#define STATE_CTH(x) state.cthelper->x + +struct ct_helper_state { + struct mnl_socket *nl; + uint32_t portid; +}; + +struct ct_state { struct ct_sync_state *sync; struct ct_stats_state *stats; + struct ct_helper_state *cthelper; }; extern struct ct_conf conf; -extern union ct_state state; +extern struct ct_state state; extern struct ct_general_state st; struct ct_mode { @@ -273,6 +285,11 @@ void ctnl_kill(void); int ctnl_local(int fd, int type, void *data); int ctnl_init(void); +/* basic cthelper functions */ +void cthelper_kill(void); +int cthelper_local(int fd, int type, void *data); +int cthelper_init(void); + /* conntrackd ctnl modes */ extern struct ct_mode sync_mode; extern struct ct_mode stats_mode; diff --git a/include/helper.h b/include/helper.h new file mode 100644 index 0000000..02ff3df --- /dev/null +++ b/include/helper.h @@ -0,0 +1,104 @@ +#ifndef _CTD_HELPER_H_ +#define _CTD_HELPER_H_ + +#include +#include "linux_list.h" +#include "myct.h" + +#include + +struct pkt_buff; + +#define CTD_HELPER_NAME_LEN 16 +#define CTD_HELPER_POLICY_MAX 4 + +struct ctd_helper_policy { + char name[CTD_HELPER_NAME_LEN]; + uint32_t expect_timeout; + uint32_t expect_max; +}; + +struct ctd_helper { + struct list_head head; + char name[CTD_HELPER_NAME_LEN]; + uint8_t l4proto; + int (*cb)(struct pkt_buff *pkt, + uint32_t protoff, + struct myct *ct, + u_int32_t ctinfo); + + struct ctd_helper_policy policy[CTD_HELPER_POLICY_MAX]; + + int priv_data_len; +}; + +struct ctd_helper_instance { + struct list_head head; + uint32_t queue_num; + uint16_t l3proto; + uint8_t l4proto; + struct ctd_helper *helper; + struct ctd_helper_policy policy[CTD_HELPER_POLICY_MAX]; +}; + +extern int cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, uint32_t class, union nfct_attr_grp_addr *saddr, union nfct_attr_grp_addr *daddr, uint8_t l4proto, uint16_t *sport, uint16_t *dport, uint32_t flags); +extern int cthelper_add_expect(struct nf_expect *exp); +extern int cthelper_del_expect(struct nf_expect *exp); + +extern void cthelper_get_addr_src(struct nf_conntrack *ct, int dir, union nfct_attr_grp_addr *addr); +extern void cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, union nfct_attr_grp_addr *addr); + +extern int in4_pton(const char *src, int srclen, uint8_t *dst, int delim, const char **end); +extern int in6_pton(const char *src, int srclen, uint8_t *dst, int delim, const char **end); + +extern void helper_register(struct ctd_helper *helper); +struct ctd_helper *helper_find(const char *libdir_path, const char *name, uint8_t l4proto, int flags); + +#define min_t(type, x, y) ({ \ + type __min1 = (x); \ + type __min2 = (y); \ + __min1 < __min2 ? __min1: __min2; }) + +#define max_t(type, x, y) ({ \ + type __max1 = (x); \ + type __max2 = (y); \ + __max1 > __max2 ? __max1: __max2; }) + +#define ARRAY_SIZE MNL_ARRAY_SIZE + +enum ip_conntrack_dir { + IP_CT_DIR_ORIGINAL, + IP_CT_DIR_REPLY, + IP_CT_DIR_MAX +}; + +/* Connection state tracking for netfilter. This is separated from, + but required by, the NAT layer; it can also be used by an iptables + extension. */ +enum ip_conntrack_info { + /* Part of an established connection (either direction). */ + IP_CT_ESTABLISHED, + + /* Like NEW, but related to an existing connection, or ICMP error + (in either direction). */ + IP_CT_RELATED, + + /* Started a new connection to track (only + IP_CT_DIR_ORIGINAL); may be a retransmission. */ + IP_CT_NEW, + + /* >= this indicates reply direction */ + IP_CT_IS_REPLY, + + IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY, + IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY, + IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY, + /* Number of distinct IP_CT types (no NEW in reply dirn). */ + IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 +}; + +#define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) + +#define pr_debug printf + +#endif diff --git a/include/linux/netfilter/Makefile.am b/include/linux/netfilter/Makefile.am index 84315e3..6574060 100644 --- a/include/linux/netfilter/Makefile.am +++ b/include/linux/netfilter/Makefile.am @@ -1 +1 @@ -noinst_HEADERS = nfnetlink.h nfnetlink_cttimeout.h +noinst_HEADERS = nfnetlink.h nfnetlink_cttimeout.h nfnetlink_queue.h nfnetlink_cthelper.h diff --git a/include/linux/netfilter/nfnetlink_cthelper.h b/include/linux/netfilter/nfnetlink_cthelper.h new file mode 100644 index 0000000..33659f6 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_cthelper.h @@ -0,0 +1,55 @@ +#ifndef _NFNL_CTHELPER_H_ +#define _NFNL_CTHELPER_H_ + +#define NFCT_HELPER_STATUS_DISABLED 0 +#define NFCT_HELPER_STATUS_ENABLED 1 + +enum nfnl_acct_msg_types { + NFNL_MSG_CTHELPER_NEW, + NFNL_MSG_CTHELPER_GET, + NFNL_MSG_CTHELPER_DEL, + NFNL_MSG_CTHELPER_MAX +}; + +enum nfnl_cthelper_type { + NFCTH_UNSPEC, + NFCTH_NAME, + NFCTH_TUPLE, + NFCTH_QUEUE_NUM, + NFCTH_POLICY, + NFCTH_PRIV_DATA_LEN, + NFCTH_STATUS, + __NFCTH_MAX +}; +#define NFCTH_MAX (__NFCTH_MAX - 1) + +enum nfnl_cthelper_policy_type { + NFCTH_POLICY_SET_UNSPEC, + NFCTH_POLICY_SET_NUM, + NFCTH_POLICY_SET, + NFCTH_POLICY_SET1 = NFCTH_POLICY_SET, + NFCTH_POLICY_SET2, + NFCTH_POLICY_SET3, + NFCTH_POLICY_SET4, + __NFCTH_POLICY_SET_MAX +}; +#define NFCTH_POLICY_SET_MAX (__NFCTH_POLICY_SET_MAX - 1) + +enum nfnl_cthelper_pol_type { + NFCTH_POLICY_UNSPEC, + NFCTH_POLICY_NAME, + NFCTH_POLICY_EXPECT_MAX, + NFCTH_POLICY_EXPECT_TIMEOUT, + __NFCTH_POLICY_MAX +}; +#define NFCTH_POLICY_MAX (__NFCTH_POLICY_MAX - 1) + +enum nfnl_cthelper_tuple_type { + NFCTH_TUPLE_UNSPEC, + NFCTH_TUPLE_L3PROTONUM, + NFCTH_TUPLE_L4PROTONUM, + __NFCTH_TUPLE_MAX, +}; +#define NFCTH_TUPLE_MAX (__NFCTH_TUPLE_MAX - 1) + +#endif /* _NFNL_CTHELPER_H */ diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h new file mode 100644 index 0000000..e0d8fd8 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -0,0 +1,99 @@ +#ifndef _NFNETLINK_QUEUE_H +#define _NFNETLINK_QUEUE_H + +#include +#include + +enum nfqnl_msg_types { + NFQNL_MSG_PACKET, /* packet from kernel to userspace */ + NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */ + NFQNL_MSG_CONFIG, /* connect to a particular queue */ + NFQNL_MSG_VERDICT_BATCH, /* batchv from userspace to kernel */ + + NFQNL_MSG_MAX +}; + +struct nfqnl_msg_packet_hdr { + __be32 packet_id; /* unique ID of packet in queue */ + __be16 hw_protocol; /* hw protocol (network order) */ + __u8 hook; /* netfilter hook */ +} __attribute__ ((packed)); + +struct nfqnl_msg_packet_hw { + __be16 hw_addrlen; + __u16 _pad; + __u8 hw_addr[8]; +}; + +struct nfqnl_msg_packet_timestamp { + __aligned_be64 sec; + __aligned_be64 usec; +}; + +enum nfqnl_attr_type { + NFQA_UNSPEC, + NFQA_PACKET_HDR, + NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */ + NFQA_MARK, /* __u32 nfmark */ + NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */ + NFQA_IFINDEX_INDEV, /* __u32 ifindex */ + NFQA_IFINDEX_OUTDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSINDEV, /* __u32 ifindex */ + NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */ + NFQA_HWADDR, /* nfqnl_msg_packet_hw */ + NFQA_PAYLOAD, /* opaque data payload */ + NFQA_CT, /* nf_conntrack_netlink.h */ + NFQA_CT_INFO, /* enum ip_conntrack_info */ + + __NFQA_MAX +}; +#define NFQA_MAX (__NFQA_MAX - 1) + +struct nfqnl_msg_verdict_hdr { + __be32 verdict; + __be32 id; +}; + + +enum nfqnl_msg_config_cmds { + NFQNL_CFG_CMD_NONE, + NFQNL_CFG_CMD_BIND, + NFQNL_CFG_CMD_UNBIND, + NFQNL_CFG_CMD_PF_BIND, + NFQNL_CFG_CMD_PF_UNBIND, +}; + +struct nfqnl_msg_config_cmd { + __u8 command; /* nfqnl_msg_config_cmds */ + __u8 _pad; + __be16 pf; /* AF_xxx for PF_[UN]BIND */ +}; + +enum nfqnl_config_mode { + NFQNL_COPY_NONE, + NFQNL_COPY_META, + NFQNL_COPY_PACKET, +}; + +struct nfqnl_msg_config_params { + __be32 copy_range; + __u8 copy_mode; /* enum nfqnl_config_mode */ +} __attribute__ ((packed)); + + +enum nfqnl_attr_config { + NFQA_CFG_UNSPEC, + NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */ + NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ + NFQA_CFG_QUEUE_MAXLEN, /* __u32 */ + NFQA_CFG_MASK, /* identify which flags to change */ + NFQA_CFG_FLAGS, /* value of these flags (__u32) */ + __NFQA_CFG_MAX +}; +#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1) + +/* Flags for NFQA_CFG_FLAGS */ +#define NFQA_CFG_F_FAIL_OPEN (1 << 0) +#define NFQA_CFG_F_CONNTRACK (1 << 1) + +#endif /* _NFNETLINK_QUEUE_H */ diff --git a/include/myct.h b/include/myct.h new file mode 100644 index 0000000..45d9f29 --- /dev/null +++ b/include/myct.h @@ -0,0 +1,43 @@ +#ifndef _MYCT_H_ +#define _MYCT_H_ + +#include "linux_list.h" + +#include + +struct nf_conntrack; + +enum { + MYCT_NONE = 0, + MYCT_ESTABLISHED = (1 << 0), +}; + +enum { + MYCT_DIR_ORIG = 0, + MYCT_DIR_REPL, + MYCT_DIR_MAX, +}; + +union myct_proto { + uint16_t port; + uint16_t all; +}; + +struct myct_man { + union nfct_attr_grp_addr u3; + union myct_proto u; + uint16_t l3num; + uint8_t protonum; +}; + +struct myct_tuple { + struct myct_man src; + struct myct_man dst; +}; + +struct myct { + struct nf_conntrack *ct; + void *priv_data; +}; + +#endif diff --git a/include/nfct.h b/include/nfct.h index d6271cf..5548b03 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -4,6 +4,7 @@ enum { NFCT_SUBSYS_NONE = 0, NFCT_SUBSYS_TIMEOUT, + NFCT_SUBSYS_HELPER, NFCT_SUBSYS_VERSION, NFCT_SUBSYS_HELP, }; @@ -15,6 +16,7 @@ enum { NFCT_CMD_DELETE, NFCT_CMD_GET, NFCT_CMD_FLUSH, + NFCT_CMD_DISABLE, }; void nfct_perror(const char *msg); @@ -26,4 +28,12 @@ int nfct_cmd_timeout_delete(int argc, char *argv[]); int nfct_cmd_timeout_get(int argc, char *argv[]); int nfct_cmd_timeout_flush(int argc, char *argv[]); +int nfct_cmd_helper_parse_params(int argc, char *argv[]); +int nfct_cmd_helper_list(int argc, char *argv[]); +int nfct_cmd_helper_add(int argc, char *argv[]); +int nfct_cmd_helper_delete(int argc, char *argv[]); +int nfct_cmd_helper_get(int argc, char *argv[]); +int nfct_cmd_helper_flush(int argc, char *argv[]); +int nfct_cmd_helper_disable(int argc, char *argv[]); + #endif diff --git a/include/stack.h b/include/stack.h new file mode 100644 index 0000000..512a30f --- /dev/null +++ b/include/stack.h @@ -0,0 +1,28 @@ +#ifndef _STACK_H_ +#define _STACK_H_ + +#include "linux_list.h" + +struct stack { + struct list_head list; + int items; +}; + +static inline void stack_init(struct stack *s) +{ + INIT_LIST_HEAD(&s->list); +} + +struct stack_item { + struct list_head head; + int type; + int data_len; + char data[0]; +}; + +struct stack_item *stack_item_alloc(int type, size_t data_len); +void stack_item_free(struct stack_item *e); +void stack_item_push(struct stack *s, struct stack_item *e); +struct stack_item *stack_item_pop(struct stack *s, int type); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index fa4ab75..d8074d2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,7 @@ include $(top_srcdir)/Make_global.am +SUBDIRS = helpers + AM_YFLAGS = -d CLEANFILES = read_config_yy.c read_config_lex.c @@ -10,17 +12,24 @@ 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} nfct_SOURCES = nfct.c \ - nfct-extensions/timeout.c + helpers.c \ + nfct-extensions/timeout.c \ + nfct-extensions/helper.c + nfct_LDADD = ${LIBMNL_LIBS} \ ${LIBNETFILTER_CONNTRACK_LIBS} \ - ${LIBNETFILTER_CTTIMEOUT_LIBS} + ${LIBNETFILTER_CTTIMEOUT_LIBS} \ + ${LIBNETFILTER_CTHELPER_LIBS} \ + ${libdl_LIBS} + +nfct_LDFLAGS = -export-dynamic conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ local.c log.c mcast.c udp.c netlink.c vector.c \ filter.c fds.c event.c process.c origin.c date.c \ cache.c cache-ct.c cache-exp.c \ cache_timer.c \ - ctnl.c \ + ctnl.c cthelper.c \ sync-mode.c sync-alarm.c sync-ftfw.c sync-notrack.c \ traffic_stats.c stats-mode.c \ network.c cidr.c \ @@ -29,11 +38,16 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c rbtree.c \ tcp.c channel_tcp.c \ external_cache.c external_inject.c \ internal_cache.c internal_bypass.c \ - read_config_yy.y read_config_lex.l + read_config_yy.y read_config_lex.l \ + stack.c helpers.c utils.c expect.c # 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 = ${LIBNETFILTER_CONNTRACK_LIBS} +conntrackd_LDADD = ${LIBMNL_LIBS} ${LIBNETFILTER_CONNTRACK_LIBS} \ + ${LIBNETFILTER_QUEUE_LIBS} ${LIBNETFILTER_CTHELPER_LIBS} \ + ${libdl_LIBS} + +conntrackd_LDFLAGS = -export-dynamic EXTRA_DIST = read_config_yy.h diff --git a/src/cthelper.c b/src/cthelper.c new file mode 100644 index 0000000..c119869 --- /dev/null +++ b/src/cthelper.c @@ -0,0 +1,521 @@ +/* + * (C) 2006-2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "conntrackd.h" +#include "log.h" +#include "fds.h" +#include "helper.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifndef __aligned_be64 +#define __aligned_be64 unsigned long long __attribute__((aligned(8))) +#endif + +#include + +#include +#include +#include +#include +#include +#include + +void cthelper_kill(void) +{ + mnl_socket_close(STATE_CTH(nl)); + free(state.cthelper); +} + +int cthelper_local(int fd, int type, void *data) +{ + /* No services to obtain information on helpers yet, sorry */ + return LOCAL_RET_OK; +} + +static struct nlmsghdr * +nfq_hdr_put(char *buf, int type, uint32_t queue_num) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_QUEUE << 8) | type; + nlh->nlmsg_flags = NLM_F_REQUEST; + + struct nfgenmsg *nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(queue_num); + + return nlh; +} + +static int +pkt_get(void *pkt, uint32_t pktlen, uint16_t proto, uint32_t *protoff) +{ + switch(proto) { + case ETHERTYPE_IP: { + struct iphdr *ip = (struct iphdr *) pkt; + + /* No room for IPv4 header. */ + if (pktlen < sizeof(struct iphdr)) { + dlog(LOG_ERR, "no room for IPv4 header"); + return -1; + } + + /* this is not IPv4, skip. */ + if (ip->version != 4) { + dlog(LOG_ERR, "not IPv4, skipping"); + return -1; + } + + *protoff = 4 * ip->ihl; + + switch (ip->protocol) { + case IPPROTO_TCP: { + struct tcphdr *tcph = + (struct tcphdr *) ((char *)pkt + *protoff); + + /* No room for IPv4 header plus TCP header. */ + if (pktlen < *protoff + sizeof(struct tcphdr) + || pktlen < *protoff + tcph->doff * 4) { + dlog(LOG_ERR, "no room for IPv4 + TCP header, skip"); + return -1; + } + return 0; + } + case IPPROTO_UDP: + /* No room for IPv4 header plus UDP header. */ + if (pktlen < *protoff + sizeof(struct udphdr)) { + dlog(LOG_ERR, "no room for IPv4 + UDP header, skip"); + return -1; + } + return 0; + default: + dlog(LOG_ERR, "not TCP/UDP, skipping"); + return -1; + } + break; + } + case ETHERTYPE_IPV6: + dlog(LOG_ERR, "no IPv6 support sorry"); + return 0; + default: + /* Unknown layer 3 protocol. */ + dlog(LOG_ERR, "unknown layer 3 protocol (%d), skipping", proto); + return -1; + } + return 0; +} + +static int +pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct, + uint16_t queue_num, uint32_t id, uint32_t verdict, + struct pkt_buff *pktb) +{ + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlattr *nest; + + nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); + + /* save private data and send it back to kernel-space. */ + nfct_set_attr_l(myct->ct, ATTR_HELPER_INFO, myct->priv_data, + cur->helper->priv_data_len); + + nfq_nlmsg_verdict_put(nlh, id, verdict); + if (pktb_mangled(pktb)) + nfq_nlmsg_verdict_put_pkt(nlh, pktb_data(pktb), pktb_len(pktb)); + + nest = mnl_attr_nest_start(nlh, NFQA_CT); + if (nest == NULL) + return -1; + + nfct_nlmsg_build(nlh, myct->ct); + 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; + } + + return 0; +} + +static int +pkt_verdict_error(uint16_t queue_num, uint32_t id) +{ + struct nlmsghdr *nlh; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); + nfq_nlmsg_verdict_put(nlh, id, NF_ACCEPT); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send verdict: %s", strerror(errno)); + return -1; + } + return 0; +} + +static struct ctd_helper_instance * +helper_run(struct pkt_buff *pktb, uint32_t protoff, struct myct *myct, + uint32_t ctinfo, uint32_t queue_num, int *verdict) +{ + struct ctd_helper_instance *cur, *helper = NULL; + + list_for_each_entry(cur, &CONFIG(cthelper).list, head) { + if (cur->queue_num == queue_num) { + const void *priv_data; + + /* retrieve helper private data. */ + priv_data = nfct_get_attr(myct->ct, ATTR_HELPER_INFO); + if (priv_data != NULL) { + myct->priv_data = + calloc(1, cur->helper->priv_data_len); + + if (myct->priv_data == NULL) + continue; + + memcpy(myct->priv_data, priv_data, + cur->helper->priv_data_len); + } + + *verdict = cur->helper->cb(pktb, protoff, myct, ctinfo); + helper = cur; + break; + } + } + return helper; +} + +static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nfqnl_msg_packet_hdr *ph = NULL; + struct nlattr *attr[NFQA_MAX+1] = {}; + struct nfgenmsg *nfg; + uint8_t *pkt; + uint16_t l3num; + uint32_t id, ctinfo, queue_num = 0, protoff = 0, pktlen; + struct nf_conntrack *ct = NULL; + struct myct *myct; + struct ctd_helper_instance *helper; + struct pkt_buff *pktb; + int verdict = NF_ACCEPT; + + if (nfq_nlmsg_parse(nlh, attr) < 0) { + dlog(LOG_ERR, "problems parsing message from kernel"); + return MNL_CB_ERROR; + } + + ph = (struct nfqnl_msg_packet_hdr *) + mnl_attr_get_payload(attr[NFQA_PACKET_HDR]); + if (ph == NULL) { + dlog(LOG_ERR, "problems retrieving metaheader"); + return MNL_CB_ERROR; + } + + id = ntohl(ph->packet_id); + + if (!attr[NFQA_PAYLOAD]) { + dlog(LOG_ERR, "packet with no payload"); + goto err; + } + if (!attr[NFQA_CT] || !attr[NFQA_CT_INFO]) { + dlog(LOG_ERR, "no CT attached to this packet"); + goto err; + } + + pkt = mnl_attr_get_payload(attr[NFQA_PAYLOAD]); + pktlen = mnl_attr_get_payload_len(attr[NFQA_PAYLOAD]); + + nfg = mnl_nlmsg_get_payload(nlh); + l3num = nfg->nfgen_family; + queue_num = ntohs(nfg->res_id); + + if (pkt_get(pkt, pktlen, ntohs(ph->hw_protocol), &protoff)) + goto err; + + ct = nfct_new(); + if (ct == NULL) + goto err; + + 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; + } + + myct = calloc(1, sizeof(struct myct)); + if (myct == NULL) + goto err; + + myct->ct = ct; + ctinfo = ntohl(mnl_attr_get_u32(attr[NFQA_CT_INFO])); + + /* XXX: 256 bytes enough for possible NAT mangling in helpers? */ + pktb = pktb_alloc(AF_INET, pkt, pktlen, 256); + if (pktb == NULL) + goto err; + + /* Misconfiguration: if no helper found, accept the packet. */ + helper = helper_run(pktb, protoff, myct, ctinfo, queue_num, &verdict); + if (!helper) + goto err_pktb; + + if (pkt_verdict_issue(helper, myct, queue_num, id, verdict, pktb) < 0) + goto err_pktb; + + if (ct != NULL) + nfct_destroy(ct); + if (myct && myct->priv_data != NULL) + free(myct->priv_data); + if (myct != NULL) + free(myct); + + return MNL_CB_OK; +err_pktb: + pktb_free(pktb); +err: + /* 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; +} + +static void nfq_cb(void *data) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + ret = mnl_socket_recvfrom(STATE_CTH(nl), buf, sizeof(buf)); + if (ret == -1) { + dlog(LOG_ERR, "failed to receive message: %s", strerror(errno)); + return; + } + + ret = mnl_cb_run(buf, ret, 0, STATE_CTH(portid), nfq_queue_cb, NULL); + if (ret < 0){ + dlog(LOG_ERR, "failed to process message"); + return; + } +} + +static int cthelper_setup(struct ctd_helper_instance *cur) +{ + struct nfct_helper *t; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t seq; + int j, ret; + + t = nfct_helper_alloc(); + if (t == NULL) { + dlog(LOG_ERR, "cannot allocate object for helper"); + return -1; + } + + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, cur->helper->name); + nfct_helper_attr_set_u32(t, NFCTH_ATTR_QUEUE_NUM, cur->queue_num); + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, cur->l3proto); + nfct_helper_attr_set_u8(t, NFCTH_ATTR_PROTO_L4NUM, cur->l4proto); + nfct_helper_attr_set_u32(t, NFCTH_ATTR_STATUS, + NFCT_HELPER_STATUS_ENABLED); + + dlog(LOG_NOTICE, "configuring helper `%s' with queuenum=%d", + cur->helper->name, cur->queue_num); + + for (j=0; jhelper->policy[j].name[0]) + break; + + p = nfct_helper_policy_alloc(); + if (p == NULL) { + dlog(LOG_ERR, "cannot allocate object for helper"); + return -1; + } + /* FIXME: get existing policy values from the kernel first. */ + nfct_helper_policy_attr_set(p, NFCTH_ATTR_POLICY_NAME, + cur->helper->policy[j].name); + nfct_helper_policy_attr_set_u32(p, NFCTH_ATTR_POLICY_TIMEOUT, + cur->helper->policy[j].expect_timeout); + nfct_helper_policy_attr_set_u32(p, NFCTH_ATTR_POLICY_MAX, + cur->helper->policy[j].expect_max); + + dlog(LOG_NOTICE, "policy name=%s expect_timeout=%d expect_max=%d", + cur->helper->policy[j].name, + cur->helper->policy[j].expect_timeout, + cur->helper->policy[j].expect_max); + + nfct_helper_attr_set(t, NFCTH_ATTR_POLICY+j, p); + } + + if (j == 0) { + dlog(LOG_ERR, "you have to define one policy for helper"); + return -1; + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_NEW, + NLM_F_CREATE | NLM_F_ACK, seq); + nfct_helper_nlmsg_build_payload(nlh, t); + + nfct_helper_free(t); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "sending cthelper configuration"); + return -1; + } + + ret = mnl_socket_recvfrom(STATE_CTH(nl), buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, STATE_CTH(portid), NULL, NULL); + if (ret <= 0) + break; + ret = mnl_socket_recvfrom(STATE_CTH(nl), buf, sizeof(buf)); + } + if (ret == -1) { + dlog(LOG_ERR, "trying to configure cthelper `%s': %s", + cur->helper->name, strerror(errno)); + return -1; + } + + return 0; +} + +static int cthelper_nfqueue_setup(struct ctd_helper_instance *cur) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, cur->queue_num); + nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send bind command"); + return -1; + } + + nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, cur->queue_num); + 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)); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send configuration"); + return -1; + } + + return 0; +} + +static int cthelper_configure(struct ctd_helper_instance *cur) +{ + /* First, configure cthelper. */ + if (cthelper_setup(cur) < 0) + return -1; + + /* Now, we are ready to configure nfqueue attached to this helper. */ + if (cthelper_nfqueue_setup(cur) < 0) + return -1; + + dlog(LOG_NOTICE, "helper `%s' configured successfully", + cur->helper->name); + + return 0; +} + +static int nfq_configure(void) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0); + nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_UNBIND); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send pf unbind command"); + return -1; + } + + nlh = nfq_hdr_put(buf, NFQNL_MSG_CONFIG, 0); + nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_PF_BIND); + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send pf bind command"); + return -1; + } + + return 0; +} + +int cthelper_init(void) +{ + struct ctd_helper_instance *cur; + int ret; + + state.cthelper = calloc(1, sizeof(struct ct_helper_state)); + if (state.cthelper == NULL) { + dlog(LOG_ERR, "can't allocate memory for cthelper struct"); + return -1; + } + + STATE_CTH(nl) = mnl_socket_open(NETLINK_NETFILTER); + if (STATE_CTH(nl) == NULL) { + dlog(LOG_ERR, "cannot open nfq socket"); + return -1; + } + fcntl(mnl_socket_get_fd(STATE_CTH(nl)), F_SETFL, O_NONBLOCK); + + if (mnl_socket_bind(STATE_CTH(nl), 0, MNL_SOCKET_AUTOPID) < 0) { + dlog(LOG_ERR, "cannot bind nfq socket"); + return -1; + } + STATE_CTH(portid) = mnl_socket_get_portid(STATE_CTH(nl)); + + if (nfq_configure()) + return -1; + + list_for_each_entry(cur, &CONFIG(cthelper).list, head) { + ret = cthelper_configure(cur); + if (ret < 0) + return ret; + } + + register_fd(mnl_socket_get_fd(STATE_CTH(nl)), nfq_cb, NULL, STATE(fds)); + + return 0; +} diff --git a/src/expect.c b/src/expect.c new file mode 100644 index 0000000..6069770 --- /dev/null +++ b/src/expect.c @@ -0,0 +1,214 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation (or any later at your option). + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "helper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +int +cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + uint32_t class, + union nfct_attr_grp_addr *saddr, + union nfct_attr_grp_addr *daddr, + uint8_t l4proto, uint16_t *sport, uint16_t *dport, + uint32_t flags) +{ + struct nf_conntrack *expected, *mask; + + expected = nfct_new(); + if (!expected) + return -1; + + mask = nfct_new(); + if (!mask) + return -1; + + if (saddr) { + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + int i; + uint32_t addr[4] = {}; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_SRC, saddr->ip); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0xffffffff); + break; + 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, 0xffffffff, sizeof(uint32_t)); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(mask, ATTR_IPV6_SRC, addr); + break; + default: + break; + } + } else { + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + int i; + uint32_t addr[4] = {}; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_SRC, 0x00000000); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0x00000000); + break; + case AF_INET6: + for (i=0; i<4; i++) + memset(addr, 0x00000000, sizeof(uint32_t)); + + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(expected, ATTR_IPV6_SRC, addr); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(mask, ATTR_IPV6_SRC, addr); + break; + default: + break; + } + } + + if (sport) { + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_SRC, *sport); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0xffff); + break; + default: + break; + } + } else { + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_SRC, 0x0000); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0x0000); + break; + default: + break; + } + } + + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + uint32_t addr[4] = {}; + int i; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_DST, daddr->ip); + nfct_set_attr_u32(mask, ATTR_IPV4_DST, 0xffffffff); + break; + 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)); + + nfct_set_attr(mask, ATTR_IPV6_DST, addr); + break; + default: + break; + } + + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_DST, *dport); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_DST, 0xffff); + break; + default: + break; + } + + nfexp_set_attr(exp, ATTR_EXP_MASTER, master); + nfexp_set_attr(exp, ATTR_EXP_EXPECTED, expected); + nfexp_set_attr(exp, ATTR_EXP_MASK, mask); + nfexp_set_attr_u32(exp, ATTR_EXP_FLAGS, flags); + + nfct_destroy(expected); + nfct_destroy(mask); + + return 0; +} + +static int cthelper_expect_cmd(struct nf_expect *exp, int cmd) +{ + int ret; + struct nfct_handle *h; + + h = nfct_open(EXPECT, 0); + if (!h) + return -1; + + ret = nfexp_query(h, cmd, exp); + + nfct_close(h); + return ret; +} + +int cthelper_add_expect(struct nf_expect *exp) +{ + return cthelper_expect_cmd(exp, NFCT_Q_CREATE_UPDATE); +} + +int cthelper_del_expect(struct nf_expect *exp) +{ + return cthelper_expect_cmd(exp, NFCT_Q_DESTROY); +} + +void +cthelper_get_addr_src(struct nf_conntrack *ct, int dir, + union nfct_attr_grp_addr *addr) +{ + switch (dir) { + case MYCT_DIR_ORIG: + nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_SRC, addr); + break; + case MYCT_DIR_REPL: + nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_SRC, addr); + break; + } +} + +void +cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, + union nfct_attr_grp_addr *addr) +{ + switch (dir) { + case MYCT_DIR_ORIG: + nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_DST, addr); + break; + case MYCT_DIR_REPL: + nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_DST, addr); + break; + } +} diff --git a/src/helpers.c b/src/helpers.c new file mode 100644 index 0000000..3e4e6c8 --- /dev/null +++ b/src/helpers.c @@ -0,0 +1,76 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation (or any later at your option). + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "helper.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(helper_list); + +void helper_register(struct ctd_helper *helper) +{ + list_add(&helper->head, &helper_list); +} + +static struct ctd_helper * +__helper_find(const char *helper_name, uint8_t l4proto) +{ + struct ctd_helper *cur, *helper = NULL; + + list_for_each_entry(cur, &helper_list, head) { + if (strncmp(cur->name, helper_name, CTD_HELPER_NAME_LEN) != 0) + continue; + + if (cur->l4proto != l4proto) + continue; + + helper = cur; + break; + } + return helper; +} + +struct ctd_helper * +helper_find(const char *libdir_path, + const char *helper_name, uint8_t l4proto, int flag) +{ + char path[PATH_MAX]; + struct ctd_helper *helper; + struct stat sb; + + helper = __helper_find(helper_name, l4proto); + if (helper != NULL) + return helper; + + snprintf(path, sizeof(path), "%s/ct_helper_%s.so", + libdir_path, helper_name); + + if (stat(path, &sb) != 0) { + if (errno == ENOENT) + return NULL; + fprintf(stderr, "%s: %s\n", path, + strerror(errno)); + return NULL; + } + + if (dlopen(path, flag) == NULL) { + fprintf(stderr, "%s: %s\n", path, dlerror()); + return NULL; + } + + return __helper_find(helper_name, l4proto); +} diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am new file mode 100644 index 0000000..2c9d63b --- /dev/null +++ b/src/helpers/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Make_global.am + +pkglib_LTLIBRARIES = ct_helper_ftp.la + +ct_helper_ftp_la_SOURCES = ftp.c +ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_ftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + + diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c new file mode 100644 index 0000000..962020b --- /dev/null +++ b/src/helpers/ftp.c @@ -0,0 +1,599 @@ +/* + * (C) 2010-2012 by Pablo Neira Ayuso + * + * Based on: kernel-space FTP extension for connection tracking. + * + * This port has been sponsored by Vyatta Inc. + * + * Original copyright notice: + * + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team + * (C) 2003,2004 USAGI/WIDE Project + * + * 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 "network.h" /* for before and after */ +#include "helper.h" +#include "myct.h" +#include "log.h" + +#include /* for isdigit */ +#include + +#include + +#include +#include +#include +#include +#include +#include + +static bool loose; /* XXX: export this as config option. */ + +#define NUM_SEQ_TO_REMEMBER 2 + +/* This structure exists only once per master */ +struct ftp_info { + /* Valid seq positions for cmd matching after newline */ + uint32_t seq_aft_nl[MYCT_DIR_MAX][NUM_SEQ_TO_REMEMBER]; + /* 0 means seq_match_aft_nl not set */ + int seq_aft_nl_num[MYCT_DIR_MAX]; +}; + +enum nf_ct_ftp_type { + /* PORT command from client */ + NF_CT_FTP_PORT, + /* PASV response from server */ + NF_CT_FTP_PASV, + /* EPRT command from client */ + NF_CT_FTP_EPRT, + /* EPSV response from server */ + NF_CT_FTP_EPSV, +}; + +static int +get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term) +{ + const char *end; + int ret = in6_pton(src, min_t(size_t, dlen, 0xffff), + (uint8_t *)dst, term, &end); + if (ret > 0) + return (int)(end - src); + return 0; +} + +static int try_number(const char *data, size_t dlen, uint32_t array[], + int array_size, char sep, char term) +{ + uint32_t len; + int i; + + memset(array, 0, sizeof(array[0])*array_size); + + /* Keep data pointing at next char. */ + for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { + if (*data >= '0' && *data <= '9') { + array[i] = array[i]*10 + *data - '0'; + } + else if (*data == sep) + i++; + else { + /* Unexpected character; true if it's the + terminator and we're finished. */ + if (*data == term && i == array_size - 1) + return len; + pr_debug("Char %u (got %u nums) `%u' unexpected\n", + len, i, *data); + return 0; + } + } + pr_debug("Failed to fill %u numbers separated by %c\n", + array_size, sep); + return 0; +} + +/* Grab port: number up to delimiter */ +static int get_port(const char *data, int start, size_t dlen, char delim, + struct myct_man *cmd) +{ + uint16_t tmp_port = 0; + uint32_t i; + + for (i = start; i < dlen; i++) { + /* Finished? */ + if (data[i] == delim) { + if (tmp_port == 0) + break; + cmd->u.port = htons(tmp_port); + pr_debug("get_port: return %d\n", tmp_port); + return i + 1; + } + else if (data[i] >= '0' && data[i] <= '9') + tmp_port = tmp_port*10 + data[i] - '0'; + else { /* Some other crap */ + pr_debug("get_port: invalid char.\n"); + break; + } + } + return 0; +} + +/* Returns 0, or length of numbers: 192,168,1,1,5,6 */ +static int try_rfc959(const char *data, size_t dlen, struct myct_man *cmd, + uint16_t l3protonum, char term) +{ + int length; + uint32_t array[6]; + + length = try_number(data, dlen, array, 6, ',', term); + if (length == 0) + return 0; + + cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) | + (array[2] << 8) | array[3]); + cmd->u.port = htons((array[4] << 8) | array[5]); + return length; +} + +/* Returns 0, or length of numbers: |1|132.235.1.2|6275| or |2|3ffe::1|6275| */ +static int try_eprt(const char *data, size_t dlen, + struct myct_man *cmd, uint16_t l3protonum, char term) +{ + char delim; + int length; + + /* First character is delimiter, then "1" for IPv4 or "2" for IPv6, + then delimiter again. */ + if (dlen <= 3) { + pr_debug("EPRT: too short\n"); + return 0; + } + delim = data[0]; + if (isdigit(delim) || delim < 33 || delim > 126 || data[2] != delim) { + pr_debug("try_eprt: invalid delimitter.\n"); + return 0; + } + + if ((l3protonum == PF_INET && data[1] != '1') || + (l3protonum == PF_INET6 && data[1] != '2')) { + pr_debug("EPRT: invalid protocol number.\n"); + return 0; + } + + pr_debug("EPRT: Got %c%c%c\n", delim, data[1], delim); + if (data[1] == '1') { + uint32_t array[4]; + + /* Now we have IP address. */ + length = try_number(data + 3, dlen - 3, array, 4, '.', delim); + if (length != 0) + cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) + | (array[2] << 8) | array[3]); + } else { + /* Now we have IPv6 address. */ + length = get_ipv6_addr(data + 3, dlen - 3, + (struct in6_addr *)cmd->u3.ip6, delim); + } + + if (length == 0) + return 0; + pr_debug("EPRT: Got IP address!\n"); + /* Start offset includes initial "|1|", and trailing delimiter */ + return get_port(data, 3 + length + 1, dlen, delim, cmd); +} + +/* Returns 0, or length of numbers: |||6446| */ +static int try_epsv_response(const char *data, size_t dlen, + struct myct_man *cmd, + uint16_t l3protonum, char term) +{ + char delim; + + /* Three delimiters. */ + if (dlen <= 3) return 0; + delim = data[0]; + if (isdigit(delim) || delim < 33 || delim > 126 || + data[1] != delim || data[2] != delim) + return 0; + + return get_port(data, 3, dlen, delim, cmd); +} + +static struct ftp_search { + const char *pattern; + size_t plen; + char skip; + char term; + enum nf_ct_ftp_type ftptype; + int (*getnum)(const char *, size_t, struct myct_man *, uint16_t, char); +} search[MYCT_DIR_MAX][2] = { + [MYCT_DIR_ORIG] = { + { + .pattern = "PORT", + .plen = sizeof("PORT") - 1, + .skip = ' ', + .term = '\r', + .ftptype = NF_CT_FTP_PORT, + .getnum = try_rfc959, + }, + { + .pattern = "EPRT", + .plen = sizeof("EPRT") - 1, + .skip = ' ', + .term = '\r', + .ftptype = NF_CT_FTP_EPRT, + .getnum = try_eprt, + }, + }, + [MYCT_DIR_REPL] = { + { + .pattern = "227 ", + .plen = sizeof("227 ") - 1, + .skip = '(', + .term = ')', + .ftptype = NF_CT_FTP_PASV, + .getnum = try_rfc959, + }, + { + .pattern = "229 ", + .plen = sizeof("229 ") - 1, + .skip = '(', + .term = ')', + .ftptype = NF_CT_FTP_EPSV, + .getnum = try_epsv_response, + }, + }, +}; + +static int ftp_find_pattern(struct pkt_buff *pkt, + unsigned int dataoff, unsigned int dlen, + const char *pattern, size_t plen, + char skip, char term, + unsigned int *matchoff, unsigned int *matchlen, + struct myct_man *cmd, + int (*getnum)(const char *, size_t, + struct myct_man *cmd, + uint16_t, char), + int dir) +{ + char *data = (char *)pktb_network_header(pkt) + dataoff; + int numlen; + uint32_t i; + + if (dlen == 0) + return 0; + + /* short packet, skip partial matching. */ + if (dlen <= plen) + return 0; + + if (strncmp(data, pattern, plen) != 0) + return 0; + + pr_debug("Pattern matches!\n"); + + /* Now we've found the constant string, try to skip + to the 'skip' character */ + for (i = plen; data[i] != skip; i++) + if (i == dlen - 1) return 0; + + /* Skip over the last character */ + i++; + + pr_debug("Skipped up to `%c'!\n", skip); + + numlen = getnum(data + i, dlen - i, cmd, PF_INET, term); + if (!numlen) + return 0; + + pr_debug("Match succeded!\n"); + return 1; +} + +/* Look up to see if we're just after a \n. */ +static int find_nl_seq(uint32_t seq, struct ftp_info *info, int dir) +{ + int i; + + for (i = 0; i < info->seq_aft_nl_num[dir]; i++) + if (info->seq_aft_nl[dir][i] == seq) + return 1; + return 0; +} + +/* We don't update if it's older than what we have. */ +static void update_nl_seq(uint32_t nl_seq, struct ftp_info *info, int dir) +{ + int i, oldest; + + /* Look for oldest: if we find exact match, we're done. */ + for (i = 0; i < info->seq_aft_nl_num[dir]; i++) { + if (info->seq_aft_nl[dir][i] == nl_seq) + return; + } + + if (info->seq_aft_nl_num[dir] < NUM_SEQ_TO_REMEMBER) { + info->seq_aft_nl[dir][info->seq_aft_nl_num[dir]++] = nl_seq; + } else { + if (before(info->seq_aft_nl[dir][0], info->seq_aft_nl[dir][1])) + oldest = 0; + else + oldest = 1; + + if (after(nl_seq, info->seq_aft_nl[dir][oldest])) + info->seq_aft_nl[dir][oldest] = nl_seq; + } +} + +static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type, + char *buffer, size_t buflen, + uint32_t addr, uint16_t port) +{ + switch (type) { + case NF_CT_FTP_PORT: + case NF_CT_FTP_PASV: + return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u", + ((unsigned char *)&addr)[0], + ((unsigned char *)&addr)[1], + ((unsigned char *)&addr)[2], + ((unsigned char *)&addr)[3], + port >> 8, + port & 0xFF); + case NF_CT_FTP_EPRT: + return snprintf(buffer, buflen, "|1|%pI4|%u|", &addr, port); + case NF_CT_FTP_EPSV: + return snprintf(buffer, buflen, "|||%u|", port); + } + + return 0; +} + +/* So, this packet has hit the connection tracking matching code. + Mangle it, and change the expectation to match the new version. */ +static unsigned int nf_nat_ftp(struct pkt_buff *pkt, + int dir, + int ctinfo, + enum nf_ct_ftp_type type, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack *ct, + struct nf_expect *exp) +{ + union nfct_attr_grp_addr newip; + uint16_t port; + char buffer[sizeof("|1|255.255.255.255|65535|")]; + unsigned int buflen; + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + uint16_t initial_port; + + pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); + + /* Connection will come from wherever this packet goes, hence !dir */ + cthelper_get_addr_dst(ct, !dir, &newip); + + expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); + + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, !dir); + + /* 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. */ + 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 ret; + + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + ret = cthelper_add_expect(exp); + if (ret == 0) + break; + else if (ret != -EBUSY) { + port = 0; + break; + } + } + + if (port == 0) + return NF_DROP; + + buflen = nf_nat_ftp_fmt_cmd(type, buffer, sizeof(buffer), + newip.ip, port); + if (!buflen) + goto out; + + if (!nfq_tcp_mangle_ipv4(pkt, matchoff, matchlen, buffer, buflen)) + goto out; + + return NF_ACCEPT; + +out: + cthelper_del_expect(exp); + return NF_DROP; +} + +static int +ftp_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct tcphdr *th; + unsigned int dataoff; + unsigned int matchoff = 0, matchlen = 0; /* makes gcc happy. */ + unsigned int datalen; + unsigned int i; + int found = 0, ends_in_nl; + uint32_t seq; + int ret = NF_ACCEPT; + struct myct_man cmd; + union nfct_attr_grp_addr addr; + union nfct_attr_grp_addr daddr; + int dir = CTINFO2DIR(ctinfo); + struct ftp_info *ftp_info = myct->priv_data; + struct nf_expect *exp = NULL; + + memset(&cmd, 0, sizeof(struct myct_man)); + memset(&addr, 0, sizeof(union nfct_attr_grp_addr)); + + /* Until there's been traffic both ways, don't look in packets. */ + if (ctinfo != IP_CT_ESTABLISHED && + ctinfo != IP_CT_ESTABLISHED_REPLY) { + pr_debug("ftp: Conntrackinfo = %u\n", ctinfo); + goto out; + } + + th = (struct tcphdr *) (pktb_network_header(pkt) + protoff); + + dataoff = protoff + th->doff * 4; + datalen = pktb_len(pkt) - dataoff; + + ends_in_nl = (pktb_network_header(pkt)[pktb_len(pkt) - 1] == '\n'); + seq = ntohl(th->seq) + datalen; + + /* Look up to see if we're just after a \n. */ + if (!find_nl_seq(ntohl(th->seq), ftp_info, dir)) { + /* Now if this ends in \n, update ftp info. */ + pr_debug("nf_conntrack_ftp: wrong seq pos %s(%u) or %s(%u)\n", + ftp_info->seq_aft_nl_num[dir] > 0 ? "" : "(UNSET)", + ftp_info->seq_aft_nl[dir][0], + ftp_info->seq_aft_nl_num[dir] > 1 ? "" : "(UNSET)", + ftp_info->seq_aft_nl[dir][1]); + goto out_update_nl; + } + + /* Initialize IP/IPv6 addr to expected address (it's not mentioned + in EPSV responses) */ + cmd.l3num = nfct_get_attr_u16(myct->ct, ATTR_L3PROTO); + nfct_get_attr_grp(myct->ct, ATTR_GRP_ORIG_ADDR_SRC, &cmd.u3); + + for (i = 0; i < ARRAY_SIZE(search[dir]); i++) { + found = ftp_find_pattern(pkt, dataoff, datalen, + search[dir][i].pattern, + search[dir][i].plen, + search[dir][i].skip, + search[dir][i].term, + &matchoff, &matchlen, + &cmd, + search[dir][i].getnum, + dir); + if (found) break; + } + if (found == 0) /* No match */ + goto out_update_nl; + + pr_debug("conntrack_ftp: match `%.*s' (%u bytes at %u)\n", + matchlen, pktb_network_header(pkt) + matchoff, + matchlen, ntohl(th->seq) + matchoff); + + /* We refer to the reverse direction ("!dir") tuples here, + * because we're expecting something in the other direction. + * Doesn't matter unless NAT is happening. */ + cthelper_get_addr_dst(myct->ct, !dir, &daddr); + + cthelper_get_addr_src(myct->ct, dir, &addr); + + /* Update the ftp info */ + if ((cmd.l3num == nfct_get_attr_u16(myct->ct, ATTR_L3PROTO)) && + memcmp(&cmd.u3, &addr, sizeof(addr)) != 0) { + /* Enrico Scholz's passive FTP to partially RNAT'd ftp + server: it really wants us to connect to a + different IP address. Simply don't record it for + NAT. */ + if (cmd.l3num == PF_INET) { + pr_debug("conntrack_ftp: NOT RECORDING: %pI4 != %pI4\n", + &cmd.u3.ip, &addr); + } else { + pr_debug("conntrack_ftp: NOT RECORDING: %pI6 != %pI6\n", + cmd.u3.ip6, &addr); + } + /* Thanks to Cristiano Lincoln Mattos + for reporting this potential + problem (DMZ machines opening holes to internal + networks, or the packet filter itself). */ + if (!loose) { + ret = NF_ACCEPT; + goto out; + } + memcpy(&daddr, &cmd.u3, sizeof(cmd.u3)); + } + + exp = nfexp_new(); + if (exp == NULL) + goto out_update_nl; + + cthelper_get_addr_src(myct->ct, !dir, &addr); + + if (cthelper_expect_init(exp, myct->ct, 0, &addr, &daddr, IPPROTO_TCP, + NULL, &cmd.u.port, 0)) { + pr_debug("conntrack_ftp: failed to init expectation\n"); + goto out_update_nl; + } + + /* Now, NAT might want to mangle the packet, and register the + * (possibly changed) expectation itself. */ + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { + ret = nf_nat_ftp(pkt, dir, ctinfo, search[dir][i].ftptype, + matchoff, matchlen, myct->ct, exp); + goto out_update_nl; + } + + /* Can't expect this? Best to drop packet now. */ + if (cthelper_add_expect(exp) < 0) { + pr_debug("conntrack_ftp: cannot add expectation: %s\n", + strerror(errno)); + ret = NF_DROP; + goto out_update_nl; + } + +out_update_nl: + if (exp != NULL) + nfexp_destroy(exp); + + /* Now if this ends in \n, update ftp info. Seq may have been + * adjusted by NAT code. */ + if (ends_in_nl) + update_nl_seq(seq, ftp_info, dir); +out: + return ret; +} + +static struct ctd_helper ftp_helper = { + .name = "ftp", + .l4proto = IPPROTO_TCP, + .cb = ftp_helper_cb, + .priv_data_len = sizeof(struct ftp_info), + .policy = { + [0] = { + .name = "ftp", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +void __attribute__ ((constructor)) ftp_init(void); + +void ftp_init(void) +{ + helper_register(&ftp_helper); +} diff --git a/src/main.c b/src/main.c index 26f6c14..831a3c2 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,7 @@ #include "conntrackd.h" #include "log.h" +#include "helper.h" #include #include @@ -31,7 +32,7 @@ #include struct ct_general_state st; -union ct_state state; +struct ct_state state; static const char usage_daemon_commands[] = "Daemon mode commands:\n" diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c new file mode 100644 index 0000000..e8f85bb --- /dev/null +++ b/src/nfct-extensions/helper.c @@ -0,0 +1,619 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This code has been sponsored by Vyatta Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nfct.h" +#include "helper.h" + +static void +nfct_cmd_helper_usage(char *argv[]) +{ + fprintf(stderr, "nfct v%s: Missing command\n" + "%s helper list|add|delete|get|flush " + "[parameters...]\n", VERSION, argv[0]); +} + +int +nfct_cmd_helper_parse_params(int argc, char *argv[]) +{ + int cmd = NFCT_CMD_NONE, ret = 0; + + 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); + } + 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 { + fprintf(stderr, "nfct v%s: Unknown command: %s\n", + VERSION, argv[2]); + nfct_cmd_helper_usage(argv); + exit(EXIT_FAILURE); + } + switch(cmd) { + case NFCT_CMD_LIST: + ret = nfct_cmd_helper_list(argc, argv); + break; + case NFCT_CMD_ADD: + ret = nfct_cmd_helper_add(argc, argv); + break; + case NFCT_CMD_DELETE: + ret = nfct_cmd_helper_delete(argc, argv); + break; + case NFCT_CMD_GET: + ret = nfct_cmd_helper_get(argc, argv); + break; + case NFCT_CMD_FLUSH: + ret = nfct_cmd_helper_flush(argc, argv); + break; + case NFCT_CMD_DISABLE: + ret = nfct_cmd_helper_disable(argc, argv); + break; + } + + return ret; +} + +static int nfct_helper_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nfct_helper *t; + char buf[4096]; + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + goto err; + } + + if (nfct_helper_nlmsg_parse_payload(nlh, t) < 0) { + nfct_perror("nfct_helper_nlmsg_parse_payload"); + goto err_free; + } + + nfct_helper_snprintf(buf, sizeof(buf), t, 0, 0); + printf("%s\n", buf); + +err_free: + nfct_helper_free(t); +err: + return MNL_CB_OK; +} + +int nfct_cmd_helper_list(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"); + return -1; + } + + seq = time(NULL); + 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"); + 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[]) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_helper *t; + uint16_t l3proto; + uint8_t l4proto; + struct ctd_helper *helper; + int ret, j; + + if (argc < 6) { + nfct_perror("missing parameters\n" + "syntax: nfct helper add name " + "family protocol"); + return -1; + } + + if (strcmp(argv[4], "inet") == 0) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } + + if (strcmp(argv[5], "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp(argv[5], "udp") == 0) + l4proto = IPPROTO_UDP; + else { + nfct_perror("unsupported layer 4 protocol"); + return -1; + } + + /* XXX use prefix defined in configure.ac. */ + helper = helper_find("/usr/lib/conntrack-tools", + argv[3], l4proto, RTLD_LAZY); + if (helper == NULL) { + nfct_perror("that helper is not supported"); + return -1; + } + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + return -1; + } + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, argv[3]); + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, l3proto); + nfct_helper_attr_set_u8(t, NFCTH_ATTR_PROTO_L4NUM, l4proto); + nfct_helper_attr_set_u32(t, NFCTH_ATTR_PRIV_DATA_LEN, + helper->priv_data_len); + + for (j=0; jpolicy[j].name[0]) + break; + + p = nfct_helper_policy_alloc(); + if (p == NULL) { + nfct_perror("OOM"); + return -1; + } + + nfct_helper_policy_attr_set(p, NFCTH_ATTR_POLICY_NAME, + helper->policy[j].name); + nfct_helper_policy_attr_set_u32(p, NFCTH_ATTR_POLICY_TIMEOUT, + helper->policy[j].expect_timeout); + nfct_helper_policy_attr_set_u32(p, NFCTH_ATTR_POLICY_MAX, + helper->policy[j].expect_max); + + nfct_helper_attr_set(t, NFCTH_ATTR_POLICY+j, p); + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_NEW, + NLM_F_CREATE | NLM_F_ACK, seq); + nfct_helper_nlmsg_build_payload(nlh, t); + + 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, 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[]) +{ + 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"); + return -1; + } else if (argc > 6) { + nfct_perror("too many arguments"); + return -1; + } + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + return -1; + } + + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, argv[3]); + + if (argc >= 5) { + uint16_t l3proto; + + if (strcmp(argv[4], "inet") == 0) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, l3proto); + } + + if (argc == 6) { + uint8_t l4proto; + + if (strcmp(argv[5], "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp(argv[5], "udp") == 0) + l4proto = IPPROTO_UDP; + else { + nfct_perror("unsupported layer 4 protocol"); + return -1; + } + nfct_helper_attr_set_u32(t, NFCTH_ATTR_PROTO_L4NUM, l4proto); + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_DEL, + NLM_F_ACK, seq); + nfct_helper_nlmsg_build_payload(nlh, t); + + 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, 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[]) +{ + 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"); + return -1; + } else if (argc > 6) { + nfct_perror("too many arguments"); + return -1; + } + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + return -1; + } + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, argv[3]); + + if (argc >= 5) { + uint16_t l3proto; + + if (strcmp(argv[4], "inet") == 0) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, l3proto); + } + + if (argc == 6) { + uint8_t l4proto; + + if (strcmp(argv[5], "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp(argv[5], "udp") == 0) + l4proto = IPPROTO_UDP; + else { + nfct_perror("unsupported layer 4 protocol"); + return -1; + } + nfct_helper_attr_set_u32(t, NFCTH_ATTR_PROTO_L4NUM, l4proto); + } + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_GET, + NLM_F_ACK, seq); + + nfct_helper_nlmsg_build_payload(nlh, t); + + 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"); + return -1; + } + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_helper_flush(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"); + return -1; + } + + seq = time(NULL); + 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"); + return -1; + } + + mnl_socket_close(nl); + + return 0; +} + +int nfct_cmd_helper_disable(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; + 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"); + return -1; + } + + if (strcmp(argv[4], "inet") == 0) + l3proto = AF_INET; + else if (strcmp(argv[4], "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } + + if (strcmp(argv[5], "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp(argv[5], "udp") == 0) + l4proto = IPPROTO_UDP; + else { + nfct_perror("unsupported layer 4 protocol"); + return -1; + } + + /* XXX use prefix defined in configure.ac. */ + helper = helper_find("/usr/lib/conntrack-tools", + argv[3], l4proto, RTLD_LAZY); + if (helper == NULL) { + nfct_perror("that helper is not supported"); + return -1; + } + + t = nfct_helper_alloc(); + if (t == NULL) { + nfct_perror("OOM"); + return -1; + } + nfct_helper_attr_set(t, NFCTH_ATTR_NAME, argv[3]); + nfct_helper_attr_set_u16(t, NFCTH_ATTR_PROTO_L3NUM, l3proto); + nfct_helper_attr_set_u8(t, NFCTH_ATTR_PROTO_L4NUM, l4proto); + nfct_helper_attr_set_u32(t, NFCTH_ATTR_STATUS, + NFCT_HELPER_STATUS_DISABLED); + + seq = time(NULL); + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_NEW, + NLM_F_CREATE | NLM_F_ACK, seq); + nfct_helper_nlmsg_build_payload(nlh, t); + + 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, 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; +} + diff --git a/src/nfct.c b/src/nfct.c index db629e7..b5c9654 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -56,6 +56,8 @@ int main(int argc, char *argv[]) } 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) @@ -71,6 +73,9 @@ int main(int argc, char *argv[]) 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; case NFCT_SUBSYS_VERSION: ret = nfct_cmd_version(argc, argv); break; @@ -99,6 +104,7 @@ static const char help_msg[] = "nfct v%s: utility for the Netfilter's Connection Tracking System\n" "Usage: %s command [parameters]...\n\n" "Subsystem:\n" + " helper\t\tAllows to configure user-space helper\n" " timeout\t\tAllows definition of fine-grain timeout policies\n" " version\t\tDisplay version and disclaimer\n" " help\t\t\tDisplay this help message\n" diff --git a/src/read_config_lex.l b/src/read_config_lex.l index 01fe4fc..31fa32e 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -142,6 +142,11 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k] "TCPWindowTracking" { return T_TCP_WINDOW_TRACKING; } "ExpectationSync" { return T_EXPECT_SYNC; } "ErrorQueueLength" { return T_ERROR_QUEUE_LENGTH; } +"Helper" { return T_HELPER; } +"QueueNum" { return T_HELPER_QUEUE_NUM; } +"Policy" { return T_HELPER_POLICY; } +"ExpectMax" { return T_HELPER_EXPECT_MAX; } +"ExpectTimeout" { return T_HELPER_EXPECT_TIMEOUT; } {is_on} { return T_ON; } {is_off} { return T_OFF; } diff --git a/src/read_config_yy.y b/src/read_config_yy.y index b22784c..c9235d3 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -28,8 +28,11 @@ #include "conntrackd.h" #include "bitops.h" #include "cidr.h" +#include "helper.h" +#include "stack.h" #include #include +#include #include #include @@ -48,6 +51,15 @@ static void print_err(int err, const char *msg, ...); static void __kernel_filter_start(void); static void __kernel_filter_add_state(int value); static void __max_dedicated_links_reached(void); + +struct stack symbol_stack; + +enum { + SYMBOL_HELPER_QUEUE_NUM, + SYMBOL_HELPER_POLICY_EXPECT_ROOT, + SYMBOL_HELPER_EXPECT_POLICY_LEAF, +}; + %} %union { @@ -74,6 +86,8 @@ static void __max_dedicated_links_reached(void); %token T_SCHEDULER T_TYPE T_PRIO T_NETLINK_EVENTS_RELIABLE %token T_DISABLE_INTERNAL_CACHE T_DISABLE_EXTERNAL_CACHE T_ERROR_QUEUE_LENGTH %token T_OPTIONS T_TCP_WINDOW_TRACKING T_EXPECT_SYNC +%token T_HELPER T_HELPER_QUEUE_NUM T_HELPER_POLICY T_HELPER_EXPECT_MAX +%token T_HELPER_EXPECT_TIMEOUT %token T_IP T_PATH_VAL %token T_NUMBER @@ -96,6 +110,7 @@ line : ignore_protocol | general | sync | stats + | helper ; logfile_bool : T_LOG T_ON @@ -1561,6 +1576,186 @@ buffer_size: T_STAT_BUFFER_SIZE T_NUMBER print_err(CTD_CFG_WARN, "`LogFileBufferSize' is deprecated"); }; +helper: T_HELPER '{' helper_list '}' +{ + conf.flags |= CTD_HELPER; +}; + +helper_list: + | helper_list helper_line + ; + +helper_line: helper_type + ; + +helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}' +{ + struct ctd_helper_instance *helper_inst; + struct ctd_helper *helper; + struct stack_item *e; + uint16_t l3proto; + uint8_t l4proto; + + if (strcmp($3, "inet") == 0) + l3proto = AF_INET; + else if (strcmp($3, "inet6") == 0) + l3proto = AF_INET6; + else { + print_err(CTD_CFG_ERROR, "unknown layer 3 protocol"); + exit(EXIT_FAILURE); + } + + if (strcmp($4, "tcp") == 0) + l4proto = IPPROTO_TCP; + else if (strcmp($4, "udp") == 0) + l4proto = IPPROTO_UDP; + else { + print_err(CTD_CFG_ERROR, "unknown layer 4 protocol"); + exit(EXIT_FAILURE); + } + + /* XXX use configure.ac definitions. */ + helper = helper_find("/usr/lib/conntrack-tools", $2, l4proto, RTLD_NOW); + if (helper == NULL) { + print_err(CTD_CFG_ERROR, "Unknown `%s' helper", $2); + exit(EXIT_FAILURE); + } + + helper_inst = calloc(1, sizeof(struct ctd_helper_instance)); + if (helper_inst == NULL) + break; + + helper_inst->l3proto = l3proto; + helper_inst->l4proto = l4proto; + helper_inst->helper = helper; + + while ((e = stack_item_pop(&symbol_stack, -1)) != NULL) { + + switch(e->type) { + case SYMBOL_HELPER_QUEUE_NUM: { + int *qnum = (int *) &e->data; + + helper_inst->queue_num = *qnum; + stack_item_free(e); + break; + } + case SYMBOL_HELPER_POLICY_EXPECT_ROOT: { + struct ctd_helper_policy *pol = + (struct ctd_helper_policy *) &e->data; + struct ctd_helper_policy *matching = NULL; + int i; + + for (i=0; ipolicy[i].name, + pol->name) != 0) + continue; + + matching = pol; + break; + } + if (matching == NULL) { + print_err(CTD_CFG_ERROR, + "Unknown policy `%s' in helper " + "configuration", pol->name); + exit(EXIT_FAILURE); + } + /* FIXME: First set default policy, then change only + * tuned fields, not everything. + */ + memcpy(&helper->policy[i], pol, + sizeof(struct ctd_helper_policy)); + + stack_item_free(e); + break; + } + default: + print_err(CTD_CFG_ERROR, + "Unexpected symbol parsing helper policy"); + exit(EXIT_FAILURE); + break; + } + } + list_add(&helper_inst->head, &CONFIG(cthelper).list); +}; + +helper_type_list: + | helper_type_list helper_type_line + ; + +helper_type_line: helper_type + ; + +helper_type: T_HELPER_QUEUE_NUM T_NUMBER +{ + int *qnum; + struct stack_item *e; + + e = stack_item_alloc(SYMBOL_HELPER_QUEUE_NUM, sizeof(int)); + qnum = (int *) e->data; + *qnum = $2; + stack_item_push(&symbol_stack, e); +}; + +helper_type: T_HELPER_POLICY T_STRING '{' helper_policy_list '}' +{ + struct stack_item *e; + struct ctd_helper_policy *policy; + + e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF); + if (e == NULL) { + print_err(CTD_CFG_ERROR, + "Helper policy configuration empty, fix your " + "configuration file, please"); + exit(EXIT_FAILURE); + break; + } + + policy = (struct ctd_helper_policy *) &e->data; + strncpy(policy->name, $2, CTD_HELPER_NAME_LEN); + policy->name[CTD_HELPER_NAME_LEN-1] = '\0'; + /* Now object is complete. */ + e->type = SYMBOL_HELPER_POLICY_EXPECT_ROOT; + stack_item_push(&symbol_stack, e); +}; + +helper_policy_list: + | helper_policy_list helper_policy_line + ; + +helper_policy_line: helper_policy_expect_max + | helper_policy_expect_timeout + ; + +helper_policy_expect_max: T_HELPER_EXPECT_MAX T_NUMBER +{ + struct stack_item *e; + struct ctd_helper_policy *policy; + + e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF); + if (e == NULL) { + e = stack_item_alloc(SYMBOL_HELPER_EXPECT_POLICY_LEAF, + sizeof(struct ctd_helper_policy)); + } + policy = (struct ctd_helper_policy *) &e->data; + policy->expect_max = $2; + stack_item_push(&symbol_stack, e); +}; + +helper_policy_expect_timeout: T_HELPER_EXPECT_TIMEOUT T_NUMBER +{ + struct stack_item *e; + struct ctd_helper_policy *policy; + + e = stack_item_pop(&symbol_stack, SYMBOL_HELPER_EXPECT_POLICY_LEAF); + if (e == NULL) { + e = stack_item_alloc(SYMBOL_HELPER_EXPECT_POLICY_LEAF, + sizeof(struct ctd_helper_policy)); + } + policy = (struct ctd_helper_policy *) &e->data; + policy->expect_timeout = $2; + stack_item_push(&symbol_stack, e); +}; + %% int __attribute__((noreturn)) @@ -1640,6 +1835,11 @@ init_config(char *filename) CONFIG(stats).syslog_facility = -1; CONFIG(netlink).subsys_id = -1; + /* Initialize list of user-space helpers */ + INIT_LIST_HEAD(&CONFIG(cthelper).list); + + stack_init(&symbol_stack); + yyrestart(fp); yyparse(); fclose(fp); diff --git a/src/run.c b/src/run.c index 852bec6..3337694 100644 --- a/src/run.c +++ b/src/run.c @@ -50,6 +50,9 @@ void killer(int foo) if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) ctnl_kill(); + if (CONFIG(flags) & CTD_HELPER) + cthelper_kill(); + destroy_fds(STATE(fds)); unlink(CONFIG(lockfile)); dlog(LOG_NOTICE, "---- shutdown received ----"); @@ -199,6 +202,9 @@ static int local_handler(int fd, void *data) if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) return ctnl_local(fd, type, data); + if (CONFIG(flags) & CTD_HELPER) + return cthelper_local(fd, type, data); + return ret; } @@ -250,6 +256,11 @@ init(void) if (ctnl_init() < 0) return -1; + if (CONFIG(flags) & CTD_HELPER) { + if (cthelper_init() < 0) + return -1; + } + time(&STATE(stats).daemon_start_time); dlog(LOG_NOTICE, "initialization completed"); diff --git a/src/stack.c b/src/stack.c new file mode 100644 index 0000000..104b7ba --- /dev/null +++ b/src/stack.c @@ -0,0 +1,56 @@ +/* + * (C) 2005-2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include "stack.h" + +struct stack_item * +stack_item_alloc(int type, size_t data_len) +{ + struct stack_item *e; + + e = calloc(1, sizeof(struct stack_item) + data_len); + if (e == NULL) + return NULL; + + e->data_len = data_len; + e->type = type; + + return e; +} + +void stack_item_free(struct stack_item *e) +{ + free(e); +} + +void stack_item_push(struct stack *s, struct stack_item *e) +{ + list_add(&e->head, &s->list); +} + +struct stack_item *stack_item_pop(struct stack *s, int type) +{ + struct stack_item *cur, *tmp, *found = NULL; + + list_for_each_entry_safe(cur, tmp, &s->list, head) { + if (cur->type != type && type != -1) + continue; + + list_del(&cur->head); + found = cur; + break; + } + + return found; +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..fabec24 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,243 @@ +/* The following code has been extracted from the kenrel sources, if there is + * any problem, blame for mangling it. --pablo */ + +/* + * Generic address resultion entity + * + * Authors: + * net_random Alan Cox + * net_ratelimit Andi Kleen + * in{4,6}_pton YOSHIFUJI Hideaki, Copyright (C)2006 USAGI/WIDE Project + * + * Created by Alexey Kuznetsov + * + * 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. + */ + +/* + * lib/hexdump.c + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include /* for memcpy */ + +#include "helper.h" + +static int hex_to_bin(char ch) +{ + if ((ch >= '0') && (ch <= '9')) + return ch - '0'; + ch = tolower(ch); + if ((ch >= 'a') && (ch <= 'f')) + return ch - 'a' + 10; + return -1; +} + +#define IN6PTON_XDIGIT 0x00010000 +#define IN6PTON_DIGIT 0x00020000 +#define IN6PTON_COLON_MASK 0x00700000 +#define IN6PTON_COLON_1 0x00100000 /* single : requested */ +#define IN6PTON_COLON_2 0x00200000 /* second : requested */ +#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ +#define IN6PTON_DOT 0x00800000 /* . */ +#define IN6PTON_DELIM 0x10000000 +#define IN6PTON_NULL 0x20000000 /* first/tail */ +#define IN6PTON_UNKNOWN 0x40000000 + +static inline int xdigit2bin(char c, int delim) +{ + int val; + + if (c == delim || c == '\0') + return IN6PTON_DELIM; + if (c == ':') + return IN6PTON_COLON_MASK; + if (c == '.') + return IN6PTON_DOT; + + val = hex_to_bin(c); + if (val >= 0) + return val | IN6PTON_XDIGIT | (val < 10 ? IN6PTON_DIGIT : 0); + + if (delim == -1) + return IN6PTON_DELIM; + return IN6PTON_UNKNOWN; +} + +int in4_pton(const char *src, int srclen, + uint8_t *dst, + int delim, const char **end) +{ + const char *s; + uint8_t *d; + uint8_t dbuf[4]; + int ret = 0; + int i; + int w = 0; + + if (srclen < 0) + srclen = strlen(src); + s = src; + d = dbuf; + i = 0; + while(1) { + int c; + c = xdigit2bin(srclen > 0 ? *s : '\0', delim); + if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK))) { + goto out; + } + if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + if (w == 0) + goto out; + *d++ = w & 0xff; + w = 0; + i++; + if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + if (i != 4) + goto out; + break; + } + goto cont; + } + w = (w * 10) + c; + if ((w & 0xffff) > 255) { + goto out; + } +cont: + if (i >= 4) + goto out; + s++; + srclen--; + } + ret = 1; + memcpy(dst, dbuf, sizeof(dbuf)); +out: + if (end) + *end = s; + return ret; +} + +int in6_pton(const char *src, int srclen, + uint8_t *dst, + int delim, const char **end) +{ + const char *s, *tok = NULL; + uint8_t *d, *dc = NULL; + uint8_t dbuf[16]; + int ret = 0; + int i; + int state = IN6PTON_COLON_1_2 | IN6PTON_XDIGIT | IN6PTON_NULL; + int w = 0; + + memset(dbuf, 0, sizeof(dbuf)); + + s = src; + d = dbuf; + if (srclen < 0) + srclen = strlen(src); + + while (1) { + int c; + + c = xdigit2bin(srclen > 0 ? *s : '\0', delim); + if (!(c & state)) + goto out; + if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + /* process one 16-bit word */ + if (!(state & IN6PTON_NULL)) { + *d++ = (w >> 8) & 0xff; + *d++ = w & 0xff; + } + w = 0; + if (c & IN6PTON_DELIM) { + /* We've processed last word */ + break; + } + /* + * COLON_1 => XDIGIT + * COLON_2 => XDIGIT|DELIM + * COLON_1_2 => COLON_2 + */ + switch (state & IN6PTON_COLON_MASK) { + case IN6PTON_COLON_2: + dc = d; + state = IN6PTON_XDIGIT | IN6PTON_DELIM; + if (dc - dbuf >= (int)sizeof(dbuf)) + state |= IN6PTON_NULL; + break; + case IN6PTON_COLON_1|IN6PTON_COLON_1_2: + state = IN6PTON_XDIGIT | IN6PTON_COLON_2; + break; + case IN6PTON_COLON_1: + state = IN6PTON_XDIGIT; + break; + case IN6PTON_COLON_1_2: + state = IN6PTON_COLON_2; + break; + default: + state = 0; + } + tok = s + 1; + goto cont; + } + + if (c & IN6PTON_DOT) { + ret = in4_pton(tok ? tok : s, srclen + (int)(s - tok), d, delim, &s); + if (ret > 0) { + d += 4; + break; + } + goto out; + } + + w = (w << 4) | (0xff & c); + state = IN6PTON_COLON_1 | IN6PTON_DELIM; + if (!(w & 0xf000)) { + state |= IN6PTON_XDIGIT; + } + if (!dc && d + 2 < dbuf + sizeof(dbuf)) { + state |= IN6PTON_COLON_1_2; + state &= ~IN6PTON_DELIM; + } + if (d + 2 >= dbuf + sizeof(dbuf)) { + state &= ~(IN6PTON_COLON_1|IN6PTON_COLON_1_2); + } +cont: + if ((dc && d + 4 < dbuf + sizeof(dbuf)) || + d + 4 == dbuf + sizeof(dbuf)) { + state |= IN6PTON_DOT; + } + if (d >= dbuf + sizeof(dbuf)) { + state &= ~(IN6PTON_XDIGIT|IN6PTON_COLON_MASK); + } + s++; + srclen--; + } + + i = 15; d--; + + if (dc) { + while(d >= dc) + dst[i--] = *d--; + while(i >= dc - dbuf) + dst[i--] = 0; + while(i >= 0) + dst[i--] = *d--; + } else + memcpy(dst, dbuf, sizeof(dbuf)); + + ret = 1; +out: + if (end) + *end = s; + return ret; +} -- cgit v1.2.3 From 969d93f14fffadb5cae67a7662484c1e064bbff1 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 15 May 2012 14:31:35 +0200 Subject: conntrackd: RPC helper added to cthelper How to use this helper in a few steps: 1) You can enable this helper via: nfct helper add rpc inet tcp nfct helper add rpc inet udp 2) Configure /etc/conntrackd/conntrackd.conf and launch it. 3) You can test this helper locally with the following rule-set: iptables -A OUTPUT -t raw -p udp -m udp --dport 111 -j CT --helper rpc iptables -A OUTPUT -t raw -p tcp -m tcp --dport 111 -j CT --helper rpc iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -m tcp --dport 111 -j ACCEPT iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -m udp --dport 111 -j ACCEPT iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -P OUTPUT DROP 4) Configure NFS and export some local directory. Then, mount it with version 3. mount.nfs -onfsvers=3 127.0.0.1:/srv/cvs /mnt/ You should see permanent expectations created for this. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 14 ++ src/helpers/Makefile.am | 7 +- src/helpers/rpc.c | 488 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+), 2 deletions(-) create mode 100644 src/helpers/rpc.c diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 711b309..2bf99fa 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -28,6 +28,20 @@ Helper { ExpectTimeout 300 } } + Type rpc inet tcp { + QueueNum 1 + Policy rpc { + ExpectMax 1 + ExpectTimeout 300 + } + } + Type rpc inet udp { + QueueNum 2 + Policy rpc { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 2c9d63b..f441b29 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,9 +1,12 @@ include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_ftp.la +pkglib_LTLIBRARIES = ct_helper_ftp.la \ + ct_helper_rpc.la ct_helper_ftp_la_SOURCES = ftp.c ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ct_helper_ftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) - +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) diff --git a/src/helpers/rpc.c b/src/helpers/rpc.c new file mode 100644 index 0000000..82493c2 --- /dev/null +++ b/src/helpers/rpc.c @@ -0,0 +1,488 @@ +/* + * (C) 2012 by Jozsef Kadlecsik + * (C) 2012 by Pablo Neira Ayuso + * + * Based on: RPC extension for conntrack. + * + * This port has been sponsored by Vyatta Inc. + * + * Original copyright notice: + * + * (C) 2000 by Marcelo Barbosa Lima + * (C) 2001 by Rusty Russell + * (C) 2002,2003 by Ian (Larry) Latter + * (C) 2004,2005 by David Stes + * + * 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 "network.h" /* for before and after */ +#include "helper.h" +#include "myct.h" +#include "log.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* RFC 1050: RPC: Remote Procedure Call Protocol Specification Version 2 */ +/* RFC 1014: XDR: External Data Representation Standard */ +#define SUPPORTED_RPC_VERSION 2 + +struct rpc_info { + /* XID */ + uint32_t xid; + /* program */ + uint32_t pm_prog; + /* program version */ + uint32_t pm_vers; + /* transport protocol: TCP|UDP */ + uint32_t pm_prot; +}; + +/* So, this packet has hit the connection tracking matching code. + Mangle it, and change the expectation to match the new version. */ +static unsigned int +nf_nat_rpc(struct pkt_buff *pkt, int dir, struct nf_expect *exp, + uint8_t proto, uint32_t *port_ptr) +{ + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + uint16_t initial_port, port; + + expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); + + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, !dir); + + /* 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, proto); + 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. */ + 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 ret; + + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + ret = cthelper_add_expect(exp); + if (ret == 0) + break; + else if (ret != -EBUSY) { + port = 0; + break; + } + } + nfct_destroy(nat_tuple); + + if (port == 0) + return NF_DROP; + + *port_ptr = htonl(port); + + return NF_ACCEPT; +} + +#define OFFSET(o, n) ((o) += n) +#define ROUNDUP(n) ((((n) + 3)/4)*4) + +static int +rpc_call(const uint32_t *data, uint32_t offset, uint32_t datalen, + struct rpc_info *rpc_info) +{ + uint32_t p, r; + + /* RPC CALL message body */ + + /* call_body { + * rpcvers + * prog + * vers + * proc + * opaque_auth cred + * opaque_auth verf + * pmap + * } + * + * opaque_auth { + * flavour + * opaque[len] <= MAX_AUTH_BYTES + * } + */ + if (datalen < OFFSET(offset, 4*4 + 2*2*4)) { + pr_debug("RPC CALL: too short packet: %u < %u\n", + datalen, offset); + return -1; + } + /* Check rpcversion */ + p = IXDR_GET_INT32(data); + if (p != SUPPORTED_RPC_VERSION) { + pr_debug("RPC CALL: wrong rpcvers %u != %u\n", + p, SUPPORTED_RPC_VERSION); + return -1; + } + /* Skip non-portmap requests */ + p = IXDR_GET_INT32(data); + if (p != PMAPPROG) { + pr_debug("RPC CALL: not portmap %u != %lu\n", + p, PMAPPROG); + return -1; + } + /* Check portmap version */ + p = IXDR_GET_INT32(data); + if (p != PMAPVERS) { + pr_debug("RPC CALL: wrong portmap version %u != %lu\n", + p, PMAPVERS); + return -1; + } + /* Skip non PMAPPROC_GETPORT requests */ + p = IXDR_GET_INT32(data); + if (p != PMAPPROC_GETPORT) { + pr_debug("RPC CALL: not PMAPPROC_GETPORT %u != %lu\n", + p, PMAPPROC_GETPORT); + return -1; + } + /* Check and skip credentials */ + r = IXDR_GET_INT32(data); + p = IXDR_GET_INT32(data); + pr_debug("RPC CALL: cred: %u %u (%u, %u)\n", + r, p, datalen, offset); + if (p > MAX_AUTH_BYTES) { + pr_debug("RPC CALL: invalid sized cred %u > %u\n", + p, MAX_AUTH_BYTES); + return -1; + } + r = ROUNDUP(p); + if (datalen < OFFSET(offset, r)) { + pr_debug("RPC CALL: too short to carry cred: %u < %u, %u\n", + datalen, offset, r); + return -1; + } + data += r/4; + /* Check and skip verifier */ + r = IXDR_GET_INT32(data); + p = IXDR_GET_INT32(data); + pr_debug("RPC CALL: verf: %u %u (%u, %u)\n", + r, p, datalen, offset); + if (p > MAX_AUTH_BYTES) { + pr_debug("RPC CALL: invalid sized verf %u > %u\n", + p, MAX_AUTH_BYTES); + return -1; + } + r = ROUNDUP(p); + if (datalen < OFFSET(offset, r)) { + pr_debug("RPC CALL: too short to carry verf: %u < %u, %u\n", + datalen, offset, r); + return -1; + } + data += r/4; + /* pmap { + * prog + * vers + * prot + * port + * } + */ + /* Check CALL size */ + if (datalen != offset + 4*4) { + pr_debug("RPC CALL: invalid size to carry pmap: %u != %u\n", + datalen, offset + 4*4); + return -1; + } + rpc_info->pm_prog = IXDR_GET_INT32(data); + rpc_info->pm_vers = IXDR_GET_INT32(data); + rpc_info->pm_prot = IXDR_GET_INT32(data); + /* Check supported protocols */ + if (!(rpc_info->pm_prot == IPPROTO_TCP + || rpc_info->pm_prot == IPPROTO_UDP)) { + pr_debug("RPC CALL: unsupported protocol %u", + rpc_info->pm_prot); + return -1; + } + p = IXDR_GET_INT32(data); + /* Check port: must be zero */ + if (p != 0) { + pr_debug("RPC CALL: port is nonzero %u\n", + ntohl(p)); + return -1; + } + pr_debug("RPC CALL: processed: xid %u, prog %u, vers %u, prot %u\n", + rpc_info->xid, rpc_info->pm_prog, + rpc_info->pm_vers, rpc_info->pm_prot); + + return 0; +} + +static int +rpc_reply(uint32_t *data, uint32_t offset, uint32_t datalen, + struct rpc_info *rpc_info, uint32_t **port_ptr) +{ + uint16_t port; + uint32_t p, r; + + /* RPC REPLY message body */ + + /* reply_body { + * reply_stat + * xdr_union { + * accepted_reply + * rejected_reply + * } + * } + * accepted_reply { + * opaque_auth verf + * accept_stat + * xdr_union { + * port + * struct mismatch_info + * } + * } + */ + + /* Check size: reply status */ + if (datalen < OFFSET(offset, 4)) { + pr_debug("RPC REPL: too short, missing rp_stat: %u < %u\n", + datalen, offset); + return -1; + } + p = IXDR_GET_INT32(data); + /* Check accepted request */ + if (p != MSG_ACCEPTED) { + pr_debug("RPC REPL: not accepted %u != %u\n", + p, MSG_ACCEPTED); + return -1; + } + /* Check and skip verifier */ + if (datalen < OFFSET(offset, 2*4)) { + pr_debug("RPC REPL: too short, missing verf: %u < %u\n", + datalen, offset); + return -1; + } + r = IXDR_GET_INT32(data); + p = IXDR_GET_INT32(data); + pr_debug("RPC REPL: verf: %u %u (%u, %u)\n", + r, p, datalen, offset); + if (p > MAX_AUTH_BYTES) { + pr_debug("RPC REPL: invalid sized verf %u > %u\n", + p, MAX_AUTH_BYTES); + return -1; + } + r = ROUNDUP(p); + /* verifier + ac_stat + port */ + if (datalen != OFFSET(offset, r) + 2*4) { + pr_debug("RPC REPL: invalid size to carry verf and " + "success: %u != %u\n", + datalen, offset + 2*4); + return -1; + } + data += r/4; + /* Check success */ + p = IXDR_GET_INT32(data); + if (p != SUCCESS) { + pr_debug("RPC REPL: not success %u != %u\n", + p, SUCCESS); + return -1; + } + /* Get port */ + *port_ptr = data; + port = IXDR_GET_INT32(data); /* -Wunused-but-set-parameter */ + if (port == 0) { + pr_debug("RPC REPL: port is zero\n"); + return -1; + } + pr_debug("RPC REPL: processed: xid %u, prog %u, vers %u, " + "prot %u, port %u\n", + rpc_info->xid, rpc_info->pm_prog, rpc_info->pm_vers, + rpc_info->pm_prot, port); + return 0; +} + +static int +rpc_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + int dir = CTINFO2DIR(ctinfo); + unsigned int offset = protoff, datalen; + uint32_t *data, *port_ptr = NULL, xid; + uint16_t port; + uint8_t proto = nfct_get_attr_u8(myct->ct, ATTR_L4PROTO); + enum msg_type rm_dir; + struct rpc_info *rpc_info = myct->priv_data; + union nfct_attr_grp_addr addr, daddr; + struct nf_expect *exp = NULL; + int ret = NF_ACCEPT; + + /* Until there's been traffic both ways, don't look into TCP packets. */ + if (proto == IPPROTO_TCP + && ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED_REPLY) { + pr_debug("TCP RPC: Conntrackinfo = %u\n", ctinfo); + return ret; + } + if (proto == IPPROTO_TCP) { + struct tcphdr *th = + (struct tcphdr *) (pktb_network_header(pkt) + protoff); + offset += th->doff * 4; + } else { + offset += sizeof(struct udphdr); + } + /* Skip broken headers */ + if (offset % 4) { + pr_debug("RPC: broken header: offset %u%%4 != 0\n", offset); + return ret; + } + + /* Take into Record Fragment header */ + if (proto == IPPROTO_TCP) + offset += 4; + + datalen = pktb_len(pkt); + data = (uint32_t *)(pktb_network_header(pkt) + offset); + + /* rpc_msg { + * xid + * direction + * xdr_union { + * call_body + * reply_body + * } + * } + */ + + /* Check minimal msg size: xid + direction */ + if (datalen < OFFSET(offset, 2*4)) { + pr_debug("RPC: too short packet: %u < %u\n", + datalen, offset); + return ret; + } + xid = IXDR_GET_INT32(data); + rm_dir = IXDR_GET_INT32(data); + + /* Check direction */ + if (!((rm_dir == CALL && dir == MYCT_DIR_ORIG) + || (rm_dir == REPLY && dir == MYCT_DIR_REPL))) { + pr_debug("RPC: rm_dir != dir %u != %u\n", rm_dir, dir); + goto out; + } + + if (rm_dir == CALL) { + if (rpc_call(data, offset, datalen, rpc_info) < 0) + goto out; + + rpc_info->xid = xid; + + return ret; + } else { + /* Check XID */ + if (xid != rpc_info->xid) { + pr_debug("RPC REPL: XID does not match: %u != %u\n", + xid, rpc_info->xid); + goto out; + } + if (rpc_reply(data, offset, datalen, rpc_info, &port_ptr) < 0) + goto out; + + port = IXDR_GET_INT32(port_ptr); + port = htons(port); + + /* We refer to the reverse direction ("!dir") tuples here, + * because we're expecting something in the other direction. + * Doesn't matter unless NAT is happening. */ + cthelper_get_addr_dst(myct->ct, !dir, &daddr); + cthelper_get_addr_src(myct->ct, !dir, &addr); + + exp = nfexp_new(); + if (exp == NULL) + goto out; + + if (cthelper_expect_init(exp, myct->ct, 0, &addr, &daddr, + rpc_info->pm_prot, + NULL, &port, NF_CT_EXPECT_PERMANENT)) { + pr_debug("RPC: failed to init expectation\n"); + goto out_exp; + } + + /* Now, NAT might want to mangle the packet, and register the + * (possibly changed) expectation itself. */ + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { + ret = nf_nat_rpc(pkt, dir, exp, rpc_info->pm_prot, + port_ptr); + goto out_exp; + } + + /* Can't expect this? Best to drop packet now. */ + if (cthelper_add_expect(exp) < 0) { + pr_debug("RPC: cannot add expectation: %s\n", + strerror(errno)); + ret = NF_DROP; + } + } + +out_exp: + nfexp_destroy(exp); +out: + rpc_info->xid = 0; + return ret; +} + +static struct ctd_helper rpc_helper_tcp = { + .name = "rpc", + .l4proto = IPPROTO_TCP, + .cb = rpc_helper_cb, + .priv_data_len = sizeof(struct rpc_info), + .policy = { + { + .name = "rpc", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +static struct ctd_helper rpc_helper_udp = { + .name = "rpc", + .l4proto = IPPROTO_UDP, + .cb = rpc_helper_cb, + .priv_data_len = sizeof(struct rpc_info), + .policy = { + { + .name = "rpc", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +void __attribute__ ((constructor)) rpc_init(void); + +void rpc_init(void) +{ + helper_register(&rpc_helper_tcp); + helper_register(&rpc_helper_udp); +} -- cgit v1.2.3 From 40f4330e6b50ed2b198549b1006c6fcb349f5a3b Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 15 May 2012 14:43:20 +0200 Subject: conntrackd: TNS helper added to cthelper Signed-off-by: Jozsef Kadlecsik Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 7 + src/helpers/Makefile.am | 7 +- src/helpers/tns.c | 407 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 src/helpers/tns.c diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 2bf99fa..80f1f92 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -42,6 +42,13 @@ Helper { ExpectTimeout 300 } } + Type tns inet tcp { + QueueNum 3 + Policy tns { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index f441b29..589b4f3 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,7 +1,8 @@ include $(top_srcdir)/Make_global.am pkglib_LTLIBRARIES = ct_helper_ftp.la \ - ct_helper_rpc.la + ct_helper_rpc.la \ + ct_helper_tns.la ct_helper_ftp_la_SOURCES = ftp.c ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) @@ -10,3 +11,7 @@ ct_helper_ftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) 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_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) diff --git a/src/helpers/tns.c b/src/helpers/tns.c new file mode 100644 index 0000000..5833fea --- /dev/null +++ b/src/helpers/tns.c @@ -0,0 +1,407 @@ +/* + * (C) 2012 by Jozsef Kadlecsik + * (C) 2012 by Pablo Neira Ayuso + * + * Sponsored by Vyatta Inc. + * + * 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 "network.h" /* for before and after */ +#include "helper.h" +#include "myct.h" +#include "log.h" + +#include /* for isdigit */ +#include + +#include + +#include +#include +#include +#include +#include +#include + +/* TNS SQL*Net Version 2 */ +enum tns_types { + TNS_TYPE_CONNECT = 1, + TNS_TYPE_ACCEPT = 2, + TNS_TYPE_ACK = 3, + TNS_TYPE_REFUSE = 4, + TNS_TYPE_REDIRECT = 5, + TNS_TYPE_DATA = 6, + TNS_TYPE_NULL = 7, + TNS_TYPE_ABORT = 9, + TNS_TYPE_RESEND = 11, + TNS_TYPE_MARKER = 12, + TNS_TYPE_ATTENTION = 13, + TNS_TYPE_CONTROL = 14, + TNS_TYPE_MAX = 19, +}; + +struct tns_header { + uint16_t len; + uint16_t csum; + uint8_t type; + uint8_t reserved; + uint16_t header_csum; +}; + +struct tns_redirect { + uint16_t data_len; +}; + +struct tns_info { + /* Scan next DATA|REDIRECT packet */ + bool parse; +}; + +static int try_number(const char *data, size_t dlen, uint32_t array[], + int array_size, char sep, char term) +{ + uint32_t len; + int i; + + memset(array, 0, sizeof(array[0])*array_size); + + /* Keep data pointing at next char. */ + for (i = 0, len = 0; len < dlen && i < array_size; len++, data++) { + if (*data >= '0' && *data <= '9') { + array[i] = array[i]*10 + *data - '0'; + } + else if (*data == sep) + i++; + else { + /* Skip spaces. */ + if (*data == ' ') + continue; + /* Unexpected character; true if it's the + terminator and we're finished. */ + if (*data == term && i == array_size - 1) + return len; + pr_debug("Char %u (got %u nums) `%c' unexpected\n", + len, i, *data); + return 0; + } + } + pr_debug("Failed to fill %u numbers separated by %c\n", + array_size, sep); + return 0; +} + +/* Grab port: number up to delimiter */ +static int get_port(const char *data, size_t dlen, char delim, + struct myct_man *cmd) +{ + uint16_t tmp_port = 0; + uint32_t i; + + for (i = 0; i < dlen; i++) { + /* Finished? */ + if (data[i] == delim) { + if (tmp_port == 0) + break; + cmd->u.port = htons(tmp_port); + pr_debug("get_port: return %d\n", tmp_port); + return i + 1; + } + else if (data[i] >= '0' && data[i] <= '9') + tmp_port = tmp_port*10 + data[i] - '0'; + else if (data[i] == ' ') /* Skip spaces */ + continue; + else { /* Some other crap */ + pr_debug("get_port: invalid char `%c'\n", data[i]); + break; + } + } + return 0; +} + +/* (ADDRESS=(PROTOCOL=tcp)(DEV=x)(HOST=a.b.c.d)(PORT=a)) */ +/* FIXME: handle hostnames */ + +/* Returns 0, or length of port number */ +static unsigned int +find_pattern(struct pkt_buff *pkt, unsigned int dataoff, size_t dlen, + struct myct_man *cmd, unsigned int *numoff) +{ + const char *data = (const char *)pktb_network_header(pkt) + dataoff + + sizeof(struct tns_header); + int length, offset, ret; + uint32_t array[4]; + const char *p, *start; + + p = strstr(data, "("); + if (!p) + return 0; + + p = strstr(p+1, "HOST="); + if (!p) { + pr_debug("HOST= not found\n"); + return 0; + } + + start = p + strlen("HOST="); + offset = (int)(p - data) + strlen("HOST="); + *numoff = offset + sizeof(struct tns_header); + data += offset; + + length = try_number(data, dlen - offset, array, 4, '.', ')'); + if (length == 0) + return 0; + + cmd->u3.ip = htonl((array[0] << 24) | (array[1] << 16) | + (array[2] << 8) | array[3]); + + p = strstr(data+length, "("); + if (!p) + return 0; + + p = strstr(p, "PORT="); + if (!p) { + pr_debug("PORT= not found\n"); + return 0; + } + + p += strlen("PORT="); + ret = get_port(p, dlen - offset - length, ')', cmd); + if (ret == 0) + return 0; + + p += ret; + return (int)(p - start); +} + +static inline uint16_t +nton(uint16_t len, unsigned int matchoff, unsigned int matchlen) +{ + uint32_t l = (uint32_t)ntohs(len) + matchoff - matchlen; + + return htons(l); +} + +/* So, this packet has hit the connection tracking matching code. + Mangle it, and change the expectation to match the new version. */ +static unsigned int +nf_nat_tns(struct pkt_buff *pkt, struct tns_header *tns, struct nf_expect *exp, + struct nf_conntrack *ct, int dir, + unsigned int matchoff, unsigned int matchlen) +{ + union nfct_attr_grp_addr newip; + char buffer[sizeof("255.255.255.255)(PORT=65535)")]; + unsigned int buflen; + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + uint16_t initial_port, port; + + /* Connection will come from wherever this packet goes, hence !dir */ + cthelper_get_addr_dst(ct, !dir, &newip); + + expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); + + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, !dir); + + /* 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. */ + 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 ret; + + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + ret = cthelper_add_expect(exp); + if (ret == 0) + break; + else if (ret != -EBUSY) { + port = 0; + break; + } + } + nfct_destroy(nat_tuple); + + if (port == 0) + return NF_DROP; + + buflen = snprintf(buffer, sizeof(buffer), + "%u.%u.%u.%u)(PORT=%u)", + ((unsigned char *)&newip.ip)[0], + ((unsigned char *)&newip.ip)[1], + ((unsigned char *)&newip.ip)[2], + ((unsigned char *)&newip.ip)[3], port); + if (!buflen) + goto out; + + if (!nfq_tcp_mangle_ipv4(pkt, matchoff, matchlen, buffer, buflen)) + goto out; + + if (buflen != matchlen) { + /* FIXME: recalculate checksum */ + tns->csum = 0; + tns->header_csum = 0; + + tns->len = nton(tns->len, matchlen, buflen); + if (tns->type == TNS_TYPE_REDIRECT) { + struct tns_redirect *r; + + r = (struct tns_redirect *)((char *)tns + sizeof(struct tns_header)); + + r->data_len = nton(r->data_len, matchlen, buflen); + } + } + + return NF_ACCEPT; + +out: + cthelper_del_expect(exp); + return NF_DROP; +} + +static int +tns_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct tcphdr *th; + struct tns_header *tns; + int dir = CTINFO2DIR(ctinfo); + unsigned int dataoff, datalen, numoff = 0, numlen; + struct tns_info *tns_info = myct->priv_data; + union nfct_attr_grp_addr addr; + struct nf_expect *exp = NULL; + struct myct_man cmd; + int ret = NF_ACCEPT; + + memset(&cmd, 0, sizeof(struct myct_man)); + memset(&addr, 0, sizeof(union nfct_attr_grp_addr)); + + /* Until there's been traffic both ways, don't look into TCP packets. */ + if (ctinfo != IP_CT_ESTABLISHED + && ctinfo != IP_CT_ESTABLISHED_REPLY) { + pr_debug("TNS: Conntrackinfo = %u\n", ctinfo); + goto out; + } + /* Parse server direction only */ + if (dir != MYCT_DIR_REPL) { + pr_debug("TNS: skip client direction\n"); + goto out; + } + + th = (struct tcphdr *) (pktb_network_header(pkt) + protoff); + + dataoff = protoff + th->doff * 4; + datalen = pktb_len(pkt); + + if (datalen < sizeof(struct tns_header)) { + pr_debug("TNS: skip packet with short header\n"); + goto out; + } + + tns = (struct tns_header *)(pktb_network_header(pkt) + dataoff); + + if (tns->type == TNS_TYPE_REDIRECT) { + struct tns_redirect *redirect; + + dataoff += sizeof(struct tns_header); + datalen -= sizeof(struct tns_header); + redirect = (struct tns_redirect *)(pktb_network_header(pkt) + dataoff); + tns_info->parse = false; + + if (ntohs(redirect->data_len) == 0) { + tns_info->parse = true; + goto out; + } + goto parse; + } + + /* Parse only the very next DATA packet */ + if (!(tns_info->parse && tns->type == TNS_TYPE_DATA)) { + tns_info->parse = false; + goto out; + } +parse: + numlen = find_pattern(pkt, dataoff, datalen, &cmd, &numoff); + tns_info->parse = false; + if (!numlen) + goto out; + + /* We refer to the reverse direction ("!dir") tuples here, + * because we're expecting something in the other direction. + * Doesn't matter unless NAT is happening. */ + cthelper_get_addr_src(myct->ct, !dir, &addr); + + exp = nfexp_new(); + if (exp == NULL) + goto out; + + if (cthelper_expect_init(exp, myct->ct, 0, + &addr, &cmd.u3, + IPPROTO_TCP, + NULL, &cmd.u.port, 0)) { + pr_debug("TNS: failed to init expectation\n"); + goto out_exp; + } + + /* Now, NAT might want to mangle the packet, and register the + * (possibly changed) expectation itself. + */ + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { + ret = nf_nat_tns(pkt, tns, exp, myct->ct, dir, + numoff + sizeof(struct tns_header), numlen); + goto out_exp; + } + + /* Can't expect this? Best to drop packet now. */ + if (cthelper_add_expect(exp) < 0) { + pr_debug("TNS: cannot add expectation: %s\n", + strerror(errno)); + ret = NF_DROP; + goto out_exp; + } + goto out; + +out_exp: + nfexp_destroy(exp); +out: + return ret; +} + +static struct ctd_helper tns_helper = { + .name = "tns", + .l4proto = IPPROTO_TCP, + .cb = tns_helper_cb, + .priv_data_len = sizeof(struct tns_info), + .policy = { + [0] = { + .name = "tns", + .expect_max = 1, + .expect_timeout = 300, + }, + }, +}; + +void __attribute__ ((constructor)) tns_init(void); + +void tns_init(void) +{ + helper_register(&tns_helper); +} -- cgit v1.2.3 From ff5eb25aa6963536e3f640c5c38c341ce02809bb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 25 May 2012 03:03:33 +0200 Subject: tests: conntrackd: add cthelper-test infrastructure This patch adds the automated testing infrastructure the user-space helpers. Basically, this adds the `cthelper-test' program that can be invoked from the command line: ./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 To test the helper with one PCAP file that contains traces of Oracle TNS traffic. It also provides tweaks to test the DNAT content mangling code: ./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 dnat This will also allow fuzzy testing of user-space helper, for further validation, not yet implemented. To compile this tool, you have to run: ./configure make check under the qa/cthelper-test/ directory. I'm doing like this because this directory is not included in the standalone tarball that make distcheck generates (I don't want to bloat it with development tools that can be retrieved from the git repository). Signed-off-by: Pablo Neira Ayuso --- tests/conntrackd/cthelper/.gitignore | 14 ++ tests/conntrackd/cthelper/Make_global.am | 7 + tests/conntrackd/cthelper/Makefile.am | 20 ++ tests/conntrackd/cthelper/README | 2 + tests/conntrackd/cthelper/configure.ac | 64 ++++++ tests/conntrackd/cthelper/ct.c | 91 +++++++++ tests/conntrackd/cthelper/ct.h | 22 +++ tests/conntrackd/cthelper/expect.c | 199 +++++++++++++++++++ tests/conntrackd/cthelper/l3_ipv4.c | 86 ++++++++ tests/conntrackd/cthelper/l4_tcp.c | 88 +++++++++ tests/conntrackd/cthelper/l4_udp.c | 88 +++++++++ tests/conntrackd/cthelper/main.c | 220 +++++++++++++++++++++ tests/conntrackd/cthelper/pcaps/nfsv3.pcap | Bin 0 -> 6824 bytes .../cthelper/pcaps/oracle-tns-redirect.pcap | Bin 0 -> 1095 bytes tests/conntrackd/cthelper/proto.c | 49 +++++ tests/conntrackd/cthelper/proto.h | 50 +++++ tests/conntrackd/cthelper/run-test.sh | 11 ++ tests/conntrackd/cthelper/test.h | 13 ++ 18 files changed, 1024 insertions(+) create mode 100644 tests/conntrackd/cthelper/.gitignore create mode 100644 tests/conntrackd/cthelper/Make_global.am create mode 100644 tests/conntrackd/cthelper/Makefile.am create mode 100644 tests/conntrackd/cthelper/README create mode 100644 tests/conntrackd/cthelper/configure.ac create mode 100755 tests/conntrackd/cthelper/ct.c create mode 100755 tests/conntrackd/cthelper/ct.h create mode 100644 tests/conntrackd/cthelper/expect.c create mode 100755 tests/conntrackd/cthelper/l3_ipv4.c create mode 100755 tests/conntrackd/cthelper/l4_tcp.c create mode 100755 tests/conntrackd/cthelper/l4_udp.c create mode 100755 tests/conntrackd/cthelper/main.c create mode 100644 tests/conntrackd/cthelper/pcaps/nfsv3.pcap create mode 100644 tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap create mode 100755 tests/conntrackd/cthelper/proto.c create mode 100755 tests/conntrackd/cthelper/proto.h create mode 100644 tests/conntrackd/cthelper/run-test.sh create mode 100644 tests/conntrackd/cthelper/test.h diff --git a/tests/conntrackd/cthelper/.gitignore b/tests/conntrackd/cthelper/.gitignore new file mode 100644 index 0000000..928e44b --- /dev/null +++ b/tests/conntrackd/cthelper/.gitignore @@ -0,0 +1,14 @@ +.deps/ +.libs/ +Makefile +Makefile.in +*.o +*.la +*.lo + +/aclocal.m4 +/autom4te.cache/ +/build-aux/ +/config.* +/configure +/libtool diff --git a/tests/conntrackd/cthelper/Make_global.am b/tests/conntrackd/cthelper/Make_global.am new file mode 100644 index 0000000..06785a1 --- /dev/null +++ b/tests/conntrackd/cthelper/Make_global.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I../../../include + +AM_CFLAGS = -std=gnu99 -W -Wall \ + -Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \ + -Wno-unused-parameter \ + ${LIBNETFILTER_CONNTRACK_CFLAGS} \ + ${LIBNETFILTER_CTTIMEOUT_CFLAGS} diff --git a/tests/conntrackd/cthelper/Makefile.am b/tests/conntrackd/cthelper/Makefile.am new file mode 100644 index 0000000..b8f0d42 --- /dev/null +++ b/tests/conntrackd/cthelper/Makefile.am @@ -0,0 +1,20 @@ +include $(top_srcdir)/Make_global.am + +check_PROGRAMS = cthelper-test + +cthelper_test_SOURCES = proto.c \ + ct.c \ + l3_ipv4.c \ + l4_tcp.c \ + l4_udp.c \ + expect.c \ + ../../../src/helpers.c \ + main.c + +cthelper_test_LDFLAGS = -dynamic \ + -lpcap \ + -ldl \ + -lmnl \ + -lnetfilter_queue \ + -lnetfilter_conntrack \ + -export-dynamic diff --git a/tests/conntrackd/cthelper/README b/tests/conntrackd/cthelper/README new file mode 100644 index 0000000..6e8b385 --- /dev/null +++ b/tests/conntrackd/cthelper/README @@ -0,0 +1,2 @@ +This directory contains PCAP files with traffic traces that we can use to test +the user-space helpers. diff --git a/tests/conntrackd/cthelper/configure.ac b/tests/conntrackd/cthelper/configure.ac new file mode 100644 index 0000000..8b3da5c --- /dev/null +++ b/tests/conntrackd/cthelper/configure.ac @@ -0,0 +1,64 @@ +AC_INIT(cthelper-test, 0.0.1, pablo@netfilter.org) +AC_CONFIG_AUX_DIR([build-aux]) + +AC_CANONICAL_HOST +AC_CONFIG_MACRO_DIR([m4]) +AM_INIT_AUTOMAKE([-Wall foreign subdir-objects + tar-pax no-dist-gzip dist-bzip2 1.6]) + +dnl kernel style compile messages +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +AC_SEARCH_LIBS([dlopen], [dl], [libdl_LIBS="$LIBS"; LIBS=""]) +AC_SUBST([libdl_LIBS]) + +AC_PROG_CC +AC_DISABLE_STATIC +AM_PROG_LIBTOOL +AC_PROG_INSTALL +AC_PROG_LN_S +AM_PROG_LEX +AC_PROG_YACC + +case "$host" in +*-*-linux*) ;; +*) AC_MSG_ERROR([Linux only, dude!]);; +esac + +PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.0]) +PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.0]) + +AC_CHECK_HEADERS(arpa/inet.h) +dnl check for inet_pton +AC_CHECK_FUNCS(inet_pton) +dnl Some systems have it, but not IPv6 +if test "$ac_cv_func_inet_pton" = "yes" ; then +AC_MSG_CHECKING(if inet_pton supports IPv6) +AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +int main() + { + struct in6_addr addr6; + if (inet_pton(AF_INET6, "::1", &addr6) < 1) + exit(1); + else + exit(0); + } + ]])],[ AC_MSG_RESULT(yes) + AC_DEFINE_UNQUOTED(HAVE_INET_PTON_IPV6, 1, [Define to 1 if inet_pton supports IPv6.]) + ],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)]) +fi + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/tests/conntrackd/cthelper/ct.c b/tests/conntrackd/cthelper/ct.c new file mode 100755 index 0000000..1c17336 --- /dev/null +++ b/tests/conntrackd/cthelper/ct.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include + +#include + +#include "proto.h" +#include "helper.h" +#include "myct.h" +#include "ct.h" + +static LIST_HEAD(ct_list); + +struct nf_ct_entry * +ct_alloc(const uint8_t *pkt, unsigned int l3hdr_len, + struct cthelper_proto_l2l3_helper *l3h, + struct cthelper_proto_l4_helper *l4h) +{ + struct nf_ct_entry *ct; + + ct = calloc(1, sizeof(struct nf_ct_entry)); + if (ct == NULL) + return NULL; + + ct->myct = calloc(1, sizeof(struct myct)); + if (ct->myct == NULL) { + free(ct); + return NULL; + } + ct->myct->ct = nfct_new(); + if (ct->myct->ct == NULL) { + free(ct->myct); + free(ct); + return NULL; + } + /* FIXME: use good private helper size */ + ct->myct->priv_data = calloc(1, 128); + if (ct->myct->priv_data == NULL) { + nfct_destroy(ct->myct->ct); + free(ct->myct); + free(ct); + return NULL; + } + + l3h->l3ct_build(pkt, ct->myct->ct); + l4h->l4ct_build(pkt + l3hdr_len, ct->myct->ct); + + return ct; +} + +struct nf_ct_entry * +ct_find(const uint8_t *pkt, unsigned int l3hdr_len, + struct cthelper_proto_l2l3_helper *l3h, + struct cthelper_proto_l4_helper *l4h, unsigned int *ctinfo) +{ + struct nf_ct_entry *cur; + + list_for_each_entry(cur, &ct_list, head) { + if (l3h->l3ct_cmp_orig(pkt, cur->myct->ct) && + l4h->l4ct_cmp_orig(pkt + l3hdr_len, cur->myct->ct)) { + *ctinfo = 0; + return cur; + } + if (l3h->l3ct_cmp_repl(pkt, cur->myct->ct) && + l4h->l4ct_cmp_repl(pkt + l3hdr_len, cur->myct->ct)) { + *ctinfo = IP_CT_IS_REPLY; + return cur; + } + } + return NULL; +} + +void ct_add(struct nf_ct_entry *ct) +{ + list_add(&ct->head, &ct_list); +} + +void ct_flush(void) +{ + struct nf_ct_entry *cur, *tmp; + + list_for_each_entry_safe(cur, tmp, &ct_list, head) { + list_del(&cur->head); + free(cur->myct->priv_data); + free(cur->myct->ct); + free(cur->myct); + free(cur); + } +} diff --git a/tests/conntrackd/cthelper/ct.h b/tests/conntrackd/cthelper/ct.h new file mode 100755 index 0000000..f01d49d --- /dev/null +++ b/tests/conntrackd/cthelper/ct.h @@ -0,0 +1,22 @@ +#ifndef _CT_H_ +#define _CT_H_ + +#include "../../../include/linux_list.h" +#include "../../../include/myct.h" + +struct nf_ct_entry { + struct list_head head; + struct myct *myct; +}; + +struct cthelper_proto_l2l3_helper; +struct cthelper_proto_l4_helper; + +struct nf_ct_entry *ct_alloc(const uint8_t *pkt, unsigned int l3hdr_len, struct cthelper_proto_l2l3_helper *l3h, struct cthelper_proto_l4_helper *l4h); + +struct nf_ct_entry *ct_find(const uint8_t *pkt, unsigned int l3hdr_len, struct cthelper_proto_l2l3_helper *l3h, struct cthelper_proto_l4_helper *l4h, unsigned int *ctinfo); + +void ct_add(struct nf_ct_entry *ct); +void ct_flush(void); + +#endif diff --git a/tests/conntrackd/cthelper/expect.c b/tests/conntrackd/cthelper/expect.c new file mode 100644 index 0000000..c667293 --- /dev/null +++ b/tests/conntrackd/cthelper/expect.c @@ -0,0 +1,199 @@ +/* + * (C) 2012 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation (or any later at your option). + * + * This code has been sponsored by Vyatta Inc. + */ + +#include "../../../include/helper.h" +#include "test.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +int +cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + uint32_t class, + union nfct_attr_grp_addr *saddr, + union nfct_attr_grp_addr *daddr, + uint8_t l4proto, uint16_t *sport, uint16_t *dport) +{ + struct nf_conntrack *expected, *mask; + + expected = nfct_new(); + if (!expected) + return -1; + + mask = nfct_new(); + if (!mask) + return -1; + + if (saddr) { + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + int i; + uint32_t addr[4] = {}; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_SRC, saddr->ip); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0xffffffff); + break; + 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, 0xffffffff, sizeof(uint32_t)); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(mask, ATTR_IPV6_SRC, addr); + break; + default: + break; + } + } else { + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + int i; + uint32_t addr[4] = {}; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_SRC, 0x00000000); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0x00000000); + break; + case AF_INET6: + for (i=0; i<4; i++) + memset(addr, 0x00000000, sizeof(uint32_t)); + + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(expected, ATTR_IPV6_SRC, addr); + + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(mask, ATTR_IPV6_SRC, addr); + break; + default: + break; + } + } + + if (sport) { + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_SRC, *sport); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0xffff); + break; + default: + break; + } + } else { + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_SRC, 0x0000); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0x0000); + break; + default: + break; + } + } + + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { + uint32_t addr[4] = {}; + int i; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(expected, ATTR_IPV4_DST, daddr->ip); + nfct_set_attr_u32(mask, ATTR_IPV4_DST, 0xffffffff); + break; + 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)); + + nfct_set_attr(mask, ATTR_IPV6_DST, addr); + break; + default: + break; + } + + switch(l4proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(expected, ATTR_PORT_DST, *dport); + nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); + nfct_set_attr_u16(mask, ATTR_PORT_DST, 0xffff); + break; + default: + break; + } + + nfexp_set_attr(exp, ATTR_EXP_MASTER, master); + nfexp_set_attr(exp, ATTR_EXP_EXPECTED, expected); + nfexp_set_attr(exp, ATTR_EXP_MASK, mask); + + nfct_destroy(expected); + nfct_destroy(mask); + + return 0; +} + +int cthelper_add_expect(struct nf_expect *exp) +{ + cthelper_test_stats.ct_expect_created++; + return 0; +} + +int cthelper_del_expect(struct nf_expect *exp) +{ + return 0; +} + +void +cthelper_get_addr_src(struct nf_conntrack *ct, int dir, + union nfct_attr_grp_addr *addr) +{ + switch (dir) { + case MYCT_DIR_ORIG: + nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_SRC, addr); + break; + case MYCT_DIR_REPL: + nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_SRC, addr); + break; + } +} + +void +cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, + union nfct_attr_grp_addr *addr) +{ + switch (dir) { + case MYCT_DIR_ORIG: + nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_DST, addr); + break; + case MYCT_DIR_REPL: + nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_DST, addr); + break; + } +} diff --git a/tests/conntrackd/cthelper/l3_ipv4.c b/tests/conntrackd/cthelper/l3_ipv4.c new file mode 100755 index 0000000..8edfd2e --- /dev/null +++ b/tests/conntrackd/cthelper/l3_ipv4.c @@ -0,0 +1,86 @@ +#include +#include +#include + +#include "proto.h" + +#include + +#define PRINT_CMP(...) + +static void +l3_ipv4_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct iphdr *iph = (const struct iphdr *)pkt; + + nfct_set_attr_u16(ct, ATTR_ORIG_L3PROTO, AF_INET); + nfct_set_attr_u16(ct, ATTR_REPL_L3PROTO, AF_INET); + nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_SRC, iph->saddr); + nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_DST, iph->daddr); + nfct_set_attr_u32(ct, ATTR_REPL_IPV4_SRC, iph->daddr); + nfct_set_attr_u32(ct, ATTR_REPL_IPV4_DST, iph->saddr); +} + +static int +l3_ipv4_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct iphdr *iph = (const struct iphdr *)pkt; + + PRINT_CMP("cmp_orig iph->saddr: %x == %x\n", + iph->saddr, nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC)); + PRINT_CMP("cmp_orig iph->daddr: %x == %x\n", + iph->daddr, nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST)); + + if (iph->saddr == nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC) && + iph->daddr == nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST)) + return 1; + + return 0; +} + +static int +l3_ipv4_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct iphdr *iph = (const struct iphdr *)pkt; + + PRINT_CMP("cmp_repl iph->saddr: %x == %x\n", + iph->saddr, nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC)); + PRINT_CMP("cmp_repl iph->daddr: %x == %x\n", + iph->daddr, nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST)); + + if (iph->saddr == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC) && + iph->daddr == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST)) + return 1; + + return 0; +} + +static int l3_ipv4_pkt_l4proto_num(const uint8_t *pkt) +{ + const struct iphdr *iph = (const struct iphdr *)pkt; + + return iph->protocol; +} + +static int l3_ipv4_pkt_l3hdr_len(const uint8_t *pkt) +{ + const struct iphdr *iph = (const struct iphdr *)pkt; + + return iph->ihl << 2; +} + +static struct cthelper_proto_l2l3_helper ipv4 = { + .l2protonum = ETH_P_IP, + .l3protonum = AF_INET, + .l2hdr_len = ETH_HLEN, + .l3ct_build = l3_ipv4_ct_build_tuple, + .l3ct_cmp_orig = l3_ipv4_ct_cmp_tuple_orig, + .l3ct_cmp_repl = l3_ipv4_ct_cmp_tuple_repl, + .l3pkt_hdr_len = l3_ipv4_pkt_l3hdr_len, + .l4pkt_proto = l3_ipv4_pkt_l4proto_num, +}; + +void l2l3_ipv4_init(void) +{ + cthelper_proto_l2l3_helper_register(&ipv4); +} diff --git a/tests/conntrackd/cthelper/l4_tcp.c b/tests/conntrackd/cthelper/l4_tcp.c new file mode 100755 index 0000000..f27c85d --- /dev/null +++ b/tests/conntrackd/cthelper/l4_tcp.c @@ -0,0 +1,88 @@ +#include +#include + +#include "proto.h" + +#include + +#define PRINT_CMP(...) + +static void l4_tcp_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct tcphdr *tcph = (const struct tcphdr *)pkt; + + nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, tcph->source); + nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, tcph->dest); + nfct_set_attr_u16(ct, ATTR_REPL_PORT_SRC, tcph->dest); + nfct_set_attr_u16(ct, ATTR_REPL_PORT_DST, tcph->source); +} + +static int l4_tcp_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct tcphdr *tcph = (const struct tcphdr *)pkt; + + PRINT_CMP("cmp_orig tcph->source: %u == %u\n", + tcph->source, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); + PRINT_CMP("cmp_orig tcph->dest: %u == %u\n", + tcph->dest, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); + + if (tcph->source == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) && + tcph->dest == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)) + return 1; + + return 0; +} + +static int +l4_tcp_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct tcphdr *tcph = (const struct tcphdr *)pkt; + + PRINT_CMP("cmp_repl tcph->source: %u == %u\n", + tcph->source, nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC)); + PRINT_CMP("cmp_repl tcph->dest: %u == %u\n", + tcph->dest, nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)); + + if (tcph->source == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC) && + tcph->dest == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)) + return 1; + + return 0; +} + +static int +l4_tcp_ct_cmp_port(struct nf_conntrack *ct, uint16_t port) +{ + PRINT_CMP("cmp_port src: %u == %u\n", + port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); + PRINT_CMP("cmp_port dst: %u == %u\n", + port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); + + if (port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) || + port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)) + return 1; + + return 0; +} + +static int l4_tcp_pkt_no_data(const uint8_t *pkt) +{ + const struct tcphdr *tcph = (const struct tcphdr *)pkt; + return tcph->syn || tcph->fin || tcph->rst || !tcph->psh; +} + +static struct cthelper_proto_l4_helper tcp = { + .l4protonum = IPPROTO_TCP, + .l4ct_build = l4_tcp_ct_build_tuple, + .l4ct_cmp_orig = l4_tcp_ct_cmp_tuple_orig, + .l4ct_cmp_repl = l4_tcp_ct_cmp_tuple_repl, + .l4ct_cmp_port = l4_tcp_ct_cmp_port, + .l4pkt_no_data = l4_tcp_pkt_no_data, +}; + +void l4_tcp_init(void) +{ + cthelper_proto_l4_helper_register(&tcp); +} diff --git a/tests/conntrackd/cthelper/l4_udp.c b/tests/conntrackd/cthelper/l4_udp.c new file mode 100755 index 0000000..4d52d0a --- /dev/null +++ b/tests/conntrackd/cthelper/l4_udp.c @@ -0,0 +1,88 @@ +#include +#include + +#include "proto.h" + +#include + +#define PRINT_CMP(...) + +static void l4_udp_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct udphdr *udph = (const struct udphdr *)pkt; + + nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, IPPROTO_UDP); + nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_UDP); + nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, udph->source); + nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, udph->dest); + nfct_set_attr_u16(ct, ATTR_REPL_PORT_SRC, udph->dest); + nfct_set_attr_u16(ct, ATTR_REPL_PORT_DST, udph->source); +} + +static int l4_udp_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct udphdr *udph = (const struct udphdr *)pkt; + + PRINT_CMP("cmp_orig udph->source: %u == %u\n", + udph->source, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); + PRINT_CMP("cmp_orig udph->dest: %u == %u\n", + udph->dest, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); + + if (udph->source == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) && + udph->dest == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)) + return 1; + + return 0; +} + +static int +l4_udp_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct) +{ + const struct udphdr *udph = (const struct udphdr *)pkt; + + PRINT_CMP("cmp_repl udph->source: %u == %u\n", + udph->source, nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC)); + PRINT_CMP("cmp_repl udph->dest: %u == %u\n", + udph->dest, nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)); + + if (udph->source == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC) && + udph->dest == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)) + return 1; + + return 0; +} + +static int +l4_udp_ct_cmp_port(struct nf_conntrack *ct, uint16_t port) +{ + PRINT_CMP("cmp_port src: %u == %u\n", + port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); + PRINT_CMP("cmp_port dst: %u == %u\n", + port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); + + if (port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) || + port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)) + return 1; + + return 0; +} + +static int l4_udp_pkt_no_data(const uint8_t *pkt) +{ + /* UDP has no control packets. */ + return 1; +} + +static struct cthelper_proto_l4_helper tcp = { + .l4protonum = IPPROTO_UDP, + .l4ct_build = l4_udp_ct_build_tuple, + .l4ct_cmp_orig = l4_udp_ct_cmp_tuple_orig, + .l4ct_cmp_repl = l4_udp_ct_cmp_tuple_repl, + .l4ct_cmp_port = l4_udp_ct_cmp_port, + .l4pkt_no_data = l4_udp_pkt_no_data, +}; + +void l4_udp_init(void) +{ + cthelper_proto_l4_helper_register(&tcp); +} diff --git a/tests/conntrackd/cthelper/main.c b/tests/conntrackd/cthelper/main.c new file mode 100755 index 0000000..f229c5d --- /dev/null +++ b/tests/conntrackd/cthelper/main.c @@ -0,0 +1,220 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ct.h" +#include "proto.h" +#include "../../../include/helper.h" +#include "test.h" + +#include + +struct cthelper_test_stats cthelper_test_stats; + +enum { + TEST_NORMAL = 0, + TEST_DNAT, +}; + +static int +cthelper_process_packet(const uint8_t *pkt, uint32_t pktlen, + struct ctd_helper *h, int proto, uint16_t port, + int type) +{ + struct pkt_buff *pktb; + struct cthelper_proto_l2l3_helper *l3h; + struct cthelper_proto_l4_helper *l4h; + unsigned int l3hdr_len, l4protonum; + struct nf_ct_entry *ct; + int ret, this_proto; + uint32_t dataoff, ctinfo = 0; + + l3h = cthelper_proto_l2l3_helper_find(pkt, &l4protonum, &l3hdr_len); + if (l3h == NULL) { + fprintf(stderr, "Unsupported layer 3 protocol, skipping.\n"); + return -1; + } + + l4h = cthelper_proto_l4_helper_find(pkt, l4protonum); + if (l4h == NULL) { + fprintf(stderr, "Unsupported layer 4 protocol, skipping.\n"); + return -1; + } + /* get layer 3 header. */ + pkt += l3h->l2hdr_len; + pktlen -= l3h->l2hdr_len; + + /* skip packet with mismatching protocol */ + this_proto = l3h->l4pkt_proto(pkt); + if (this_proto != proto) { + cthelper_test_stats.pkt_mismatch_proto++; + return 0; + } + + /* Look for the fake conntrack. */ + ct = ct_find(pkt, l3hdr_len, l3h, l4h, &ctinfo); + if (ct == NULL) { + /* It doesn't exist any, create one. */ + ct = ct_alloc(pkt, l3hdr_len, l3h, l4h); + if (ct == NULL) { + fprintf(stderr, "Not enough memory\n"); + return -1; + } + ct_add(ct); + ctinfo += IP_CT_NEW; + } else + ctinfo += IP_CT_ESTABLISHED; + + /* skip packets with mismatching ports */ + if (!l4h->l4ct_cmp_port(ct->myct->ct, ntohs(port))) { + cthelper_test_stats.pkt_mismatch_port++; + return -1; + } + + /* + * FIXME: reminder, implement this below in the kernel for cthelper. + */ + + /* This packet contains no data, skip it. */ +/* if (l4h->l4pkt_no_data && l4h->l4pkt_no_data(pkt + l3hdr_len)) { + NFG_DEBUG("skipping packet with no data\n"); + continue; + } */ + + /* Create the fake network buffer. */ + pktb = pktb_alloc(AF_INET, pkt, pktlen, 128); + if (pktb == NULL) { + fprintf(stderr, "Not enough memory\n"); + return -1; + } + + dataoff = l3h->l3pkt_hdr_len(pkt); + if (dataoff > pktb_len(pktb)) { + fprintf(stderr, "wrong layer 3 offset: %d > %d\n", + dataoff, pktb_len(pktb)); + return -1; + } + + /* tweak to run DNAT mangling code using the same PCAP file. */ + if (type == TEST_DNAT) { + struct nf_conntrack *tmp = ct->myct->ct; + /* as long as this is tested, who cares the destination IP? */ + in_addr_t addr = inet_addr("1.1.1.1"); + + /* clone the real conntrack, to add DNAT information */ + ct->myct->ct = nfct_clone(ct->myct->ct); + /* set fake DNAT information */ + nfct_set_attr_u32(ct->myct->ct, ATTR_STATUS, IPS_DST_NAT); + nfct_set_attr_u32(ct->myct->ct, ATTR_ORIG_IPV4_DST, addr); + /* pass it to helper */ + ret = h->cb(pktb, dataoff, ct->myct, ctinfo); + /* restore real conntrack */ + nfct_destroy(ct->myct->ct); + ct->myct->ct = tmp; + + if (pktb_mangled(pktb)) { + int i; + uint8_t *data = pktb_network_header(pktb); + + printf("\e[1;31mmangled content: ", pktb_len(pktb)); + + for (i=0; i < pktb_len(pktb); i++) + printf("%c", data[i]); + + printf("\e[0m\n"); + } + } else + ret = h->cb(pktb, dataoff, ct->myct, ctinfo); + + pktb_free(pktb); + + return ret; +} + +static int +cthelper_test(const char *pcapfile, const char *helper_name, + int l4proto, uint16_t port, int type) +{ + struct pcap_pkthdr pcaph; + char errbuf[PCAP_ERRBUF_SIZE]; + const u_char *pkt; + pcap_t *handle; + struct ctd_helper *h; + + h = helper_find("/usr/lib/conntrack-tools", + helper_name, l4proto, RTLD_NOW); + if (h == NULL) { + fprintf(stderr, "couldn't find helper: %s\n", helper_name); + return -1; + } + + handle = pcap_open_offline(pcapfile, errbuf); + if (handle == NULL) { + fprintf(stderr, "couldn't open pcap file %s: %s\n", + pcapfile, errbuf); + return -1; + } + while ((pkt = pcap_next(handle, &pcaph)) != NULL) { + cthelper_test_stats.pkts++; + cthelper_process_packet(pkt, pcaph.caplen, h, l4proto, port, + type); + } + + ct_flush(); + pcap_close(handle); + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret, l4proto, type = TEST_NORMAL; + + if (argc < 5 || argc > 6) { + fprintf(stderr, "Wrong usage:\n"); + fprintf(stderr, "%s " + " [dnat]\n", + argv[0]); + fprintf(stderr, "example: %s file.pcap ftp tcp 21\n", argv[0]); + exit(EXIT_FAILURE); + } + if (strncmp("tcp", argv[3], strlen("tcp")) == 0) + l4proto = IPPROTO_TCP; + else if (strncmp("udp", argv[3], strlen("udp")) == 0) + l4proto = IPPROTO_UDP; + else { + fprintf(stderr, "%s not supported, send a patch to Pablo\n", + argv[3]); + exit(EXIT_FAILURE); + } + if (argc == 6) { + if (strncmp("dnat", argv[5], strlen("dnat")) == 0) { + type = TEST_DNAT; + printf("test dnat\n"); + } + } + + /* Initialization of supported layer 3 and 4 protocols here. */ + l2l3_ipv4_init(); + l4_tcp_init(); + l4_udp_init(); + + if (cthelper_test(argv[1], argv[2], l4proto, atoi(argv[4]), type) < 0) + ret = EXIT_FAILURE; + else + ret = EXIT_SUCCESS; + + printf("\e[1;34mTest results: expect_created=%d packets=%d " + "packets_skipped=%d\e[0m\n", + cthelper_test_stats.ct_expect_created, + cthelper_test_stats.pkts, + cthelper_test_stats.pkt_mismatch_proto + + cthelper_test_stats.pkt_mismatch_port); + + return ret; +} diff --git a/tests/conntrackd/cthelper/pcaps/nfsv3.pcap b/tests/conntrackd/cthelper/pcaps/nfsv3.pcap new file mode 100644 index 0000000..04606bd Binary files /dev/null and b/tests/conntrackd/cthelper/pcaps/nfsv3.pcap differ diff --git a/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap b/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap new file mode 100644 index 0000000..32f8952 Binary files /dev/null and b/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap differ diff --git a/tests/conntrackd/cthelper/proto.c b/tests/conntrackd/cthelper/proto.c new file mode 100755 index 0000000..6a1f345 --- /dev/null +++ b/tests/conntrackd/cthelper/proto.c @@ -0,0 +1,49 @@ +#include +#include +#include + +#include "linux_list.h" +#include "proto.h" + +static LIST_HEAD(l2l3_helper_list); +static LIST_HEAD(l4_helper_list); + +struct cthelper_proto_l2l3_helper * +cthelper_proto_l2l3_helper_find(const uint8_t *pkt, + unsigned int *l4protonum, + unsigned int *l3hdr_len) +{ + const struct ethhdr *eh = (const struct ethhdr *)pkt; + struct cthelper_proto_l2l3_helper *cur; + + list_for_each_entry(cur, &l2l3_helper_list, head) { + if (ntohs(cur->l2protonum) == eh->h_proto) { + *l4protonum = cur->l4pkt_proto(pkt + ETH_HLEN); + *l3hdr_len = cur->l3pkt_hdr_len(pkt + ETH_HLEN); + return cur; + } + } + return NULL; +} + +void cthelper_proto_l2l3_helper_register(struct cthelper_proto_l2l3_helper *h) +{ + list_add(&h->head, &l2l3_helper_list); +} + +struct cthelper_proto_l4_helper * +cthelper_proto_l4_helper_find(const uint8_t *pkt, unsigned int l4protocol) +{ + struct cthelper_proto_l4_helper *cur; + + list_for_each_entry(cur, &l4_helper_list, head) { + if (cur->l4protonum == l4protocol) + return cur; + } + return NULL; +} + +void cthelper_proto_l4_helper_register(struct cthelper_proto_l4_helper *h) +{ + list_add(&h->head, &l4_helper_list); +} diff --git a/tests/conntrackd/cthelper/proto.h b/tests/conntrackd/cthelper/proto.h new file mode 100755 index 0000000..9e99eea --- /dev/null +++ b/tests/conntrackd/cthelper/proto.h @@ -0,0 +1,50 @@ +#ifndef _HELPER_H_ +#define _HELPER_H_ + +#include + +#include "../../../include/linux_list.h" + +struct nf_conntrack; + +struct cthelper_proto_l4_helper { + struct list_head head; + + unsigned int l4protonum; + + void (*l4ct_build)(const uint8_t *pkt, struct nf_conntrack *ct); + int (*l4ct_cmp_orig)(const uint8_t *pkt, struct nf_conntrack *ct); + int (*l4ct_cmp_repl)(const uint8_t *pkt, struct nf_conntrack *ct); + int (*l4ct_cmp_port)(struct nf_conntrack *ct, uint16_t port); + + int (*l4pkt_no_data)(const uint8_t *pkt); +}; + +struct cthelper_proto_l2l3_helper { + struct list_head head; + + unsigned int l2protonum; + unsigned int l2hdr_len; + + unsigned int l3protonum; + + void (*l3ct_build)(const uint8_t *pkt, struct nf_conntrack *ct); + int (*l3ct_cmp_orig)(const uint8_t *pkt, struct nf_conntrack *ct); + int (*l3ct_cmp_repl)(const uint8_t *pkt, struct nf_conntrack *ct); + + int (*l3pkt_hdr_len)(const uint8_t *pkt); + int (*l4pkt_proto)(const uint8_t *pkt); +}; + +struct cthelper_proto_l2l3_helper *cthelper_proto_l2l3_helper_find(const uint8_t *pkt, unsigned int *l4protonum, unsigned int *l3hdr_len); +void cthelper_proto_l2l3_helper_register(struct cthelper_proto_l2l3_helper *h); + +struct cthelper_proto_l4_helper *cthelper_proto_l4_helper_find(const uint8_t *pkt, unsigned int l4protonum); +void cthelper_proto_l4_helper_register(struct cthelper_proto_l4_helper *h); + +/* Initialization of supported protocols here. */ +void l2l3_ipv4_init(void); +void l4_tcp_init(void); +void l4_udp_init(void); + +#endif diff --git a/tests/conntrackd/cthelper/run-test.sh b/tests/conntrackd/cthelper/run-test.sh new file mode 100644 index 0000000..fe31602 --- /dev/null +++ b/tests/conntrackd/cthelper/run-test.sh @@ -0,0 +1,11 @@ +echo "Running test for oracle TNS port 1521" +./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 + +echo "Running test for oracle TNS port 1521" +./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 dnat + +echo "Running test for NFSv3 UDP port 111" +./cthelper-test pcaps/nfsv3.pcap rpc udp 111 + +echo "Running test for NFSv3 TCP port 111" +./cthelper-test pcaps/nfsv3.pcap rpc tcp 111 diff --git a/tests/conntrackd/cthelper/test.h b/tests/conntrackd/cthelper/test.h new file mode 100644 index 0000000..4f5a6b6 --- /dev/null +++ b/tests/conntrackd/cthelper/test.h @@ -0,0 +1,13 @@ +#ifndef _CTHELPER_TEST_H_ +#define _CTHELPER_TEST_H_ + +struct cthelper_test_stats { + int pkts; + int pkt_mismatch_proto; + int pkt_mismatch_port; + int ct_expect_created; +}; + +extern struct cthelper_test_stats cthelper_test_stats; + +#endif -- cgit v1.2.3 From f9ad41077c473884f33c6677f81119614d5a8eb2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 1 Aug 2012 19:36:01 +0200 Subject: nfct: helper: use CONNTRACKD_LIB_DIR variable set during configuration Instead of hardcoded path to /usr/lib/conntrack-tools/ which might not be true if options like --prefix with different location is passed to conntrack. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 5 +++++ src/nfct-extensions/helper.c | 8 ++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 5d1860d..27ad01b 100644 --- a/configure.ac +++ b/configure.ac @@ -119,5 +119,10 @@ dnl debug/src/Makefile dnl extensions/Makefile dnl src/Makefile]) +if test ! -z "$libdir"; then + MODULE_DIR="\\\"$libdir/conntrack-tools/\\\"" + CFLAGS="$CFLAGS -DCONNTRACKD_LIB_DIR=$MODULE_DIR" +fi + AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile src/helpers/Makefile]) AC_OUTPUT diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index e8f85bb..f91fc41 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -202,9 +202,7 @@ int nfct_cmd_helper_add(int argc, char *argv[]) return -1; } - /* XXX use prefix defined in configure.ac. */ - helper = helper_find("/usr/lib/conntrack-tools", - argv[3], l4proto, RTLD_LAZY); + helper = helper_find(CONNTRACKD_LIB_DIR, argv[3], l4proto, RTLD_LAZY); if (helper == NULL) { nfct_perror("that helper is not supported"); return -1; @@ -558,9 +556,7 @@ int nfct_cmd_helper_disable(int argc, char *argv[]) return -1; } - /* XXX use prefix defined in configure.ac. */ - helper = helper_find("/usr/lib/conntrack-tools", - argv[3], l4proto, RTLD_LAZY); + helper = helper_find(CONNTRACKD_LIB_DIR, argv[3], l4proto, RTLD_LAZY); if (helper == NULL) { nfct_perror("that helper is not supported"); return -1; -- cgit v1.2.3 From 18bfa4bfb3bb875c36756272bb653c33e338c776 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Thu, 2 Aug 2012 05:33:56 +0000 Subject: conntrackd: don't resync expectations if such sync has been disabled conntrackd was segfaulting with `ExpectationSync` set to `Off` and PollSecs (polling mode) in use. Signed-off-by: Vincent Bernat Signed-off-by: Pablo Neira Ayuso --- src/ctnl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ctnl.c b/src/ctnl.c index 107cd5d..bb54727 100644 --- a/src/ctnl.c +++ b/src/ctnl.c @@ -164,7 +164,9 @@ static void do_polling_alarm(struct alarm_block *a, void *data) STATE(mode)->internal->exp.purge(); nl_send_resync(STATE(resync)); - nl_send_expect_resync(STATE(resync)); + if (CONFIG(flags) & CTD_EXPECT) + nl_send_expect_resync(STATE(resync)); + add_alarm(&STATE(polling_alarm), CONFIG(poll_kernel_secs), 0); } -- cgit v1.2.3 From 3733cabc2640d00a0fc3aee2d37d1bb9d097fc0f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 21 Aug 2012 13:12:04 +0200 Subject: cthelper: disable debugging information by default The pr_debug call should be converted to use dlog instead. I'll try to add some some configuration parameter to enable/disable debugging in runtime. This is not very flexible. Signed-off-by: Pablo Neira Ayuso --- include/helper.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/helper.h b/include/helper.h index 02ff3df..329fd2d 100644 --- a/include/helper.h +++ b/include/helper.h @@ -99,6 +99,12 @@ enum ip_conntrack_info { #define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL) -#define pr_debug printf +#if 0 +#define pr_debug(fmt, arg...) \ + printf(fmt, ##arg) +#else +#define pr_debug(fmt, arg...) \ + ({ if (0) printf(fmt, ##arg); 0; }) +#endif #endif -- cgit v1.2.3 From 137b0a2ac9568638be078126ca92ac62ca51e1f4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 21 Aug 2012 13:46:02 +0200 Subject: cthelper: ftp: fix EPRT case for IPv4 %pI4 also exists in the Linux kernel. It would be good to have some generic functions to convert binary data to address string. Later. Signed-off-by: Pablo Neira Ayuso --- src/helpers/ftp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c index 962020b..2c8dcd6 100644 --- a/src/helpers/ftp.c +++ b/src/helpers/ftp.c @@ -346,7 +346,12 @@ static int nf_nat_ftp_fmt_cmd(enum nf_ct_ftp_type type, port >> 8, port & 0xFF); case NF_CT_FTP_EPRT: - return snprintf(buffer, buflen, "|1|%pI4|%u|", &addr, port); + return snprintf(buffer, buflen, "|1|%u.%u.%u.%u|%u|", + ((unsigned char *)&addr)[0], + ((unsigned char *)&addr)[1], + ((unsigned char *)&addr)[2], + ((unsigned char *)&addr)[3], + port); case NF_CT_FTP_EPSV: return snprintf(buffer, buflen, "|||%u|", port); } -- cgit v1.2.3 From c9698ed05f450f9c32b8c1342c42b584988102de Mon Sep 17 00:00:00 2001 From: Ansis Atteka Date: Thu, 23 Aug 2012 15:27:22 +0000 Subject: tests: conntrackd: fix compile errors and warnings This patch fixes few compile warnings and errors. Signed-off-by: Ansis Atteka Signed-off-by: Pablo Neira Ayuso --- tests/conntrackd/cthelper/expect.c | 3 ++- tests/conntrackd/cthelper/main.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/conntrackd/cthelper/expect.c b/tests/conntrackd/cthelper/expect.c index c667293..d1d1d28 100644 --- a/tests/conntrackd/cthelper/expect.c +++ b/tests/conntrackd/cthelper/expect.c @@ -25,7 +25,8 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, uint32_t class, union nfct_attr_grp_addr *saddr, union nfct_attr_grp_addr *daddr, - uint8_t l4proto, uint16_t *sport, uint16_t *dport) + uint8_t l4proto, uint16_t *sport, uint16_t *dport, + uint32_t flags) { struct nf_conntrack *expected, *mask; diff --git a/tests/conntrackd/cthelper/main.c b/tests/conntrackd/cthelper/main.c index f229c5d..7021f00 100755 --- a/tests/conntrackd/cthelper/main.c +++ b/tests/conntrackd/cthelper/main.c @@ -119,10 +119,10 @@ cthelper_process_packet(const uint8_t *pkt, uint32_t pktlen, ct->myct->ct = tmp; if (pktb_mangled(pktb)) { - int i; + uint32_t i; uint8_t *data = pktb_network_header(pktb); - printf("\e[1;31mmangled content: ", pktb_len(pktb)); + printf("\e[1;31mmangled content: "); for (i=0; i < pktb_len(pktb); i++) printf("%c", data[i]); -- cgit v1.2.3 From 46faeab56cf4117f41cb6f1f1c40a9c18a81372f Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 8 Sep 2012 21:39:21 +0200 Subject: conntrackd: parse: fix wrong maximum length for ATTR_EXP_FN It was set to NFCT_HELPER_NAME_MAX (16 bytes), but we have function names that are larger, eg. nf-nat-follow-master which is 18 bytes long. This leads to hitting malformed message while synchronizing expectations. I'll add some new constant to libnetfilter_conntrack instead of hardcoding this, later. Reported-by: Gaurav Sinha Signed-off-by: Pablo Neira Ayuso --- src/parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse.c b/src/parse.c index 1b83f81..8ce4495 100644 --- a/src/parse.c +++ b/src/parse.c @@ -397,7 +397,7 @@ static struct exp_parser { [NTA_EXP_FN] = { .parse = exp_parse_str, .exp_attr = ATTR_EXP_FN, - .max_size = NFCT_HELPER_NAME_MAX, + .max_size = 32, /* XXX: artificial limit */ }, }; -- cgit v1.2.3 From febb3cceac1889fb6558b8ef40ac733072fdcd47 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 10 Sep 2012 13:17:24 +0200 Subject: conntrackd: cthelper: add QueueLen option This patch adds the QueueLen option, that allows you to increase the maximum number of packets waiting in the nfnetlink_queue to receive a verdict from userspace. Rising the default value (1024) is useful to avoid hitting the following error message: "nf_queue: full at X entries, dropping packets(s)". Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 13 +++++++++++++ include/helper.h | 1 + src/cthelper.c | 6 ++++-- src/read_config_lex.l | 1 + src/read_config_yy.y | 23 +++++++++++++++++++++-- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 80f1f92..56f5162 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -14,6 +14,16 @@ Helper { # the kernel. # QueueNum 0 + + # + # Maximum number of packets waiting in the queue to receive + # a verdict from user-space. Default is 1024. + # + # Rise value if you hit the following error message: + # "nf_queue: full at X entries, dropping packets(s)" + # + QueueLen 10240 + # # Set the Expectation policy for this helper. # @@ -30,6 +40,7 @@ Helper { } Type rpc inet tcp { QueueNum 1 + QueueLen 10240 Policy rpc { ExpectMax 1 ExpectTimeout 300 @@ -37,6 +48,7 @@ Helper { } Type rpc inet udp { QueueNum 2 + QueueLen 10240 Policy rpc { ExpectMax 1 ExpectTimeout 300 @@ -44,6 +56,7 @@ Helper { } Type tns inet tcp { QueueNum 3 + QueueLen 10240 Policy tns { ExpectMax 1 ExpectTimeout 300 diff --git a/include/helper.h b/include/helper.h index 329fd2d..9d96fb7 100644 --- a/include/helper.h +++ b/include/helper.h @@ -35,6 +35,7 @@ struct ctd_helper { struct ctd_helper_instance { struct list_head head; uint32_t queue_num; + uint32_t queue_len; uint16_t l3proto; uint8_t l4proto; struct ctd_helper *helper; diff --git a/src/cthelper.c b/src/cthelper.c index c119869..307be96 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -353,8 +353,9 @@ static int cthelper_setup(struct ctd_helper_instance *cur) nfct_helper_attr_set_u32(t, NFCTH_ATTR_STATUS, NFCT_HELPER_STATUS_ENABLED); - dlog(LOG_NOTICE, "configuring helper `%s' with queuenum=%d", - cur->helper->name, cur->queue_num); + dlog(LOG_NOTICE, "configuring helper `%s' with queuenum=%d and " + "queuelen=%d", cur->helper->name, cur->queue_num, + cur->queue_len); for (j=0; jqueue_len)); if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { dlog(LOG_ERR, "failed to send configuration"); diff --git a/src/read_config_lex.l b/src/read_config_lex.l index 31fa32e..bec2d81 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -144,6 +144,7 @@ notrack [N|n][O|o][T|t][R|r][A|a][C|c][K|k] "ErrorQueueLength" { return T_ERROR_QUEUE_LENGTH; } "Helper" { return T_HELPER; } "QueueNum" { return T_HELPER_QUEUE_NUM; } +"QueueLen" { return T_HELPER_QUEUE_LEN; } "Policy" { return T_HELPER_POLICY; } "ExpectMax" { return T_HELPER_EXPECT_MAX; } "ExpectTimeout" { return T_HELPER_EXPECT_TIMEOUT; } diff --git a/src/read_config_yy.y b/src/read_config_yy.y index c9235d3..72a9654 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -56,6 +56,7 @@ struct stack symbol_stack; enum { SYMBOL_HELPER_QUEUE_NUM, + SYMBOL_HELPER_QUEUE_LEN, SYMBOL_HELPER_POLICY_EXPECT_ROOT, SYMBOL_HELPER_EXPECT_POLICY_LEAF, }; @@ -86,8 +87,8 @@ enum { %token T_SCHEDULER T_TYPE T_PRIO T_NETLINK_EVENTS_RELIABLE %token T_DISABLE_INTERNAL_CACHE T_DISABLE_EXTERNAL_CACHE T_ERROR_QUEUE_LENGTH %token T_OPTIONS T_TCP_WINDOW_TRACKING T_EXPECT_SYNC -%token T_HELPER T_HELPER_QUEUE_NUM T_HELPER_POLICY T_HELPER_EXPECT_MAX -%token T_HELPER_EXPECT_TIMEOUT +%token T_HELPER T_HELPER_QUEUE_NUM T_HELPER_QUEUE_LEN T_HELPER_POLICY +%token T_HELPER_EXPECT_TIMEOUT T_HELPER_EXPECT_MAX %token T_IP T_PATH_VAL %token T_NUMBER @@ -1639,6 +1640,13 @@ helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}' stack_item_free(e); break; } + case SYMBOL_HELPER_QUEUE_LEN: { + int *qlen = (int *) &e->data; + + helper_inst->queue_len = *qlen; + stack_item_free(e); + break; + } case SYMBOL_HELPER_POLICY_EXPECT_ROOT: { struct ctd_helper_policy *pol = (struct ctd_helper_policy *) &e->data; @@ -1696,6 +1704,17 @@ helper_type: T_HELPER_QUEUE_NUM T_NUMBER stack_item_push(&symbol_stack, e); }; +helper_type: T_HELPER_QUEUE_LEN T_NUMBER +{ + int *qlen; + struct stack_item *e; + + e = stack_item_alloc(SYMBOL_HELPER_QUEUE_LEN, sizeof(int)); + qlen = (int *) e->data; + *qlen = $2; + stack_item_push(&symbol_stack, e); +}; + helper_type: T_HELPER_POLICY T_STRING '{' helper_policy_list '}' { struct stack_item *e; -- cgit v1.2.3 From a95338a1715b025bf1b39136ba10de5907c1080b Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Fri, 21 Sep 2012 03:55:39 +0000 Subject: build: fix libraries dependencies in Makefiles Several includes are missing when netfilter libs are not in the standard path. Signed-off-by: Nicolas Dichtel Signed-off-by: Pablo Neira Ayuso --- Make_global.am | 4 +++- src/Makefile.am | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Make_global.am b/Make_global.am index 7b5342d..23c7dd0 100644 --- a/Make_global.am +++ b/Make_global.am @@ -4,4 +4,6 @@ AM_CFLAGS = -std=gnu99 -W -Wall \ -Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \ -Wno-unused-parameter ${LIBNFNETLINK_CFLAGS} ${LIBMNL_CFLAGS} \ ${LIBNETFILTER_CONNTRACK_CFLAGS} \ - ${LIBNETFILTER_CTTIMEOUT_CFLAGS} + ${LIBNETFILTER_CTTIMEOUT_CFLAGS} \ + ${LIBNETFILTER_QUEUE_CFLAGS} \ + ${LIBNETFILTER_CTHELPER_CFLAGS} diff --git a/src/Makefile.am b/src/Makefile.am index d8074d2..ec03e46 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,7 +9,7 @@ CLEANFILES = read_config_yy.c read_config_lex.c 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} +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 \ @@ -46,7 +46,7 @@ read_config_yy.o read_config_lex.o: AM_CFLAGS += -Wno-missing-prototypes -Wno-mi conntrackd_LDADD = ${LIBMNL_LIBS} ${LIBNETFILTER_CONNTRACK_LIBS} \ ${LIBNETFILTER_QUEUE_LIBS} ${LIBNETFILTER_CTHELPER_LIBS} \ - ${libdl_LIBS} + ${libdl_LIBS} ${LIBNFNETLINK_LIBS} conntrackd_LDFLAGS = -export-dynamic -- cgit v1.2.3 From 3f845636159298fb18b6d6c455066d0344a61bee Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 3 Oct 2012 22:19:25 +0200 Subject: conntrackd: fix crash if ExpectationSync is enabled on old Linux kernels ExpectationSync requires Linux kernel >= 3.5 to work sanely, document this. Still, we don't want to crash if someone enables expectation sync with old Linux kernels (like 2.6.32). Reported-by: James Gutholm Tested-by: James Gutholm Signed-off-by: Pablo Neira Ayuso --- doc/manual/conntrack-tools.tmpl | 7 +++++++ doc/sync/alarm/conntrackd.conf | 3 ++- doc/sync/ftfw/conntrackd.conf | 3 ++- doc/sync/notrack/conntrackd.conf | 3 ++- src/build.c | 3 ++- src/filter.c | 12 +++++++++++- 6 files changed, 26 insertions(+), 5 deletions(-) diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index 47e6f84..63a53e4 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -660,6 +660,13 @@ Sync { Synchronization of expectations + Check your Linux kernel version first + + The synchronization of expectations require a Linux kernel >= 3.5 + to work appropriately. + + + The connection tracking system provides helpers that allows you to filter multi-flow application protocols like FTP, H.323 and SIP among many others. These protocols usually split the control and data traffic in diff --git a/doc/sync/alarm/conntrackd.conf b/doc/sync/alarm/conntrackd.conf index b9520fb..0223745 100644 --- a/doc/sync/alarm/conntrackd.conf +++ b/doc/sync/alarm/conntrackd.conf @@ -194,7 +194,8 @@ Sync { # Set this option on if you want to enable the synchronization # of expectations. You have to specify the list of helpers that - # you want to enable. Default is off. + # you want to enable. Default is off. This feature requires + # a Linux kernel >= 3.5. # # ExpectationSync { # ftp diff --git a/doc/sync/ftfw/conntrackd.conf b/doc/sync/ftfw/conntrackd.conf index 53a7d0f..65e7b77 100644 --- a/doc/sync/ftfw/conntrackd.conf +++ b/doc/sync/ftfw/conntrackd.conf @@ -217,7 +217,8 @@ Sync { # Set this option on if you want to enable the synchronization # of expectations. You have to specify the list of helpers that - # you want to enable. Default is off. + # you want to enable. Default is off. This feature requires + # a Linux kernel >= 3.5. # # ExpectationSync { # ftp diff --git a/doc/sync/notrack/conntrackd.conf b/doc/sync/notrack/conntrackd.conf index 11f022e..3d036fb 100644 --- a/doc/sync/notrack/conntrackd.conf +++ b/doc/sync/notrack/conntrackd.conf @@ -256,7 +256,8 @@ Sync { # Set this option on if you want to enable the synchronization # of expectations. You have to specify the list of helpers that - # you want to enable. Default is off. + # you want to enable. Default is off. This feature requires + # a Linux kernel >= 3.5. # # ExpectationSync { # ftp diff --git a/src/build.c b/src/build.c index 7d4ef12..e15eb4f 100644 --- a/src/build.c +++ b/src/build.c @@ -356,7 +356,8 @@ void exp2msg(const struct nf_expect *exp, struct nethdr *n) exp_build_u32(exp, ATTR_EXP_NAT_DIR, n, NTA_EXP_NAT_DIR); } - exp_build_str(exp, ATTR_EXP_HELPER_NAME, n, NTA_EXP_HELPER_NAME); + if (nfexp_attr_is_set(exp, ATTR_EXP_HELPER_NAME)) + exp_build_str(exp, ATTR_EXP_HELPER_NAME, n, NTA_EXP_HELPER_NAME); if (nfexp_attr_is_set(exp, ATTR_EXP_FN)) exp_build_str(exp, ATTR_EXP_FN, n, NTA_EXP_FN); } diff --git a/src/filter.c b/src/filter.c index 39dd4ca..02a8078 100644 --- a/src/filter.c +++ b/src/filter.c @@ -473,7 +473,17 @@ int exp_filter_find(struct exp_filter *f, const struct nf_expect *exp) return 1; list_for_each_entry(item, &f->list, head) { - const char *name = nfexp_get_attr(exp, ATTR_EXP_HELPER_NAME); + const char *name; + + if (nfexp_attr_is_set(exp, ATTR_EXP_HELPER_NAME)) + name = nfexp_get_attr(exp, ATTR_EXP_HELPER_NAME); + else { + /* No helper name, this is likely to be a kernel older + * which does not include the helper name, just skip + * this so we don't crash. + */ + return 0; + } /* we allow partial matching to support things like sip-PORT. */ if (strncasecmp(item->helper_name, name, -- cgit v1.2.3 From affe4656f3aeeba4040f9d63efd7719ef0345ae9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 8 Oct 2012 12:22:28 +0200 Subject: doc: detail user-space helper support This patch adds documentation on how to enable user-space helper support. Signed-off-by: Pablo Neira Ayuso --- doc/manual/conntrack-tools.tmpl | 155 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 150 insertions(+), 5 deletions(-) diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index 63a53e4..f21a4ff 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -19,7 +19,7 @@ - 2008-2011 + 2008-2012 Pablo Neira Ayuso @@ -37,7 +37,7 @@ This document details how to install and configure the conntrack-tools - >= 1.0.0. This document will evolve in the future to cover new features + >= 1.4.0. This document will evolve in the future to cover new features and changes. @@ -827,7 +827,154 @@ Sync { -Troubleshooting + + +User-space helpers + + Check your Linux kernel version first + + The user-space helper infrastructure requires a Linux kernel >= 3.6 + to work appropriately. + + + +Connection tracking helpers allows you to filter multi-flow protocols +that usually separate control and data traffic into different flows. +These protocols usually violate network layering by including layer 3/4 +details, eg. IP address and TCP/UDP ports, in their application protocol +(which resides in layer 7). This is problematic for gateways since they +operate at packet-level, ie. layers 3/4, and therefore they miss this +important information to filter these protocols appropriately. + +Helpers inspect packet content (at layer 7) and create the so-called +expectations. These expectations are added to one internal table +that resides in the gateway. For each new packet arriving to the +gateway, the gateway first looks up for matching expectations. If +there is any, then this flow is accepted since it's been expected. +Note this lookup only occurs for the first packet that is part of one +newly established flow, not for all packets. + +Since 1.4.0, conntrackd provides the infrastructure to develop +helpers in user-space. The main features of the user-space infrastructure +for helpers are: + + + +Rapid connection tracking helper development, as developing code +in user-space is usually faster. + +Reliability: A buggy helper does not crash the kernel. If the helper +fails, ie. the conntrackd crashes, Moreover, we can monitor the helper process +and restart it in case of problems. + +Security: Avoid complex string matching and mangling in +kernel-space running in privileged mode. Going further, we can even think +about running user-space helper as a non-root process. + +It allows the development of very specific helpers for +proprietary protocols that are not standard. This is the case of the SQL*net +helper. Implementing this in kernel-space may be problematic, since +this may not be accepted for ainline inclusion in the Linux kernel. +As an alternative, we can still distribute this support as separate +patches. However, my personal experience is that, given that the +kernel API/ABI is not stable, changes in the interface lead to the +breakage of the patch. This highly increase the overhead in the +maintainance. + + + +Currently, the infrastructure supports the following user-space helpers: + + + +Oracle*TNS, to support its special Redirect message. +NFSv3, mind that version 4 does not require this helper. +FTP (this helper is also available in kernel-space). + + +The following steps describe how to enable the RPC portmapper helper for NFSv3 (this is similar for other helpers): + + +Register user-space helper: + + +nfct helper add rpc inet udp +nfct helper add rpc inet tcp + + +This registers the portmapper helper for both UDP and TCP (NFSv3 traffic goes both over TCP and UDP). + + +Add iptables rule using the CT target: + + +# iptables -I OUTPUT -t raw -p udp --dport 111 -j CT --helper rpc +# iptables -I OUTPUT -t raw -p tcp --dport 111 -j CT --helper rpc + + +With this, packets matching port TCP/UDP/111 are passed to user-space for +inspection. If there is no instance of conntrackd configured to support +user-space helpers, no inspection happens and packets are not sent to +user-space. + +Add configuration to conntrackd.conf: + + +Helper { + Type rpc inet udp { + QueueNum 1 + QueueLen 10240 + Policy rpc { + ExpectMax 1 + ExpectTimeout 300 + } + } + Type rpc inet tcp { + QueueNum 2 + QueueLen 10240 + Policy rpc { + ExpectMax 1 + ExpectTimeout 300 + } + } +} + + +This configures conntrackd to use NFQUEUE queue numbers 1 and 2 to send traffic +for inspection to user-space + + If you have some custom libnetfilter_queue application + + Make sure your queue numbers do not collide with those used in your + conntrackd.conf file. + + + + + + + +Now you can test this (assuming you have some working NFSv3 setup) with: + + +mount -t nfs -onfsvers=3 mynfs.server.info:/srv/cvs /mnt/ + + + + +You should see new expectations being added via: + + +# conntrack -E expect + [NEW] 300 proto=17 src=1.2.3.4 dst=1.2.3.4 sport=0 dport=54834 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=1.2.3.4 master-dst=1.2.3.4 sport=58190 dport=111 PERMANENT class=0 helper=rpc + [NEW] 300 proto=6 src=1.2.3.4 dst=1.2.3.4 sport=0 dport=2049 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=1.2.3.4 master-dst=1.2.3.4 sport=55450 dport=111 PERMANENT class=0 helper=rpc + [NEW] 300 proto=17 src=1.2.3.4 dst=1.2.3.4 sport=0 dport=58031 mask-src=255.255.255.255 mask-dst=255.255.255.255 sport=0 dport=65535 master-src=1.2.3.4 master-dst=1.2.3.4 sport=56309 dport=111 PERMANENT class=0 helper=rpc + + + + + +Troubleshooting Problems with conntrackd? The following list of questions should help for troubleshooting: @@ -1033,8 +1180,6 @@ not enough space errors: 0 - - -- cgit v1.2.3 From e61ac9a2e58cdcf6dc9a12d32b1f221e078e5d05 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 8 Oct 2012 12:33:09 +0200 Subject: bump version to 1.4.0 and update dependencies Signed-off-by: Pablo Neira Ayuso --- configure.ac | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 27ad01b..cbe581c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(conntrack-tools, 1.2.2, pablo@netfilter.org) +AC_INIT(conntrack-tools, 1.4.0, pablo@netfilter.org) AC_CONFIG_AUX_DIR([build-aux]) AC_CANONICAL_HOST @@ -54,12 +54,12 @@ else flex.]) fi -PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.0]) +PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.1]) PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3]) -PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.1]) +PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.2]) PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) PKG_CHECK_MODULES([LIBNETFILTER_CTHELPER], [libnetfilter_cthelper >= 1.0.0]) -PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.0]) +PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.2]) AC_CHECK_HEADERS([linux/capability.h],, [AC_MSG_ERROR([Cannot find linux/capabibility.h])]) -- cgit v1.2.3 From 102293accbc6ac3a21d68ab98e058263b316a407 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 6 Nov 2012 16:51:11 +0100 Subject: conntrackd: fix deadlock due to wrong nested signal blocking The existing code may nest several signal blocking and unblocking calls in different paths of the code. This may result in deadlocks while receiving signals. This patch simplifies the signal blocking approach. Now signals are blocked in three paths: 1) Internal timers handling, while running timer callback for expired timers. 2) File descriptor handling, while running file descriptor callbacks. 3) While handling signals, to avoid that SIGINT and SIGTERM in a row results in a deadlock. Thanks a lot to Ulrich Weber for discussing a fix for this problem. Signed-off-by: Pablo Neira Ayuso --- src/process.c | 12 +----------- src/run.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/process.c b/src/process.c index 9b9734c..7f0a395 100644 --- a/src/process.c +++ b/src/process.c @@ -27,25 +27,19 @@ int fork_process_new(int type, int flags, void (*cb)(void *data), void *data) struct child_process *c, *this; int pid; - /* block SIGCHLD to avoid the access of the list concurrently */ - sigprocmask(SIG_BLOCK, &STATE(block), NULL); - /* We only want one process of this type at the same time. This is * useful if you want to prevent two child processes from accessing * a shared descriptor at the same time. */ if (flags & CTD_PROC_F_EXCL) { list_for_each_entry(this, &process_list, head) { if (this->type == type) { - sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); return -1; } } } c = calloc(sizeof(struct child_process), 1); - if (c == NULL) { - sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); + if (c == NULL) return -1; - } c->type = type; c->cb = cb; @@ -55,8 +49,6 @@ int fork_process_new(int type, int flags, void (*cb)(void *data), void *data) if (c->pid > 0) list_add(&c->head, &process_list); - sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); - return pid; } @@ -89,7 +81,6 @@ void fork_process_dump(int fd) char buf[4096]; int size = 0; - sigprocmask(SIG_BLOCK, &STATE(block), NULL); list_for_each_entry(this, &process_list, head) { size += snprintf(buf+size, sizeof(buf), "PID=%u type=%s\n", @@ -97,7 +88,6 @@ void fork_process_dump(int fd) this->type < CTD_PROC_MAX ? process_type_to_name[this->type] : "unknown"); } - sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); send(fd, buf, size, 0); } diff --git a/src/run.c b/src/run.c index 3337694..44a179f 100644 --- a/src/run.c +++ b/src/run.c @@ -40,10 +40,15 @@ #include #include -void killer(int foo) +void killer(int signal) { - /* no signals while handling signals */ - sigprocmask(SIG_BLOCK, &STATE(block), NULL); + /* Signals are re-entrant, disable signal handling to avoid problems + * in case we receive SIGINT and SIGTERM in a row. This function is + * also called via -k from the unix socket context, we already disabled + * signals in that path, so don't do it. + */ + if (signal) + sigprocmask(SIG_BLOCK, &STATE(block), NULL); local_server_destroy(&STATE(local)); @@ -58,8 +63,6 @@ void killer(int foo) dlog(LOG_NOTICE, "---- shutdown received ----"); close_log(); - sigprocmask(SIG_UNBLOCK, &STATE(block), NULL); - exit(0); } -- cgit v1.2.3 From 134bffa852edc341664dcc4808b1de41107d16d6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 27 Nov 2012 20:57:42 +0100 Subject: conntrack: add support to dump the dying and unconfirmed list via ctnetlink This patch adds support for: conntrack -L dying conntrack -L unconfirmed To display the list of dying and unconfirmed conntracks. This provides some instrumentation in case that `conntrack -C` really deviates from what `conntrack -L | wc -l` says. Users like to check this to make sure things are going OK. Still, some conntrack objects may be still in the dying and the unconfirmed list. With this patch, we can also dump their content, before it was not possible. In normal cases both lists would be simply empty, or in the case of the dying list, you can observe that entries go slightly down in number. Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 95 insertions(+), 13 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index 7451a80..227c355 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -822,27 +822,45 @@ add_command(unsigned int *cmd, const int newcmd) *cmd |= newcmd; } -static unsigned int -check_type(int argc, char *argv[]) +static char *get_table(int argc, char *argv[]) { char *table = NULL; - /* Nasty bug or feature in getopt_long ? + /* 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; - else if (optind < argc && argv[optind][0] != '-' - && argv[optind][0] != '!') + else if (optind < argc && argv[optind][0] != '-' && + argv[optind][0] != '!') table = argv[optind++]; - - if (!table) - return 0; - + + return table; +} + +enum { + CT_TABLE_CONNTRACK, + CT_TABLE_EXPECT, + CT_TABLE_DYING, + CT_TABLE_UNCONFIRMED, +}; + +static unsigned int check_type(int argc, char *argv[]) +{ + const char *table = get_table(argc, argv); + + /* default to conntrack subsystem if nothing has been specified. */ + if (table == NULL) + return CT_TABLE_CONNTRACK; + if (strncmp("expect", table, strlen(table)) == 0) - return 1; + return CT_TABLE_EXPECT; else if (strncmp("conntrack", table, strlen(table)) == 0) - return 0; + return CT_TABLE_CONNTRACK; + else if (strncmp("dying", table, strlen(table)) == 0) + return CT_TABLE_DYING; + else if (strncmp("unconfirmed", table, strlen(table)) == 0) + return CT_TABLE_UNCONFIRMED; else exit_error(PARAMETER_PROBLEM, "unknown type `%s'", table); @@ -1686,6 +1704,27 @@ static int nfct_global_stats_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } +static int mnl_nfct_dump_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nf_conntrack *ct; + char buf[4096]; + + ct = nfct_new(); + if (ct == NULL) + return MNL_CB_OK; + + nfct_nlmsg_parse(nlh, ct); + + nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, 0); + printf("%s\n", buf); + + nfct_destroy(ct); + + counter++; + + return MNL_CB_OK; +} + static struct ctproto_handler *h; int main(int argc, char *argv[]) @@ -1720,6 +1759,16 @@ int main(int argc, char *argv[]) switch(c) { /* commands */ case 'L': + type = check_type(argc, argv); + /* Special case: dumping dying and unconfirmed list + * are handled like normal conntrack dumps. + */ + if (type == CT_TABLE_DYING || + type == CT_TABLE_UNCONFIRMED) + add_command(&command, cmd2type[c][0]); + else + add_command(&command, cmd2type[c][type]); + break; case 'I': case 'D': case 'G': @@ -1730,14 +1779,25 @@ int main(int argc, char *argv[]) case 'C': case 'S': type = check_type(argc, argv); + if (type == CT_TABLE_DYING || + type == CT_TABLE_UNCONFIRMED) { + exit_error(PARAMETER_PROBLEM, + "Can't do that command with " + "tables `dying' and `unconfirmed'"); + } add_command(&command, cmd2type[c][type]); break; case 'U': type = check_type(argc, argv); - if (type == 0) + if (type == CT_TABLE_DYING || + type == CT_TABLE_UNCONFIRMED) { + exit_error(PARAMETER_PROBLEM, + "Can't do that command with " + "tables `dying' and `unconfirmed'"); + } else if (type == CT_TABLE_CONNTRACK) add_command(&command, CT_UPDATE); else - exit_error(PARAMETER_PROBLEM, + exit_error(PARAMETER_PROBLEM, "Can't update expectations"); break; /* options */ @@ -1937,6 +1997,28 @@ int main(int argc, char *argv[]) struct nfct_filter_dump *filter_dump; case CT_LIST: + if (type == CT_TABLE_DYING) { + if (nfct_mnl_socket_open() < 0) + exit_error(OTHER_PROBLEM, "Can't open handler"); + + res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_DYING, + mnl_nfct_dump_cb); + + nfct_mnl_socket_close(); + break; + } else if (type == CT_TABLE_UNCONFIRMED) { + if (nfct_mnl_socket_open() < 0) + exit_error(OTHER_PROBLEM, "Can't open handler"); + + res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_UNCONFIRMED, + mnl_nfct_dump_cb); + + nfct_mnl_socket_close(); + break; + } + cth = nfct_open(CONNTRACK, 0); if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); -- cgit v1.2.3 From a96fdeaac8274c0544b0ffa808782932e637a6f5 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 3 Mar 2013 21:49:58 +0100 Subject: build: bump version to 1.4.1 Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index cbe581c..44c49e2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(conntrack-tools, 1.4.0, pablo@netfilter.org) +AC_INIT(conntrack-tools, 1.4.1, pablo@netfilter.org) AC_CONFIG_AUX_DIR([build-aux]) AC_CANONICAL_HOST -- cgit v1.2.3 From 0237147701853ba4bb1f626a1315c5cc4fd5f378 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 4 Mar 2013 16:42:48 +0100 Subject: build: requires libnetfilter_conntrack >= 1.0.3 Reported-by: Gustavo Zacarias Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 44c49e2..c544773 100644 --- a/configure.ac +++ b/configure.ac @@ -56,7 +56,7 @@ fi PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.1]) PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3]) -PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.2]) +PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.3]) PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) PKG_CHECK_MODULES([LIBNETFILTER_CTHELPER], [libnetfilter_cthelper >= 1.0.0]) PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.2]) -- cgit v1.2.3 From 02fd8b1fef9caf1da74e4e0d5ef3c16b2e4d37d7 Mon Sep 17 00:00:00 2001 From: James Guthrie Date: Wed, 20 Mar 2013 15:41:56 +0100 Subject: conntrackd: fix parsing of non-abbreviated IPv6 address in config file Both representations of this example IPv6 address should be accepted: fe80::1 fe80:0:0:0:0:0:0:1 This patch fixes the lexical parser for non-abbreviated version, which was not working. Signed-off-by: James Guthrie Signed-off-by: Roman Hoog Antink Signed-off-by: Pablo Neira Ayuso --- src/read_config_lex.l | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/read_config_lex.l b/src/read_config_lex.l index bec2d81..b4d11d4 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -44,8 +44,8 @@ ip4 {ip4_part}\.{ip4_part}\.{ip4_part}\.{ip4_part}{ip4_cidr}? hex_255 [0-9a-fA-F]{1,4} ip6_cidr \/[0-1]*[0-9]*[0-9]+ ip6_part {hex_255}":"? -ip6_form1 {ip6_part}{0,16}"::"{ip6_part}{0,16} -ip6_form2 ({hex_255}":"){16}{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\.\-]* persistent [P|p][E|e][R|r][S|s][I|i][S|s][T|t][E|e][N|n][T|T] -- cgit v1.2.3 From ca99976f450afa0f7ec2f4320e863860990af61d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 15 May 2013 16:38:30 +0200 Subject: conntrack: fix timestamps when microseconds are less than 100000 The fractional portion of timestamps reported by conntrack is printed as a left-justified integer instead of fixed-width and zero-padded. Closes netfilter's bugzilla 817: https://bugzilla.netfilter.org/show_bug.cgi?id=817 Reported-by: hoffman@stanford.edu Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conntrack.c b/src/conntrack.c index 227c355..d4e79de 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1141,7 +1141,7 @@ static int event_cb(enum nf_conntrack_msg_type type, if (!(output_mask & _O_XML)) { struct timeval tv; gettimeofday(&tv, NULL); - printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec); + printf("[%-.8ld.%-.6ld]\t", tv.tv_sec, tv.tv_usec); } else op_flags |= NFCT_OF_TIME; } -- cgit v1.2.3 From 6869ca4032411ac79e6f2cda3cbd401c444364ef Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 2 Jun 2013 23:18:24 +0000 Subject: conntrackd: fix compiler warnings main.c:359:6: warning: ignoring return value of 'nice' [..] main.c:395:7: warning: ignoring return value of 'chdir' [..] run.c:43:17: warning: declaration of 'signal' shadows a global declaration Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- src/main.c | 22 ++++++++++++++++++++-- src/run.c | 4 ++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index 831a3c2..dafeaee 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -112,6 +113,23 @@ set_action_by_table(int i, int argc, char *argv[], return i; } +static void +set_nice_value(int nv) +{ + errno = 0; + if (nice(nv) == -1 && errno) /* warn only */ + fprintf(stderr, "Cannot set nice level %d: %s\n", + nv, strerror(errno)); +} + +static void +do_chdir(const char *d) +{ + if (chdir(d)) + fprintf(stderr, "Cannot change current directory to %s: %s\n", + d, strerror(errno)); +} + int main(int argc, char *argv[]) { int ret, i, action = -1; @@ -356,7 +374,7 @@ int main(int argc, char *argv[]) /* * Setting process priority and scheduler */ - nice(CONFIG(nice)); + set_nice_value(CONFIG(nice)); if (CONFIG(sched).type != SCHED_OTHER) { struct sched_param schedparam = { @@ -382,7 +400,7 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - chdir("/"); + do_chdir("/"); close(STDIN_FILENO); /* Daemonize conntrackd */ diff --git a/src/run.c b/src/run.c index 44a179f..7fa6889 100644 --- a/src/run.c +++ b/src/run.c @@ -40,14 +40,14 @@ #include #include -void killer(int signal) +void killer(int signo) { /* Signals are re-entrant, disable signal handling to avoid problems * in case we receive SIGINT and SIGTERM in a row. This function is * also called via -k from the unix socket context, we already disabled * signals in that path, so don't do it. */ - if (signal) + if (signo) sigprocmask(SIG_BLOCK, &STATE(block), NULL); local_server_destroy(&STATE(local)); -- cgit v1.2.3 From c9a31025a96177735c3259937da342a4f12156ae Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 2 Jun 2013 23:18:25 +0000 Subject: include: kill unused PLD_* macros Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/network.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/include/network.h b/include/network.h index 41c35af..79745f3 100644 --- a/include/network.h +++ b/include/network.h @@ -173,18 +173,6 @@ static inline int between(uint32_t seq1, uint32_t seq2, uint32_t seq3) return seq3 - seq2 >= seq1 - seq2; } -#define PLD_NETWORK2HOST(x) \ -({ \ - x->len = ntohs(x->len); \ - x->query = ntohs(x->query); \ -}) - -#define PLD_HOST2NETWORK(x) \ -({ \ - x->len = htons(x->len); \ - x->query = htons(x->query); \ -}) - struct netattr { uint16_t nta_len; uint16_t nta_attr; -- cgit v1.2.3 From ad9c4a919976a49246d74f751afe5da567328b54 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 7 Jun 2013 19:44:24 +0200 Subject: tests: cthelper: remove test infrastructure from this tree I decided to move it to: http://git.netfilter.org/conntrackd-helper-tests to reduce the bloat of this tree, most people are not interested in this stuff when they grab it via git clone. Signed-off-by: Pablo Neira Ayuso --- tests/conntrackd/cthelper/.gitignore | 14 -- tests/conntrackd/cthelper/Make_global.am | 7 - tests/conntrackd/cthelper/Makefile.am | 20 -- tests/conntrackd/cthelper/README | 2 - tests/conntrackd/cthelper/configure.ac | 64 ------ tests/conntrackd/cthelper/ct.c | 91 --------- tests/conntrackd/cthelper/ct.h | 22 --- tests/conntrackd/cthelper/expect.c | 200 ------------------- tests/conntrackd/cthelper/l3_ipv4.c | 86 -------- tests/conntrackd/cthelper/l4_tcp.c | 88 --------- tests/conntrackd/cthelper/l4_udp.c | 88 --------- tests/conntrackd/cthelper/main.c | 220 --------------------- tests/conntrackd/cthelper/pcaps/nfsv3.pcap | Bin 6824 -> 0 bytes .../cthelper/pcaps/oracle-tns-redirect.pcap | Bin 1095 -> 0 bytes tests/conntrackd/cthelper/proto.c | 49 ----- tests/conntrackd/cthelper/proto.h | 50 ----- tests/conntrackd/cthelper/run-test.sh | 11 -- tests/conntrackd/cthelper/test.h | 13 -- 18 files changed, 1025 deletions(-) delete mode 100644 tests/conntrackd/cthelper/.gitignore delete mode 100644 tests/conntrackd/cthelper/Make_global.am delete mode 100644 tests/conntrackd/cthelper/Makefile.am delete mode 100644 tests/conntrackd/cthelper/README delete mode 100644 tests/conntrackd/cthelper/configure.ac delete mode 100755 tests/conntrackd/cthelper/ct.c delete mode 100755 tests/conntrackd/cthelper/ct.h delete mode 100644 tests/conntrackd/cthelper/expect.c delete mode 100755 tests/conntrackd/cthelper/l3_ipv4.c delete mode 100755 tests/conntrackd/cthelper/l4_tcp.c delete mode 100755 tests/conntrackd/cthelper/l4_udp.c delete mode 100755 tests/conntrackd/cthelper/main.c delete mode 100644 tests/conntrackd/cthelper/pcaps/nfsv3.pcap delete mode 100644 tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap delete mode 100755 tests/conntrackd/cthelper/proto.c delete mode 100755 tests/conntrackd/cthelper/proto.h delete mode 100644 tests/conntrackd/cthelper/run-test.sh delete mode 100644 tests/conntrackd/cthelper/test.h diff --git a/tests/conntrackd/cthelper/.gitignore b/tests/conntrackd/cthelper/.gitignore deleted file mode 100644 index 928e44b..0000000 --- a/tests/conntrackd/cthelper/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -.deps/ -.libs/ -Makefile -Makefile.in -*.o -*.la -*.lo - -/aclocal.m4 -/autom4te.cache/ -/build-aux/ -/config.* -/configure -/libtool diff --git a/tests/conntrackd/cthelper/Make_global.am b/tests/conntrackd/cthelper/Make_global.am deleted file mode 100644 index 06785a1..0000000 --- a/tests/conntrackd/cthelper/Make_global.am +++ /dev/null @@ -1,7 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include -I../../../include - -AM_CFLAGS = -std=gnu99 -W -Wall \ - -Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \ - -Wno-unused-parameter \ - ${LIBNETFILTER_CONNTRACK_CFLAGS} \ - ${LIBNETFILTER_CTTIMEOUT_CFLAGS} diff --git a/tests/conntrackd/cthelper/Makefile.am b/tests/conntrackd/cthelper/Makefile.am deleted file mode 100644 index b8f0d42..0000000 --- a/tests/conntrackd/cthelper/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -include $(top_srcdir)/Make_global.am - -check_PROGRAMS = cthelper-test - -cthelper_test_SOURCES = proto.c \ - ct.c \ - l3_ipv4.c \ - l4_tcp.c \ - l4_udp.c \ - expect.c \ - ../../../src/helpers.c \ - main.c - -cthelper_test_LDFLAGS = -dynamic \ - -lpcap \ - -ldl \ - -lmnl \ - -lnetfilter_queue \ - -lnetfilter_conntrack \ - -export-dynamic diff --git a/tests/conntrackd/cthelper/README b/tests/conntrackd/cthelper/README deleted file mode 100644 index 6e8b385..0000000 --- a/tests/conntrackd/cthelper/README +++ /dev/null @@ -1,2 +0,0 @@ -This directory contains PCAP files with traffic traces that we can use to test -the user-space helpers. diff --git a/tests/conntrackd/cthelper/configure.ac b/tests/conntrackd/cthelper/configure.ac deleted file mode 100644 index 8b3da5c..0000000 --- a/tests/conntrackd/cthelper/configure.ac +++ /dev/null @@ -1,64 +0,0 @@ -AC_INIT(cthelper-test, 0.0.1, pablo@netfilter.org) -AC_CONFIG_AUX_DIR([build-aux]) - -AC_CANONICAL_HOST -AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE([-Wall foreign subdir-objects - tar-pax no-dist-gzip dist-bzip2 1.6]) - -dnl kernel style compile messages -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) - -AC_SEARCH_LIBS([dlopen], [dl], [libdl_LIBS="$LIBS"; LIBS=""]) -AC_SUBST([libdl_LIBS]) - -AC_PROG_CC -AC_DISABLE_STATIC -AM_PROG_LIBTOOL -AC_PROG_INSTALL -AC_PROG_LN_S -AM_PROG_LEX -AC_PROG_YACC - -case "$host" in -*-*-linux*) ;; -*) AC_MSG_ERROR([Linux only, dude!]);; -esac - -PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.0]) -PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.0]) - -AC_CHECK_HEADERS(arpa/inet.h) -dnl check for inet_pton -AC_CHECK_FUNCS(inet_pton) -dnl Some systems have it, but not IPv6 -if test "$ac_cv_func_inet_pton" = "yes" ; then -AC_MSG_CHECKING(if inet_pton supports IPv6) -AC_RUN_IFELSE([AC_LANG_SOURCE([[ -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_SOCKET_H -#include -#endif -#ifdef HAVE_NETINET_IN_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif -int main() - { - struct in6_addr addr6; - if (inet_pton(AF_INET6, "::1", &addr6) < 1) - exit(1); - else - exit(0); - } - ]])],[ AC_MSG_RESULT(yes) - AC_DEFINE_UNQUOTED(HAVE_INET_PTON_IPV6, 1, [Define to 1 if inet_pton supports IPv6.]) - ],[AC_MSG_RESULT(no)],[AC_MSG_RESULT(no)]) -fi - -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff --git a/tests/conntrackd/cthelper/ct.c b/tests/conntrackd/cthelper/ct.c deleted file mode 100755 index 1c17336..0000000 --- a/tests/conntrackd/cthelper/ct.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include - -#include - -#include - -#include "proto.h" -#include "helper.h" -#include "myct.h" -#include "ct.h" - -static LIST_HEAD(ct_list); - -struct nf_ct_entry * -ct_alloc(const uint8_t *pkt, unsigned int l3hdr_len, - struct cthelper_proto_l2l3_helper *l3h, - struct cthelper_proto_l4_helper *l4h) -{ - struct nf_ct_entry *ct; - - ct = calloc(1, sizeof(struct nf_ct_entry)); - if (ct == NULL) - return NULL; - - ct->myct = calloc(1, sizeof(struct myct)); - if (ct->myct == NULL) { - free(ct); - return NULL; - } - ct->myct->ct = nfct_new(); - if (ct->myct->ct == NULL) { - free(ct->myct); - free(ct); - return NULL; - } - /* FIXME: use good private helper size */ - ct->myct->priv_data = calloc(1, 128); - if (ct->myct->priv_data == NULL) { - nfct_destroy(ct->myct->ct); - free(ct->myct); - free(ct); - return NULL; - } - - l3h->l3ct_build(pkt, ct->myct->ct); - l4h->l4ct_build(pkt + l3hdr_len, ct->myct->ct); - - return ct; -} - -struct nf_ct_entry * -ct_find(const uint8_t *pkt, unsigned int l3hdr_len, - struct cthelper_proto_l2l3_helper *l3h, - struct cthelper_proto_l4_helper *l4h, unsigned int *ctinfo) -{ - struct nf_ct_entry *cur; - - list_for_each_entry(cur, &ct_list, head) { - if (l3h->l3ct_cmp_orig(pkt, cur->myct->ct) && - l4h->l4ct_cmp_orig(pkt + l3hdr_len, cur->myct->ct)) { - *ctinfo = 0; - return cur; - } - if (l3h->l3ct_cmp_repl(pkt, cur->myct->ct) && - l4h->l4ct_cmp_repl(pkt + l3hdr_len, cur->myct->ct)) { - *ctinfo = IP_CT_IS_REPLY; - return cur; - } - } - return NULL; -} - -void ct_add(struct nf_ct_entry *ct) -{ - list_add(&ct->head, &ct_list); -} - -void ct_flush(void) -{ - struct nf_ct_entry *cur, *tmp; - - list_for_each_entry_safe(cur, tmp, &ct_list, head) { - list_del(&cur->head); - free(cur->myct->priv_data); - free(cur->myct->ct); - free(cur->myct); - free(cur); - } -} diff --git a/tests/conntrackd/cthelper/ct.h b/tests/conntrackd/cthelper/ct.h deleted file mode 100755 index f01d49d..0000000 --- a/tests/conntrackd/cthelper/ct.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _CT_H_ -#define _CT_H_ - -#include "../../../include/linux_list.h" -#include "../../../include/myct.h" - -struct nf_ct_entry { - struct list_head head; - struct myct *myct; -}; - -struct cthelper_proto_l2l3_helper; -struct cthelper_proto_l4_helper; - -struct nf_ct_entry *ct_alloc(const uint8_t *pkt, unsigned int l3hdr_len, struct cthelper_proto_l2l3_helper *l3h, struct cthelper_proto_l4_helper *l4h); - -struct nf_ct_entry *ct_find(const uint8_t *pkt, unsigned int l3hdr_len, struct cthelper_proto_l2l3_helper *l3h, struct cthelper_proto_l4_helper *l4h, unsigned int *ctinfo); - -void ct_add(struct nf_ct_entry *ct); -void ct_flush(void); - -#endif diff --git a/tests/conntrackd/cthelper/expect.c b/tests/conntrackd/cthelper/expect.c deleted file mode 100644 index d1d1d28..0000000 --- a/tests/conntrackd/cthelper/expect.c +++ /dev/null @@ -1,200 +0,0 @@ -/* - * (C) 2012 by Pablo Neira Ayuso - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation (or any later at your option). - * - * This code has been sponsored by Vyatta Inc. - */ - -#include "../../../include/helper.h" -#include "test.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -int -cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, - uint32_t class, - union nfct_attr_grp_addr *saddr, - union nfct_attr_grp_addr *daddr, - uint8_t l4proto, uint16_t *sport, uint16_t *dport, - uint32_t flags) -{ - struct nf_conntrack *expected, *mask; - - expected = nfct_new(); - if (!expected) - return -1; - - mask = nfct_new(); - if (!mask) - return -1; - - if (saddr) { - switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { - int i; - uint32_t addr[4] = {}; - - case AF_INET: - nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); - nfct_set_attr_u32(expected, ATTR_IPV4_SRC, saddr->ip); - - nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET); - nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0xffffffff); - break; - 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, 0xffffffff, sizeof(uint32_t)); - - nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); - nfct_set_attr(mask, ATTR_IPV6_SRC, addr); - break; - default: - break; - } - } else { - switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { - int i; - uint32_t addr[4] = {}; - - case AF_INET: - nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); - nfct_set_attr_u32(expected, ATTR_IPV4_SRC, 0x00000000); - - nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET); - nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0x00000000); - break; - case AF_INET6: - for (i=0; i<4; i++) - memset(addr, 0x00000000, sizeof(uint32_t)); - - nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); - nfct_set_attr(expected, ATTR_IPV6_SRC, addr); - - nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); - nfct_set_attr(mask, ATTR_IPV6_SRC, addr); - break; - default: - break; - } - } - - if (sport) { - switch(l4proto) { - case IPPROTO_TCP: - case IPPROTO_UDP: - nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); - nfct_set_attr_u16(expected, ATTR_PORT_SRC, *sport); - nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); - nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0xffff); - break; - default: - break; - } - } else { - switch(l4proto) { - case IPPROTO_TCP: - case IPPROTO_UDP: - nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); - nfct_set_attr_u16(expected, ATTR_PORT_SRC, 0x0000); - nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); - nfct_set_attr_u16(mask, ATTR_PORT_SRC, 0x0000); - break; - default: - break; - } - } - - switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { - uint32_t addr[4] = {}; - int i; - - case AF_INET: - nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); - nfct_set_attr_u32(expected, ATTR_IPV4_DST, daddr->ip); - nfct_set_attr_u32(mask, ATTR_IPV4_DST, 0xffffffff); - break; - 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)); - - nfct_set_attr(mask, ATTR_IPV6_DST, addr); - break; - default: - break; - } - - switch(l4proto) { - case IPPROTO_TCP: - case IPPROTO_UDP: - nfct_set_attr_u8(expected, ATTR_L4PROTO, l4proto); - nfct_set_attr_u16(expected, ATTR_PORT_DST, *dport); - nfct_set_attr_u8(mask, ATTR_L4PROTO, l4proto); - nfct_set_attr_u16(mask, ATTR_PORT_DST, 0xffff); - break; - default: - break; - } - - nfexp_set_attr(exp, ATTR_EXP_MASTER, master); - nfexp_set_attr(exp, ATTR_EXP_EXPECTED, expected); - nfexp_set_attr(exp, ATTR_EXP_MASK, mask); - - nfct_destroy(expected); - nfct_destroy(mask); - - return 0; -} - -int cthelper_add_expect(struct nf_expect *exp) -{ - cthelper_test_stats.ct_expect_created++; - return 0; -} - -int cthelper_del_expect(struct nf_expect *exp) -{ - return 0; -} - -void -cthelper_get_addr_src(struct nf_conntrack *ct, int dir, - union nfct_attr_grp_addr *addr) -{ - switch (dir) { - case MYCT_DIR_ORIG: - nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_SRC, addr); - break; - case MYCT_DIR_REPL: - nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_SRC, addr); - break; - } -} - -void -cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, - union nfct_attr_grp_addr *addr) -{ - switch (dir) { - case MYCT_DIR_ORIG: - nfct_get_attr_grp(ct, ATTR_GRP_ORIG_ADDR_DST, addr); - break; - case MYCT_DIR_REPL: - nfct_get_attr_grp(ct, ATTR_GRP_REPL_ADDR_DST, addr); - break; - } -} diff --git a/tests/conntrackd/cthelper/l3_ipv4.c b/tests/conntrackd/cthelper/l3_ipv4.c deleted file mode 100755 index 8edfd2e..0000000 --- a/tests/conntrackd/cthelper/l3_ipv4.c +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include -#include - -#include "proto.h" - -#include - -#define PRINT_CMP(...) - -static void -l3_ipv4_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct iphdr *iph = (const struct iphdr *)pkt; - - nfct_set_attr_u16(ct, ATTR_ORIG_L3PROTO, AF_INET); - nfct_set_attr_u16(ct, ATTR_REPL_L3PROTO, AF_INET); - nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_SRC, iph->saddr); - nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_DST, iph->daddr); - nfct_set_attr_u32(ct, ATTR_REPL_IPV4_SRC, iph->daddr); - nfct_set_attr_u32(ct, ATTR_REPL_IPV4_DST, iph->saddr); -} - -static int -l3_ipv4_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct iphdr *iph = (const struct iphdr *)pkt; - - PRINT_CMP("cmp_orig iph->saddr: %x == %x\n", - iph->saddr, nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC)); - PRINT_CMP("cmp_orig iph->daddr: %x == %x\n", - iph->daddr, nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST)); - - if (iph->saddr == nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_SRC) && - iph->daddr == nfct_get_attr_u32(ct, ATTR_ORIG_IPV4_DST)) - return 1; - - return 0; -} - -static int -l3_ipv4_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct iphdr *iph = (const struct iphdr *)pkt; - - PRINT_CMP("cmp_repl iph->saddr: %x == %x\n", - iph->saddr, nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC)); - PRINT_CMP("cmp_repl iph->daddr: %x == %x\n", - iph->daddr, nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST)); - - if (iph->saddr == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC) && - iph->daddr == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST)) - return 1; - - return 0; -} - -static int l3_ipv4_pkt_l4proto_num(const uint8_t *pkt) -{ - const struct iphdr *iph = (const struct iphdr *)pkt; - - return iph->protocol; -} - -static int l3_ipv4_pkt_l3hdr_len(const uint8_t *pkt) -{ - const struct iphdr *iph = (const struct iphdr *)pkt; - - return iph->ihl << 2; -} - -static struct cthelper_proto_l2l3_helper ipv4 = { - .l2protonum = ETH_P_IP, - .l3protonum = AF_INET, - .l2hdr_len = ETH_HLEN, - .l3ct_build = l3_ipv4_ct_build_tuple, - .l3ct_cmp_orig = l3_ipv4_ct_cmp_tuple_orig, - .l3ct_cmp_repl = l3_ipv4_ct_cmp_tuple_repl, - .l3pkt_hdr_len = l3_ipv4_pkt_l3hdr_len, - .l4pkt_proto = l3_ipv4_pkt_l4proto_num, -}; - -void l2l3_ipv4_init(void) -{ - cthelper_proto_l2l3_helper_register(&ipv4); -} diff --git a/tests/conntrackd/cthelper/l4_tcp.c b/tests/conntrackd/cthelper/l4_tcp.c deleted file mode 100755 index f27c85d..0000000 --- a/tests/conntrackd/cthelper/l4_tcp.c +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include - -#include "proto.h" - -#include - -#define PRINT_CMP(...) - -static void l4_tcp_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct tcphdr *tcph = (const struct tcphdr *)pkt; - - nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, IPPROTO_TCP); - nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_TCP); - nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, tcph->source); - nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, tcph->dest); - nfct_set_attr_u16(ct, ATTR_REPL_PORT_SRC, tcph->dest); - nfct_set_attr_u16(ct, ATTR_REPL_PORT_DST, tcph->source); -} - -static int l4_tcp_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct tcphdr *tcph = (const struct tcphdr *)pkt; - - PRINT_CMP("cmp_orig tcph->source: %u == %u\n", - tcph->source, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); - PRINT_CMP("cmp_orig tcph->dest: %u == %u\n", - tcph->dest, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); - - if (tcph->source == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) && - tcph->dest == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)) - return 1; - - return 0; -} - -static int -l4_tcp_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct tcphdr *tcph = (const struct tcphdr *)pkt; - - PRINT_CMP("cmp_repl tcph->source: %u == %u\n", - tcph->source, nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC)); - PRINT_CMP("cmp_repl tcph->dest: %u == %u\n", - tcph->dest, nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)); - - if (tcph->source == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC) && - tcph->dest == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)) - return 1; - - return 0; -} - -static int -l4_tcp_ct_cmp_port(struct nf_conntrack *ct, uint16_t port) -{ - PRINT_CMP("cmp_port src: %u == %u\n", - port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); - PRINT_CMP("cmp_port dst: %u == %u\n", - port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); - - if (port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) || - port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)) - return 1; - - return 0; -} - -static int l4_tcp_pkt_no_data(const uint8_t *pkt) -{ - const struct tcphdr *tcph = (const struct tcphdr *)pkt; - return tcph->syn || tcph->fin || tcph->rst || !tcph->psh; -} - -static struct cthelper_proto_l4_helper tcp = { - .l4protonum = IPPROTO_TCP, - .l4ct_build = l4_tcp_ct_build_tuple, - .l4ct_cmp_orig = l4_tcp_ct_cmp_tuple_orig, - .l4ct_cmp_repl = l4_tcp_ct_cmp_tuple_repl, - .l4ct_cmp_port = l4_tcp_ct_cmp_port, - .l4pkt_no_data = l4_tcp_pkt_no_data, -}; - -void l4_tcp_init(void) -{ - cthelper_proto_l4_helper_register(&tcp); -} diff --git a/tests/conntrackd/cthelper/l4_udp.c b/tests/conntrackd/cthelper/l4_udp.c deleted file mode 100755 index 4d52d0a..0000000 --- a/tests/conntrackd/cthelper/l4_udp.c +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include - -#include "proto.h" - -#include - -#define PRINT_CMP(...) - -static void l4_udp_ct_build_tuple(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct udphdr *udph = (const struct udphdr *)pkt; - - nfct_set_attr_u8(ct, ATTR_ORIG_L4PROTO, IPPROTO_UDP); - nfct_set_attr_u8(ct, ATTR_REPL_L4PROTO, IPPROTO_UDP); - nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, udph->source); - nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, udph->dest); - nfct_set_attr_u16(ct, ATTR_REPL_PORT_SRC, udph->dest); - nfct_set_attr_u16(ct, ATTR_REPL_PORT_DST, udph->source); -} - -static int l4_udp_ct_cmp_tuple_orig(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct udphdr *udph = (const struct udphdr *)pkt; - - PRINT_CMP("cmp_orig udph->source: %u == %u\n", - udph->source, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); - PRINT_CMP("cmp_orig udph->dest: %u == %u\n", - udph->dest, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); - - if (udph->source == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) && - udph->dest == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)) - return 1; - - return 0; -} - -static int -l4_udp_ct_cmp_tuple_repl(const uint8_t *pkt, struct nf_conntrack *ct) -{ - const struct udphdr *udph = (const struct udphdr *)pkt; - - PRINT_CMP("cmp_repl udph->source: %u == %u\n", - udph->source, nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC)); - PRINT_CMP("cmp_repl udph->dest: %u == %u\n", - udph->dest, nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)); - - if (udph->source == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC) && - udph->dest == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)) - return 1; - - return 0; -} - -static int -l4_udp_ct_cmp_port(struct nf_conntrack *ct, uint16_t port) -{ - PRINT_CMP("cmp_port src: %u == %u\n", - port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC)); - PRINT_CMP("cmp_port dst: %u == %u\n", - port, nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)); - - if (port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) || - port == nfct_get_attr_u16(ct, ATTR_ORIG_PORT_DST)) - return 1; - - return 0; -} - -static int l4_udp_pkt_no_data(const uint8_t *pkt) -{ - /* UDP has no control packets. */ - return 1; -} - -static struct cthelper_proto_l4_helper tcp = { - .l4protonum = IPPROTO_UDP, - .l4ct_build = l4_udp_ct_build_tuple, - .l4ct_cmp_orig = l4_udp_ct_cmp_tuple_orig, - .l4ct_cmp_repl = l4_udp_ct_cmp_tuple_repl, - .l4ct_cmp_port = l4_udp_ct_cmp_port, - .l4pkt_no_data = l4_udp_pkt_no_data, -}; - -void l4_udp_init(void) -{ - cthelper_proto_l4_helper_register(&tcp); -} diff --git a/tests/conntrackd/cthelper/main.c b/tests/conntrackd/cthelper/main.c deleted file mode 100755 index 7021f00..0000000 --- a/tests/conntrackd/cthelper/main.c +++ /dev/null @@ -1,220 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ct.h" -#include "proto.h" -#include "../../../include/helper.h" -#include "test.h" - -#include - -struct cthelper_test_stats cthelper_test_stats; - -enum { - TEST_NORMAL = 0, - TEST_DNAT, -}; - -static int -cthelper_process_packet(const uint8_t *pkt, uint32_t pktlen, - struct ctd_helper *h, int proto, uint16_t port, - int type) -{ - struct pkt_buff *pktb; - struct cthelper_proto_l2l3_helper *l3h; - struct cthelper_proto_l4_helper *l4h; - unsigned int l3hdr_len, l4protonum; - struct nf_ct_entry *ct; - int ret, this_proto; - uint32_t dataoff, ctinfo = 0; - - l3h = cthelper_proto_l2l3_helper_find(pkt, &l4protonum, &l3hdr_len); - if (l3h == NULL) { - fprintf(stderr, "Unsupported layer 3 protocol, skipping.\n"); - return -1; - } - - l4h = cthelper_proto_l4_helper_find(pkt, l4protonum); - if (l4h == NULL) { - fprintf(stderr, "Unsupported layer 4 protocol, skipping.\n"); - return -1; - } - /* get layer 3 header. */ - pkt += l3h->l2hdr_len; - pktlen -= l3h->l2hdr_len; - - /* skip packet with mismatching protocol */ - this_proto = l3h->l4pkt_proto(pkt); - if (this_proto != proto) { - cthelper_test_stats.pkt_mismatch_proto++; - return 0; - } - - /* Look for the fake conntrack. */ - ct = ct_find(pkt, l3hdr_len, l3h, l4h, &ctinfo); - if (ct == NULL) { - /* It doesn't exist any, create one. */ - ct = ct_alloc(pkt, l3hdr_len, l3h, l4h); - if (ct == NULL) { - fprintf(stderr, "Not enough memory\n"); - return -1; - } - ct_add(ct); - ctinfo += IP_CT_NEW; - } else - ctinfo += IP_CT_ESTABLISHED; - - /* skip packets with mismatching ports */ - if (!l4h->l4ct_cmp_port(ct->myct->ct, ntohs(port))) { - cthelper_test_stats.pkt_mismatch_port++; - return -1; - } - - /* - * FIXME: reminder, implement this below in the kernel for cthelper. - */ - - /* This packet contains no data, skip it. */ -/* if (l4h->l4pkt_no_data && l4h->l4pkt_no_data(pkt + l3hdr_len)) { - NFG_DEBUG("skipping packet with no data\n"); - continue; - } */ - - /* Create the fake network buffer. */ - pktb = pktb_alloc(AF_INET, pkt, pktlen, 128); - if (pktb == NULL) { - fprintf(stderr, "Not enough memory\n"); - return -1; - } - - dataoff = l3h->l3pkt_hdr_len(pkt); - if (dataoff > pktb_len(pktb)) { - fprintf(stderr, "wrong layer 3 offset: %d > %d\n", - dataoff, pktb_len(pktb)); - return -1; - } - - /* tweak to run DNAT mangling code using the same PCAP file. */ - if (type == TEST_DNAT) { - struct nf_conntrack *tmp = ct->myct->ct; - /* as long as this is tested, who cares the destination IP? */ - in_addr_t addr = inet_addr("1.1.1.1"); - - /* clone the real conntrack, to add DNAT information */ - ct->myct->ct = nfct_clone(ct->myct->ct); - /* set fake DNAT information */ - nfct_set_attr_u32(ct->myct->ct, ATTR_STATUS, IPS_DST_NAT); - nfct_set_attr_u32(ct->myct->ct, ATTR_ORIG_IPV4_DST, addr); - /* pass it to helper */ - ret = h->cb(pktb, dataoff, ct->myct, ctinfo); - /* restore real conntrack */ - nfct_destroy(ct->myct->ct); - ct->myct->ct = tmp; - - if (pktb_mangled(pktb)) { - uint32_t i; - uint8_t *data = pktb_network_header(pktb); - - printf("\e[1;31mmangled content: "); - - for (i=0; i < pktb_len(pktb); i++) - printf("%c", data[i]); - - printf("\e[0m\n"); - } - } else - ret = h->cb(pktb, dataoff, ct->myct, ctinfo); - - pktb_free(pktb); - - return ret; -} - -static int -cthelper_test(const char *pcapfile, const char *helper_name, - int l4proto, uint16_t port, int type) -{ - struct pcap_pkthdr pcaph; - char errbuf[PCAP_ERRBUF_SIZE]; - const u_char *pkt; - pcap_t *handle; - struct ctd_helper *h; - - h = helper_find("/usr/lib/conntrack-tools", - helper_name, l4proto, RTLD_NOW); - if (h == NULL) { - fprintf(stderr, "couldn't find helper: %s\n", helper_name); - return -1; - } - - handle = pcap_open_offline(pcapfile, errbuf); - if (handle == NULL) { - fprintf(stderr, "couldn't open pcap file %s: %s\n", - pcapfile, errbuf); - return -1; - } - while ((pkt = pcap_next(handle, &pcaph)) != NULL) { - cthelper_test_stats.pkts++; - cthelper_process_packet(pkt, pcaph.caplen, h, l4proto, port, - type); - } - - ct_flush(); - pcap_close(handle); - return 0; -} - -int main(int argc, char *argv[]) -{ - int ret, l4proto, type = TEST_NORMAL; - - if (argc < 5 || argc > 6) { - fprintf(stderr, "Wrong usage:\n"); - fprintf(stderr, "%s " - " [dnat]\n", - argv[0]); - fprintf(stderr, "example: %s file.pcap ftp tcp 21\n", argv[0]); - exit(EXIT_FAILURE); - } - if (strncmp("tcp", argv[3], strlen("tcp")) == 0) - l4proto = IPPROTO_TCP; - else if (strncmp("udp", argv[3], strlen("udp")) == 0) - l4proto = IPPROTO_UDP; - else { - fprintf(stderr, "%s not supported, send a patch to Pablo\n", - argv[3]); - exit(EXIT_FAILURE); - } - if (argc == 6) { - if (strncmp("dnat", argv[5], strlen("dnat")) == 0) { - type = TEST_DNAT; - printf("test dnat\n"); - } - } - - /* Initialization of supported layer 3 and 4 protocols here. */ - l2l3_ipv4_init(); - l4_tcp_init(); - l4_udp_init(); - - if (cthelper_test(argv[1], argv[2], l4proto, atoi(argv[4]), type) < 0) - ret = EXIT_FAILURE; - else - ret = EXIT_SUCCESS; - - printf("\e[1;34mTest results: expect_created=%d packets=%d " - "packets_skipped=%d\e[0m\n", - cthelper_test_stats.ct_expect_created, - cthelper_test_stats.pkts, - cthelper_test_stats.pkt_mismatch_proto + - cthelper_test_stats.pkt_mismatch_port); - - return ret; -} diff --git a/tests/conntrackd/cthelper/pcaps/nfsv3.pcap b/tests/conntrackd/cthelper/pcaps/nfsv3.pcap deleted file mode 100644 index 04606bd..0000000 Binary files a/tests/conntrackd/cthelper/pcaps/nfsv3.pcap and /dev/null differ diff --git a/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap b/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap deleted file mode 100644 index 32f8952..0000000 Binary files a/tests/conntrackd/cthelper/pcaps/oracle-tns-redirect.pcap and /dev/null differ diff --git a/tests/conntrackd/cthelper/proto.c b/tests/conntrackd/cthelper/proto.c deleted file mode 100755 index 6a1f345..0000000 --- a/tests/conntrackd/cthelper/proto.c +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include - -#include "linux_list.h" -#include "proto.h" - -static LIST_HEAD(l2l3_helper_list); -static LIST_HEAD(l4_helper_list); - -struct cthelper_proto_l2l3_helper * -cthelper_proto_l2l3_helper_find(const uint8_t *pkt, - unsigned int *l4protonum, - unsigned int *l3hdr_len) -{ - const struct ethhdr *eh = (const struct ethhdr *)pkt; - struct cthelper_proto_l2l3_helper *cur; - - list_for_each_entry(cur, &l2l3_helper_list, head) { - if (ntohs(cur->l2protonum) == eh->h_proto) { - *l4protonum = cur->l4pkt_proto(pkt + ETH_HLEN); - *l3hdr_len = cur->l3pkt_hdr_len(pkt + ETH_HLEN); - return cur; - } - } - return NULL; -} - -void cthelper_proto_l2l3_helper_register(struct cthelper_proto_l2l3_helper *h) -{ - list_add(&h->head, &l2l3_helper_list); -} - -struct cthelper_proto_l4_helper * -cthelper_proto_l4_helper_find(const uint8_t *pkt, unsigned int l4protocol) -{ - struct cthelper_proto_l4_helper *cur; - - list_for_each_entry(cur, &l4_helper_list, head) { - if (cur->l4protonum == l4protocol) - return cur; - } - return NULL; -} - -void cthelper_proto_l4_helper_register(struct cthelper_proto_l4_helper *h) -{ - list_add(&h->head, &l4_helper_list); -} diff --git a/tests/conntrackd/cthelper/proto.h b/tests/conntrackd/cthelper/proto.h deleted file mode 100755 index 9e99eea..0000000 --- a/tests/conntrackd/cthelper/proto.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _HELPER_H_ -#define _HELPER_H_ - -#include - -#include "../../../include/linux_list.h" - -struct nf_conntrack; - -struct cthelper_proto_l4_helper { - struct list_head head; - - unsigned int l4protonum; - - void (*l4ct_build)(const uint8_t *pkt, struct nf_conntrack *ct); - int (*l4ct_cmp_orig)(const uint8_t *pkt, struct nf_conntrack *ct); - int (*l4ct_cmp_repl)(const uint8_t *pkt, struct nf_conntrack *ct); - int (*l4ct_cmp_port)(struct nf_conntrack *ct, uint16_t port); - - int (*l4pkt_no_data)(const uint8_t *pkt); -}; - -struct cthelper_proto_l2l3_helper { - struct list_head head; - - unsigned int l2protonum; - unsigned int l2hdr_len; - - unsigned int l3protonum; - - void (*l3ct_build)(const uint8_t *pkt, struct nf_conntrack *ct); - int (*l3ct_cmp_orig)(const uint8_t *pkt, struct nf_conntrack *ct); - int (*l3ct_cmp_repl)(const uint8_t *pkt, struct nf_conntrack *ct); - - int (*l3pkt_hdr_len)(const uint8_t *pkt); - int (*l4pkt_proto)(const uint8_t *pkt); -}; - -struct cthelper_proto_l2l3_helper *cthelper_proto_l2l3_helper_find(const uint8_t *pkt, unsigned int *l4protonum, unsigned int *l3hdr_len); -void cthelper_proto_l2l3_helper_register(struct cthelper_proto_l2l3_helper *h); - -struct cthelper_proto_l4_helper *cthelper_proto_l4_helper_find(const uint8_t *pkt, unsigned int l4protonum); -void cthelper_proto_l4_helper_register(struct cthelper_proto_l4_helper *h); - -/* Initialization of supported protocols here. */ -void l2l3_ipv4_init(void); -void l4_tcp_init(void); -void l4_udp_init(void); - -#endif diff --git a/tests/conntrackd/cthelper/run-test.sh b/tests/conntrackd/cthelper/run-test.sh deleted file mode 100644 index fe31602..0000000 --- a/tests/conntrackd/cthelper/run-test.sh +++ /dev/null @@ -1,11 +0,0 @@ -echo "Running test for oracle TNS port 1521" -./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 - -echo "Running test for oracle TNS port 1521" -./cthelper-test pcaps/oracle-tns-redirect.pcap tns tcp 1521 dnat - -echo "Running test for NFSv3 UDP port 111" -./cthelper-test pcaps/nfsv3.pcap rpc udp 111 - -echo "Running test for NFSv3 TCP port 111" -./cthelper-test pcaps/nfsv3.pcap rpc tcp 111 diff --git a/tests/conntrackd/cthelper/test.h b/tests/conntrackd/cthelper/test.h deleted file mode 100644 index 4f5a6b6..0000000 --- a/tests/conntrackd/cthelper/test.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _CTHELPER_TEST_H_ -#define _CTHELPER_TEST_H_ - -struct cthelper_test_stats { - int pkts; - int pkt_mismatch_proto; - int pkt_mismatch_port; - int ct_expect_created; -}; - -extern struct cthelper_test_stats cthelper_test_stats; - -#endif -- cgit v1.2.3 From 140bde424c1381353f37ebc3395305f2acfcf546 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 7 Jun 2013 21:34:35 +0200 Subject: cthelper: add IPv6 support Signed-off-by: Pablo Neira Ayuso --- src/cthelper.c | 70 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/cthelper.c b/src/cthelper.c index 307be96..f333625 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,8 @@ nfq_hdr_put(char *buf, int type, uint32_t queue_num) static int pkt_get(void *pkt, uint32_t pktlen, uint16_t proto, uint32_t *protoff) { + uint8_t protocol; + switch(proto) { case ETHERTYPE_IP: { struct iphdr *ip = (struct iphdr *) pkt; @@ -94,41 +97,60 @@ pkt_get(void *pkt, uint32_t pktlen, uint16_t proto, uint32_t *protoff) } *protoff = 4 * ip->ihl; + protocol = ip->protocol; + break; + } + case ETHERTYPE_IPV6: { + struct iphdr *ip = (struct iphdr *) pkt; + struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt; - switch (ip->protocol) { - case IPPROTO_TCP: { - struct tcphdr *tcph = - (struct tcphdr *) ((char *)pkt + *protoff); - - /* No room for IPv4 header plus TCP header. */ - if (pktlen < *protoff + sizeof(struct tcphdr) - || pktlen < *protoff + tcph->doff * 4) { - dlog(LOG_ERR, "no room for IPv4 + TCP header, skip"); - return -1; - } - return 0; + /* No room for IPv6 header. */ + if (pktlen < sizeof(struct ip6_hdr)) { + dlog(LOG_ERR, "no room for IPv6 header"); + return -1; } - case IPPROTO_UDP: - /* No room for IPv4 header plus UDP header. */ - if (pktlen < *protoff + sizeof(struct udphdr)) { - dlog(LOG_ERR, "no room for IPv4 + UDP header, skip"); - return -1; - } - return 0; - default: - dlog(LOG_ERR, "not TCP/UDP, skipping"); + + /* this is not IPv6, skip. */ + if (ip->version != 6) { + dlog(LOG_ERR, "not IPv6, skipping"); return -1; } + + *protoff = sizeof(struct ip6_hdr); + protocol = ip6->ip6_nxt; break; } - case ETHERTYPE_IPV6: - dlog(LOG_ERR, "no IPv6 support sorry"); - return 0; default: /* Unknown layer 3 protocol. */ dlog(LOG_ERR, "unknown layer 3 protocol (%d), skipping", proto); return -1; } + + switch (protocol) { + case IPPROTO_TCP: { + struct tcphdr *tcph = + (struct tcphdr *) ((char *)pkt + *protoff); + + /* No room for IPv4 header plus TCP header. */ + if (pktlen < *protoff + sizeof(struct tcphdr) || + pktlen < *protoff + tcph->doff * 4) { + dlog(LOG_ERR, "no room for IPv4 + TCP header, skip"); + return -1; + } + return 0; + } + case IPPROTO_UDP: + /* No room for IPv4 header plus UDP header. */ + if (pktlen < *protoff + sizeof(struct udphdr)) { + dlog(LOG_ERR, "no room for IPv4 + UDP header, skip"); + return -1; + } + return 0; + default: + dlog(LOG_ERR, "not TCP/UDP, skipping"); + return -1; + } + return 0; } -- cgit v1.2.3 From 01a4c0925c455723cbb7f026614c91a9bc8bf62a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 7 Jun 2013 21:36:39 +0200 Subject: cthelper: helpers may not use private information area Signed-off-by: Pablo Neira Ayuso --- src/cthelper.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cthelper.c b/src/cthelper.c index f333625..5a8a92a 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -165,9 +165,11 @@ pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct, nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); - /* save private data and send it back to kernel-space. */ - nfct_set_attr_l(myct->ct, ATTR_HELPER_INFO, myct->priv_data, - cur->helper->priv_data_len); + /* save private data and send it back to kernel-space, if any. */ + if (myct->priv_data) { + nfct_set_attr_l(myct->ct, ATTR_HELPER_INFO, myct->priv_data, + cur->helper->priv_data_len); + } nfq_nlmsg_verdict_put(nlh, id, verdict); if (pktb_mangled(pktb)) -- cgit v1.2.3 From d343b8c554b6a04f6c477841dc4cbb89b5cd1bd9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 4 Jul 2013 16:04:39 +0200 Subject: conntrack: add connlabel format attribute Signed-off-by: Florian Westphal --- conntrack.8 | 4 +++- src/conntrack.c | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/conntrack.8 b/conntrack.8 index a411fd4..d80a778 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -88,11 +88,13 @@ Show the in-kernel connection tracking system statistics. Atomically zero counters after reading them. This option is only valid in combination with the "-L, --dump" command options. .TP -.BI "-o, --output [extended,xml,timestamp,id,ktimestamp] " +.BI "-o, --output [extended,xml,timestamp,id,ktimestamp,labels] " Display output in a certain format. With the extended output option, this tool displays the layer 3 information. With ktimestamp, it displays the in-kernel timestamp available since 2.6.38 (you can enable it via echo 1 > /proc/sys/net/netfilter/nf_conntrack_timestamp). +The labels output option tells conntrack to show the names of connection +tracking labels that might be present. .TP .BI "-e, --event-mask " "[ALL|NEW|UPDATES|DESTROY][,...]" Set the bitmask of events that are to be generated by the in-kernel ctnetlink diff --git a/src/conntrack.c b/src/conntrack.c index d4e79de..61e2fce 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -488,6 +488,7 @@ static unsigned int addr_valid_flags[ADDR_VALID_FLAGS_MAX] = { static LIST_HEAD(proto_list); static unsigned int options; +static struct nfct_labelmap *labelmap; void register_proto(struct ctproto_handler *h) { @@ -731,6 +732,7 @@ enum { _O_TMS = (1 << 2), _O_ID = (1 << 3), _O_KTMS = (1 << 4), + _O_CL = (1 << 5), }; enum { @@ -749,8 +751,8 @@ static struct parse_parameter { { IPS_ASSURED, IPS_SEEN_REPLY, 0, IPS_FIXED_TIMEOUT, IPS_EXPECTED} }, { {"ALL", "NEW", "UPDATES", "DESTROY"}, 4, { CT_EVENT_F_ALL, CT_EVENT_F_NEW, CT_EVENT_F_UPD, CT_EVENT_F_DEL } }, - { {"xml", "extended", "timestamp", "id", "ktimestamp"}, 5, - { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS }, + { {"xml", "extended", "timestamp", "id", "ktimestamp", "labels", }, 6, + { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL }, }, }; @@ -1150,7 +1152,7 @@ static int event_cb(enum nf_conntrack_msg_type type, if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; - nfct_snprintf(buf, sizeof(buf), ct, type, op_type, op_flags); + nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap); printf("%s\n", buf); fflush(stdout); @@ -1194,7 +1196,7 @@ static int dump_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, type, op_type, op_flags, labelmap); printf("%s\n", buf); counter++; @@ -1879,6 +1881,11 @@ int main(int argc, char *argv[]) case 'o': options |= CT_OPT_OUTPUT; parse_parameter(optarg, &output_mask, PARSE_OUTPUT); + if (output_mask & _O_CL) { + labelmap = nfct_labelmap_new(NULL); + if (!labelmap) + perror("nfct_labelmap_new"); + } break; case 'z': options |= CT_OPT_ZERO; @@ -2372,6 +2379,8 @@ try_proc: free_tmpl_objects(); free_options(); + if (labelmap) + nfct_labelmap_destroy(labelmap); if (command && exit_msg[cmd][0]) { fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION); -- cgit v1.2.3 From c4ce3ffb9a9f0288376312206529eb1428f3aeca Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 6 Jul 2013 12:09:36 +0200 Subject: conntrackd: cache: fix hashing based on IPv6 address Use source and destination address, not only source address for hashing. Signed-off-by: Pablo Neira Ayuso --- src/cache-ct.c | 2 +- src/cache-exp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cache-ct.c b/src/cache-ct.c index 0ad8d2a..a538215 100644 --- a/src/cache-ct.c +++ b/src/cache-ct.c @@ -59,7 +59,7 @@ cache_hash6_ct(const struct nf_conntrack *ct, const struct hashtable *table) uint32_t a[10]; memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); - memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); + memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_DST), sizeof(uint32_t)*4); a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 | nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 | diff --git a/src/cache-exp.c b/src/cache-exp.c index e88877a..9183b2c 100644 --- a/src/cache-exp.c +++ b/src/cache-exp.c @@ -59,7 +59,7 @@ cache_hash6_exp(const struct nf_conntrack *ct, const struct hashtable *table) uint32_t a[10]; memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); - memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); + memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_DST), sizeof(uint32_t)*4); a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 | nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 | -- cgit v1.2.3 From e2c6576e775652c35d336afa0551676339c6a793 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 6 Jul 2013 14:48:04 +0200 Subject: conntrackd: deprecate `Family' in configuration file This patch deprecates the `Family' tweak in the configuration file. Several reasons for this: * If not specified, this was default to IPv4 only in table dumps from the kernel. However, non-IPv4 events were still received. This is inconsistent. * It's an early tweak that was not documented (not included in any of the example files). If we want to support any sort of consistent filtering based on the family, this should happen in the filtering code. After this patch, conntrackd uses AF_UNSPEC to dump the conntrack and expectation tables from the kernel. Reported-by: Bill Fink Signed-off-by: Pablo Neira Ayuso --- include/conntrackd.h | 1 - src/netlink.c | 12 ++++++------ src/read_config_yy.y | 9 +-------- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/include/conntrackd.h b/include/conntrackd.h index 19e613c..d338fc4 100644 --- a/include/conntrackd.h +++ b/include/conntrackd.h @@ -104,7 +104,6 @@ struct ct_conf { unsigned int netlink_buffer_size_max_grown; int nl_overrun_resync; unsigned int flags; - int family; /* protocol family */ unsigned int resend_queue_size; /* FTFW protocol */ unsigned int window_size; int poll_kernel_secs; diff --git a/src/netlink.c b/src/netlink.c index bd38d99..5be102e 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -146,9 +146,11 @@ void nl_resize_socket_buffer(struct nfct_handle *h) "to %u bytes", CONFIG(netlink_buffer_size)); } +static const int family = AF_UNSPEC; + int nl_dump_conntrack_table(struct nfct_handle *h) { - return nfct_query(h, NFCT_Q_DUMP, &CONFIG(family)); + return nfct_query(h, NFCT_Q_DUMP, &family); } static int @@ -182,7 +184,7 @@ int nl_flush_conntrack_table_selective(void) } nfct_callback_register(h, NFCT_T_ALL, nl_flush_selective_cb, NULL); - ret = nfct_query(h, NFCT_Q_DUMP, &CONFIG(family)); + ret = nfct_query(h, NFCT_Q_DUMP, &family); nfct_close(h); @@ -191,7 +193,6 @@ int nl_flush_conntrack_table_selective(void) int nl_send_resync(struct nfct_handle *h) { - int family = CONFIG(family); return nfct_send(h, NFCT_Q_DUMP, &family); } @@ -380,16 +381,15 @@ int nl_get_expect(struct nfct_handle *h, const struct nf_expect *exp) int nl_dump_expect_table(struct nfct_handle *h) { - return nfexp_query(h, NFCT_Q_DUMP, &CONFIG(family)); + return nfexp_query(h, NFCT_Q_DUMP, &family); } int nl_flush_expect_table(struct nfct_handle *h) { - return nfexp_query(h, NFCT_Q_FLUSH, &CONFIG(family)); + return nfexp_query(h, NFCT_Q_FLUSH, &family); } int nl_send_expect_resync(struct nfct_handle *h) { - int family = CONFIG(family); return nfexp_send(h, NFCT_Q_DUMP, &family); } diff --git a/src/read_config_yy.y b/src/read_config_yy.y index 72a9654..b824150 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -1193,10 +1193,7 @@ scheduler_line : T_PRIO T_NUMBER family : T_FAMILY T_STRING { - if (strncmp($2, "IPv6", strlen("IPv6")) == 0) - conf.family = AF_INET6; - else - conf.family = AF_INET; + print_err(CTD_CFG_WARN, "`Family' is deprecated, ignoring"); }; event_iterations_limit : T_EVENT_ITER_LIMIT T_NUMBER @@ -1863,10 +1860,6 @@ init_config(char *filename) yyparse(); fclose(fp); - /* default to IPv4 */ - if (CONFIG(family) == 0) - CONFIG(family) = AF_INET; - /* set to default is not specified */ if (strcmp(CONFIG(lockfile), "") == 0) strncpy(CONFIG(lockfile), DEFAULT_LOCKFILE, FILENAME_MAXLEN); -- cgit v1.2.3 From 479a37a549abf197ce59a4ae1666d8cba80fe977 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 6 Jul 2013 14:04:24 +0200 Subject: conntrackd: fix crash with IPv6 expectation in the filtering code Jul 5 00:41:06 sen-fw1 kernel: [274422.060695] conntrackd[4821]: segfault at 0 ip 000000000040c660 sp 00007fffebb098a8 error 4 in conntrackd[400000+3d000] > #0 0x000000000040f217 in jhash2 (k=0x0, length=4, initval=0) at ../include/jhash.h:99 > a = 2654435769 b = 2654435769 c = 0 len = 4 > #1 0x000000000040f564 in ct_filter_hash6 (data=0x0, table=0x16ef630) at filter.c:57 > #2 0x000000000040ad34 in hashtable_hash (table=0x16ef630, data=0x0) at hash.c:63 > #3 0x000000000040fd19 in __ct_filter_test_ipv6 (f=0x16eeba0, ct=0x1703760) at filter.c:265 > id_src = 51 id_dst = 24051376 src = 0x1703760 dst = 0x0 The master conntrack of the expectation has no reply tuple. However, the filtering routine needs it. To avoid this issue, emulate the source address in the reply tuple. While at it, fix incorrect sanity checking that should have caught this issue. Thanks to Florian Westphal for initial diagnosing of this bug. Reported-by: Bill Fink Signed-off-by: Pablo Neira Ayuso --- Make_global.am | 2 +- src/ctnl.c | 31 +++++++++++++++++++++++++------ src/filter.c | 8 ++++---- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/Make_global.am b/Make_global.am index 23c7dd0..8084249 100644 --- a/Make_global.am +++ b/Make_global.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = -std=gnu99 -W -Wall \ - -Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \ + -Wmissing-prototypes -Wwrite-strings -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \ -Wno-unused-parameter ${LIBNFNETLINK_CFLAGS} ${LIBMNL_CFLAGS} \ ${LIBNETFILTER_CONNTRACK_CFLAGS} \ ${LIBNETFILTER_CTTIMEOUT_CFLAGS} \ diff --git a/src/ctnl.c b/src/ctnl.c index bb54727..9e1cfa1 100644 --- a/src/ctnl.c +++ b/src/ctnl.c @@ -211,14 +211,35 @@ out: return NFCT_CB_CONTINUE; } +static const struct nf_conntrack *exp_get_master_ct(struct nf_expect *exp) +{ + struct nf_conntrack *master = + (struct nf_conntrack *)nfexp_get_attr(exp, ATTR_EXP_MASTER); + + /* The function ct_filter_conntrack needs the source address of the + * reply tuple, emulate it. + */ + switch (nfct_get_attr_u8(master, ATTR_L3PROTO)) { + case AF_INET: + nfct_set_attr_u32(master, ATTR_REPL_IPV4_SRC, + nfct_get_attr_u32(master, ATTR_IPV4_DST)); + break; + case AF_INET6: + nfct_set_attr(master, ATTR_REPL_IPV6_SRC, + nfct_get_attr(master, ATTR_IPV6_DST)); + break; + } + + return master; +} + static int exp_event_handler(const struct nlmsghdr *nlh, enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { int origin_type; - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); + const struct nf_conntrack *master = exp_get_master_ct(exp); STATE(stats).nl_events_received++; @@ -275,8 +296,7 @@ static int dump_handler(enum nf_conntrack_msg_type type, static int exp_dump_handler(enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); + const struct nf_conntrack *master = exp_get_master_ct(exp); if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; @@ -309,8 +329,7 @@ static int get_handler(enum nf_conntrack_msg_type type, static int exp_get_handler(enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); + const struct nf_conntrack *master = exp_get_master_ct(exp); if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; diff --git a/src/filter.c b/src/filter.c index 02a8078..e21cfde 100644 --- a/src/filter.c +++ b/src/filter.c @@ -373,8 +373,8 @@ static inline int ct_filter_sanity_check(const struct nf_conntrack *ct) switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) { case AF_INET: - if (!nfct_attr_is_set(ct, ATTR_IPV4_SRC) || - !nfct_attr_is_set(ct, ATTR_IPV4_DST)) { + if (!nfct_attr_is_set(ct, ATTR_ORIG_IPV4_SRC) || + !nfct_attr_is_set(ct, ATTR_REPL_IPV4_SRC)) { dlog(LOG_ERR, "missing IPv4 address. " "You forgot to load " "nf_conntrack_ipv4?"); @@ -382,8 +382,8 @@ static inline int ct_filter_sanity_check(const struct nf_conntrack *ct) } break; case AF_INET6: - if (!nfct_attr_is_set(ct, ATTR_IPV6_SRC) || - !nfct_attr_is_set(ct, ATTR_IPV6_DST)) { + if (!nfct_attr_is_set(ct, ATTR_ORIG_IPV6_SRC) || + !nfct_attr_is_set(ct, ATTR_REPL_IPV6_SRC)) { dlog(LOG_ERR, "missing IPv6 address. " "You forgot to load " "nf_conntrack_ipv6?"); -- cgit v1.2.3 From 586382d9a8389ee553db019fd9be14a8a7c0b8ec Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 11 Jul 2013 00:43:20 +0200 Subject: conntrackd: simplify expectation filtering This patch simplifies the expectation filtering by looking up for the master conntrack. If it does not exists, then we assume that we don't want this expectation either. This simplification also fixes the current broken expectation filtering, since the master conntrack from expectations has neither reply tuple nor state, however, the filtering code assumes the opposite. This partially reverts (479a37a conntrackd: fix crash with IPv6 expectation in the filtering code) since it was incorrectly setting the reply tuple of the master conntrack. Thanks to Bill Fink for providing feedback to resolve this issue. Signed-off-by: Pablo Neira Ayuso --- include/filter.h | 1 + include/internal.h | 1 + src/cache-ct.c | 11 +++++++++-- src/ctnl.c | 37 +++++++++---------------------------- src/filter.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/internal_bypass.c | 6 ++++++ src/internal_cache.c | 11 +++++++++++ 7 files changed, 82 insertions(+), 30 deletions(-) diff --git a/include/filter.h b/include/filter.h index 3c7c8cc..d0acd96 100644 --- a/include/filter.h +++ b/include/filter.h @@ -51,6 +51,7 @@ void ct_filter_set_logic(struct ct_filter *f, enum ct_filter_type type, enum ct_filter_logic logic); int ct_filter_conntrack(const struct nf_conntrack *ct, int userspace); +int ct_filter_master(const struct nf_conntrack *master); struct exp_filter; struct nf_expect; diff --git a/include/internal.h b/include/internal.h index 2ba9714..1a796a7 100644 --- a/include/internal.h +++ b/include/internal.h @@ -40,6 +40,7 @@ struct internal_handler { void (*new)(struct nf_expect *exp, int origin_type); void (*upd)(struct nf_expect *exp, int origin_type); int (*del)(struct nf_expect *exp, int origin_type); + int (*find)(const struct nf_conntrack *master); void (*dump)(int fd, int type); void (*populate)(struct nf_expect *exp); diff --git a/src/cache-ct.c b/src/cache-ct.c index a538215..f86d143 100644 --- a/src/cache-ct.c +++ b/src/cache-ct.c @@ -88,14 +88,21 @@ cache_ct_hash(const void *data, const struct hashtable *table) return ret; } +/* master conntrack of expectations have no ID */ +static inline int +cache_ct_cmp_id(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2) +{ + return nfct_attr_is_set(ct2, ATTR_ID) ? + nfct_get_attr_u32(ct1, ATTR_ID) == nfct_get_attr_u32(ct2, ATTR_ID) : 1; +} + static int cache_ct_cmp(const void *data1, const void *data2) { const struct cache_object *obj = data1; const struct nf_conntrack *ct = data2; return nfct_cmp(obj->ptr, ct, NFCT_CMP_ORIG) && - nfct_get_attr_u32(obj->ptr, ATTR_ID) == - nfct_get_attr_u32(ct, ATTR_ID); + cache_ct_cmp_id(obj->ptr, ct); } static void *cache_ct_alloc(void) diff --git a/src/ctnl.c b/src/ctnl.c index 9e1cfa1..10b5f4c 100644 --- a/src/ctnl.c +++ b/src/ctnl.c @@ -211,35 +211,14 @@ out: return NFCT_CB_CONTINUE; } -static const struct nf_conntrack *exp_get_master_ct(struct nf_expect *exp) -{ - struct nf_conntrack *master = - (struct nf_conntrack *)nfexp_get_attr(exp, ATTR_EXP_MASTER); - - /* The function ct_filter_conntrack needs the source address of the - * reply tuple, emulate it. - */ - switch (nfct_get_attr_u8(master, ATTR_L3PROTO)) { - case AF_INET: - nfct_set_attr_u32(master, ATTR_REPL_IPV4_SRC, - nfct_get_attr_u32(master, ATTR_IPV4_DST)); - break; - case AF_INET6: - nfct_set_attr(master, ATTR_REPL_IPV6_SRC, - nfct_get_attr(master, ATTR_IPV6_DST)); - break; - } - - return master; -} - static int exp_event_handler(const struct nlmsghdr *nlh, enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { int origin_type; - const struct nf_conntrack *master = exp_get_master_ct(exp); + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); STATE(stats).nl_events_received++; @@ -247,7 +226,7 @@ static int exp_event_handler(const struct nlmsghdr *nlh, STATE(stats).nl_events_filtered++; goto out; } - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; origin_type = origin_find(nlh); @@ -296,12 +275,13 @@ static int dump_handler(enum nf_conntrack_msg_type type, static int exp_dump_handler(enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { - const struct nf_conntrack *master = exp_get_master_ct(exp); + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; switch(type) { @@ -329,12 +309,13 @@ static int get_handler(enum nf_conntrack_msg_type type, static int exp_get_handler(enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { - const struct nf_conntrack *master = exp_get_master_ct(exp); + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; STATE(get_retval) = 1; diff --git a/src/filter.c b/src/filter.c index e21cfde..8fac71b 100644 --- a/src/filter.c +++ b/src/filter.c @@ -407,6 +407,51 @@ int ct_filter_conntrack(const struct nf_conntrack *ct, int userspace) return 0; } +static inline int +ct_filter_master_sanity_check(const struct nf_conntrack *master) +{ + if (master == NULL) { + dlog(LOG_ERR, "no master tuple in expectation"); + return 0; + } + + if (!nfct_attr_is_set(master, ATTR_L3PROTO)) { + dlog(LOG_ERR, "missing layer 3 protocol"); + return 0; + } + + switch (nfct_get_attr_u8(master, ATTR_L3PROTO)) { + case AF_INET: + if (!nfct_attr_is_set(master, ATTR_IPV4_SRC) || + !nfct_attr_is_set(master, ATTR_IPV4_DST)) { + dlog(LOG_ERR, "missing IPv4 address. " + "You forgot to load nf_conntrack_ipv4?"); + return 0; + } + break; + case AF_INET6: + if (!nfct_attr_is_set(master, ATTR_IPV6_SRC) || + !nfct_attr_is_set(master, ATTR_IPV6_DST)) { + dlog(LOG_ERR, "missing IPv6 address. " + "You forgot to load nf_conntrack_ipv6?"); + return 0; + } + break; + } + return 1; +} + +int ct_filter_master(const struct nf_conntrack *master) +{ + if (!ct_filter_master_sanity_check(master)) + return 1; + + /* Check if we've got a master conntrack for this expectation in our + * caches. If there is not, we don't want this expectation either. + */ + return STATE(mode)->internal->exp.find(master) ? 0 : 1; +} + struct exp_filter { struct list_head list; }; diff --git a/src/internal_bypass.c b/src/internal_bypass.c index 1194339..ce2ae46 100644 --- a/src/internal_bypass.c +++ b/src/internal_bypass.c @@ -283,6 +283,11 @@ static int internal_bypass_exp_event_del(struct nf_expect *exp, int origin) return 1; } +static int internal_bypass_exp_master_find(const struct nf_conntrack *master) +{ + return nl_get_conntrack(STATE(get), master) == 0; +} + struct internal_handler internal_bypass = { .init = internal_bypass_init, .close = internal_bypass_close, @@ -309,5 +314,6 @@ struct internal_handler internal_bypass = { .new = internal_bypass_exp_event_new, .upd = internal_bypass_exp_event_upd, .del = internal_bypass_exp_event_del, + .find = internal_bypass_exp_master_find, }, }; diff --git a/src/internal_cache.c b/src/internal_cache.c index ba2d74b..bad31f3 100644 --- a/src/internal_cache.c +++ b/src/internal_cache.c @@ -364,6 +364,16 @@ static int internal_cache_exp_event_del(struct nf_expect *exp, int origin) return 1; } +static int internal_cache_exp_master_find(const struct nf_conntrack *master) +{ + struct cache_object *obj; + int id; + + obj = cache_find(STATE(mode)->internal->ct.data, + (struct nf_conntrack *)master, &id); + return obj ? 1 : 0; +} + struct internal_handler internal_cache = { .flags = INTERNAL_F_POPULATE | INTERNAL_F_RESYNC, .init = internal_cache_init, @@ -391,5 +401,6 @@ struct internal_handler internal_cache = { .new = internal_cache_exp_event_new, .upd = internal_cache_exp_event_upd, .del = internal_cache_exp_event_del, + .find = internal_cache_exp_master_find, }, }; -- cgit v1.2.3 From 1239b83da27545e3275127ac339cdca29c872304 Mon Sep 17 00:00:00 2001 From: Clemence Faure Date: Tue, 9 Jul 2013 10:37:02 +0200 Subject: conntrack: introduce -l option to filter by labels Signed-off-by: Clemence Faure Signed-off-by: Florian Westphal --- conntrack.8 | 5 ++ include/conntrack.h | 2 +- src/conntrack.c | 156 +++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 137 insertions(+), 26 deletions(-) diff --git a/conntrack.8 b/conntrack.8 index d80a778..f273434 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -144,6 +144,11 @@ the MARK value into the ctmark. Otherwise, the mask is logically ANDed with the existing mark before the comparision. In "--create" mode, the mask is ignored. .TP +.BI "-l, --label " "LABEL,..." +Specify the conntrack labels. +This option is only available in conjunction with "-L, --dump" or "-E, --event". +Match entries whose labels matches at least those specified as arguments. +.TP .BI "-c, --secmark " "SECMARK" Specify the conntrack selinux security mark. .TP diff --git a/include/conntrack.h b/include/conntrack.h index fd6126b..6cd9962 100644 --- a/include/conntrack.h +++ b/include/conntrack.h @@ -10,7 +10,7 @@ #include #define NUMBER_OF_CMD 19 -#define NUMBER_OF_OPT 24 +#define NUMBER_OF_OPT 25 struct ctproto_handler { struct list_head head; diff --git a/src/conntrack.c b/src/conntrack.c index 61e2fce..353ff61 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -79,6 +79,9 @@ static struct { /* Allow to filter by mark from kernel-space. */ struct nfct_filter_dump_mark filter_mark_kernel; + + /* Allows filtering by ctlabels */ + struct nfct_bitmask *label; } tmpl; static int alloc_tmpl_objects(void) @@ -104,6 +107,8 @@ static void free_tmpl_objects(void) nfct_destroy(tmpl.mask); if (tmpl.exp) nfexp_destroy(tmpl.exp); + if (tmpl.label) + nfct_bitmask_destroy(tmpl.label); } enum ct_command { @@ -247,13 +252,16 @@ enum ct_options { CT_OPT_ZONE_BIT = 23, CT_OPT_ZONE = (1 << CT_OPT_ZONE_BIT), + + CT_OPT_LABEL_BIT = 24, + CT_OPT_LABEL = (1 << CT_OPT_LABEL_BIT), }; /* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */ /* Update this mask to allow to filter based on new options. */ #define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | \ CT_OPT_MARK | CT_OPT_SECMARK | CT_OPT_STATUS | \ - CT_OPT_ID | CT_OPT_ZONE) + CT_OPT_ID | CT_OPT_ZONE | CT_OPT_LABEL) static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_ORIG_SRC_BIT] = "src", @@ -280,6 +288,7 @@ static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_BUFFERSIZE_BIT] = "buffer-size", [CT_OPT_ANY_NAT_BIT] = "any-nat", [CT_OPT_ZONE_BIT] = "zone", + [CT_OPT_LABEL_BIT] = "label", }; static struct option original_opts[] = { @@ -320,12 +329,13 @@ static struct option original_opts[] = { {"buffer-size", 1, 0, 'b'}, {"any-nat", 2, 0, 'j'}, {"zone", 1, 0, 'w'}, + {"label", 1, 0, 'l'}, {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:"; + "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 @@ -340,26 +350,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*/ -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, -/*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}, + /* 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}, }; static const int cmd2type[][2] = { @@ -391,6 +401,7 @@ static const int opt2type[] = { ['i'] = CT_OPT_ID, ['j'] = CT_OPT_ANY_NAT, ['w'] = CT_OPT_ZONE, + ['l'] = CT_OPT_LABEL, }; static const int opt2family_attr[][2] = { @@ -413,6 +424,7 @@ static const int opt2attr[] = { ['c'] = ATTR_SECMARK, ['i'] = ATTR_ID, ['w'] = ATTR_ZONE, + ['l'] = ATTR_CONNLABELS, }; static char exit_msg[NUMBER_OF_CMD][64] = { @@ -450,7 +462,8 @@ static const char usage_conntrack_parameters[] = " -c, --secmark secmark\t\t\tSet selinux secmark\n" " -e, --event-mask eventmask\t\tEvent mask, eg. NEW,DESTROY\n" " -z, --zero \t\t\t\tZero counters while listing\n" - " -o, --output type[,...]\t\tOutput format, eg. xml\n"; + " -o, --output type[,...]\t\tOutput format, eg. xml\n" + " -l, --label label[,...]\t\tconntrack labels\n"; static const char usage_expectation_parameters[] = "Expectation parameters and options:\n" @@ -816,6 +829,59 @@ parse_u32_mask(const char *arg, struct u32_mask *m) m->mask = ~0; } +static int +get_label(char *name) +{ + int bit = nfct_labelmap_get_bit(labelmap, name); + if (bit < 0) + exit_error(PARAMETER_PROBLEM, "unknown label '%s'", name); + return bit; +} + +static void +set_label(struct nfct_bitmask *b, char *name) +{ + int bit = get_label(name); + nfct_bitmask_set_bit(b, bit); +} + +static unsigned int +set_max_label(char *name, unsigned int current_max) +{ + int bit = get_label(name); + if ((unsigned int) bit > current_max) + return (unsigned int) bit; + return current_max; +} + +static unsigned int +parse_label_get_max(char *arg) +{ + unsigned int max = 0; + char *parse; + + while ((parse = strchr(arg, ',')) != NULL) { + parse[0] = '\0'; + max = set_max_label(arg, max); + arg = &parse[1]; + } + + max = set_max_label(arg, max); + return max; +} + +static void +parse_label(struct nfct_bitmask *b, char *arg) +{ + char * parse; + while ((parse = strchr(arg, ',')) != NULL) { + parse[0] = '\0'; + set_label(b, arg); + arg = &parse[1]; + } + set_label(b, arg); +} + static void add_command(unsigned int *cmd, const int newcmd) { @@ -984,6 +1050,24 @@ usage(char *prog) static unsigned int output_mask; +static int +filter_label(const struct nf_conntrack *ct) +{ + if (tmpl.label == NULL) + return 0; + + const struct nfct_bitmask *ctb = nfct_get_attr(ct, ATTR_CONNLABELS); + if (ctb == NULL) + return 1; + + for (unsigned int i = 0; i <= nfct_bitmask_maxbit(tmpl.label); i++) { + if (nfct_bitmask_test_bit(tmpl.label, i) && + !nfct_bitmask_test_bit(ctb, i)) + return 1; + } + + return 0; +} static int filter_mark(const struct nf_conntrack *ct) @@ -994,7 +1078,6 @@ filter_mark(const struct nf_conntrack *ct) return 0; } - static int filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct) { @@ -1125,6 +1208,9 @@ static int event_cb(enum nf_conntrack_msg_type type, if (filter_mark(ct)) return NFCT_CB_CONTINUE; + if (filter_label(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -1177,6 +1263,9 @@ static int dump_cb(enum nf_conntrack_msg_type type, if (filter_mark(ct)) return NFCT_CB_CONTINUE; + if (filter_label(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -1882,7 +1971,8 @@ int main(int argc, char *argv[]) options |= CT_OPT_OUTPUT; parse_parameter(optarg, &output_mask, PARSE_OUTPUT); if (output_mask & _O_CL) { - labelmap = nfct_labelmap_new(NULL); + if (!labelmap) + labelmap = nfct_labelmap_new(NULL); if (!labelmap) perror("nfct_labelmap_new"); } @@ -1929,6 +2019,22 @@ int main(int argc, char *argv[]) tmpl.filter_mark_kernel.val = tmpl.mark.value; tmpl.filter_mark_kernel.mask = tmpl.mark.mask; break; + case 'l': + 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"); + + unsigned int max = parse_label_get_max(optarg); + struct nfct_bitmask * b = nfct_bitmask_new(max); + + parse_label(b, optarg2); + tmpl.label = b; + free(optarg2); + break; case 'a': fprintf(stderr, "WARNING: ignoring -%c, " "deprecated option.\n", c); -- cgit v1.2.3 From c9cba32f4820a9febee116bbc268ec8b1ae9a04c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 30 Jun 2013 23:10:47 +0200 Subject: conntrackd: support replication of connlabels - check if ct has label attribute, and at least one label (bit) is set - serialize bitmap into array-of-u32, in network byte order - add code to build new nfct_bitmask object from array-of-u32 Current parse functions don't have length information, this adds optional parse2() which gets struct netattr pointer. Attributes that want to use parse2 need to set .maxsize to nonzero value. Signed-off-by: Florian Westphal --- include/network.h | 4 +++ src/build.c | 39 ++++++++++++++++++++++++++++ src/parse.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 110 insertions(+), 10 deletions(-) diff --git a/include/network.h b/include/network.h index 79745f3..cc312cb 100644 --- a/include/network.h +++ b/include/network.h @@ -228,9 +228,13 @@ enum nta_attr { NTA_TCP_WSCALE_ORIG, /* uint8_t */ NTA_TCP_WSCALE_REPL, /* uint8_t */ NTA_HELPER_NAME, /* string (variable length) */ + NTA_LABELS, /* array of uint32_t (variable length) */ NTA_MAX }; +/* allow to serialize/replicate up to 4k labels per flow */ +#define NTA_LABELS_MAX_SIZE (4096/sizeof(uint32_t)) + struct nta_attr_natseqadj { uint32_t orig_seq_correction_pos; uint32_t orig_seq_offset_before; diff --git a/src/build.c b/src/build.c index e15eb4f..5799b51 100644 --- a/src/build.c +++ b/src/build.c @@ -158,6 +158,42 @@ static void build_l4proto_udp(const struct nf_conntrack *ct, struct nethdr *n) sizeof(struct nfct_attr_grp_port)); } +static void ct_build_clabel(const struct nf_conntrack *ct, struct nethdr *n) +{ + const struct nfct_bitmask *b; + uint32_t *words; + unsigned int wordcount, i, maxbit; + + if (!nfct_attr_is_set(ct, ATTR_CONNLABELS)) + return; + + b = nfct_get_attr(ct, ATTR_CONNLABELS); + + maxbit = nfct_bitmask_maxbit(b); + for (i=0; i <= maxbit; i++) { + if (nfct_bitmask_test_bit(b, i)) + break; + } + + if (i > maxbit) + return; + + wordcount = (nfct_bitmask_maxbit(b) / 32) + 1; + words = put_header(n, NTA_LABELS, wordcount * sizeof(*words)); + + for (i=0; i < wordcount; i++) { + int bit = 31; + uint32_t tmp = 0; + + do { + if (nfct_bitmask_test_bit(b, (32 * i) + bit)) + tmp |= (1 << bit); + } while (--bit >= 0); + + words[i] = htonl(tmp); + } +} + #ifndef IPPROTO_DCCP #define IPPROTO_DCCP 33 #endif @@ -233,6 +269,9 @@ void ct2msg(const struct nf_conntrack *ct, struct nethdr *n) if (nfct_attr_is_set(ct, ATTR_HELPER_NAME)) ct_build_str(ct, ATTR_HELPER_NAME, n, NTA_HELPER_NAME); + + if (nfct_attr_is_set(ct, ATTR_CONNLABELS)) + ct_build_clabel(ct, n); } static void diff --git a/src/parse.c b/src/parse.c index 8ce4495..f3ec6ac 100644 --- a/src/parse.c +++ b/src/parse.c @@ -29,15 +29,19 @@ static void ct_parse_u8(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_u16(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_u32(struct nf_conntrack *ct, int attr, void *data); -static void ct_parse_str(struct nf_conntrack *ct, int attr, void *data); +static void ct_parse_str(struct nf_conntrack *ct, + const struct netattr *, void *data); static void ct_parse_group(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data); +static void ct_parse_clabel(struct nf_conntrack *ct, + const struct netattr *, void *data); struct ct_parser { void (*parse)(struct nf_conntrack *ct, int attr, void *data); - int attr; - int size; - int max_size; + void (*parse2)(struct nf_conntrack *ct, const struct netattr *, void *); + uint16_t attr; + uint16_t size; + uint16_t max_size; }; static struct ct_parser h[NTA_MAX] = { @@ -176,10 +180,15 @@ static struct ct_parser h[NTA_MAX] = { .size = NTA_SIZE(sizeof(uint8_t)), }, [NTA_HELPER_NAME] = { - .parse = ct_parse_str, + .parse2 = ct_parse_str, .attr = ATTR_HELPER_NAME, .max_size = NFCT_HELPER_NAME_MAX, }, + [NTA_LABELS] = { + .parse2 = ct_parse_clabel, + .attr = ATTR_CONNLABELS, + .max_size = NTA_SIZE(NTA_LABELS_MAX_SIZE), + }, }; static void @@ -204,9 +213,9 @@ ct_parse_u32(struct nf_conntrack *ct, int attr, void *data) } static void -ct_parse_str(struct nf_conntrack *ct, int attr, void *data) +ct_parse_str(struct nf_conntrack *ct, const struct netattr *attr, void *data) { - nfct_set_attr(ct, h[attr].attr, data); + nfct_set_attr(ct, h[attr->nta_attr].attr, data); } static void @@ -215,6 +224,44 @@ ct_parse_group(struct nf_conntrack *ct, int attr, void *data) nfct_set_attr_grp(ct, h[attr].attr, data); } +static void +ct_parse_clabel(struct nf_conntrack *ct, const struct netattr *attr, void *data) +{ + struct nfct_bitmask *bitm; + unsigned int i, wordcount; + const uint32_t *words; + unsigned int len; + + len = attr->nta_len - NTA_LENGTH(0); + wordcount = len / sizeof(*words); + if (!wordcount) + return; + + if (len & (sizeof(*words) - 1)) + return; + + bitm = nfct_bitmask_new((len * 8) - 1); + if (!bitm) + return; + + words = data; + for (i=0; i < wordcount; i++) { + uint32_t word; + int bit; + + if (words[i] == 0) + continue; + + word = htonl(words[i]); + bit = 31; + do { + if (word & (1 << bit)) + nfct_bitmask_set_bit(bitm, (32 * i) + bit); + } while (--bit >= 0); + } + nfct_set_attr(ct, ATTR_CONNLABELS, bitm); +} + static void ct_parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data) { @@ -248,14 +295,22 @@ int msg2ct(struct nf_conntrack *ct, struct nethdr *net, size_t remain) ATTR_NETWORK2HOST(attr); if (attr->nta_len > len) return -1; + if (attr->nta_len < NTA_LENGTH(0)) + return -1; if (attr->nta_attr > NTA_MAX) return -1; if (h[attr->nta_attr].size && attr->nta_len != h[attr->nta_attr].size) return -1; - if (h[attr->nta_attr].max_size && - attr->nta_len > h[attr->nta_attr].max_size) - return -1; + + if (h[attr->nta_attr].max_size) { + if (attr->nta_len > h[attr->nta_attr].max_size) + return -1; + h[attr->nta_attr].parse2(ct, attr, NTA_DATA(attr)); + attr = NTA_NEXT(attr, len); + continue; + } + if (h[attr->nta_attr].parse == NULL) { attr = NTA_NEXT(attr, len); continue; @@ -457,6 +512,8 @@ int msg2exp(struct nf_expect *exp, struct nethdr *net, size_t remain) goto err; if (attr->nta_attr > NTA_MAX) goto err; + if (attr->nta_len < NTA_LENGTH(0)) + goto err; if (exp_h[attr->nta_attr].size && attr->nta_len != exp_h[attr->nta_attr].size) goto err; -- cgit v1.2.3 From 89d016bb2cd72013cec1ae1c5cb2dfff15563d51 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 24 Jul 2013 12:40:50 +0200 Subject: conntrack: fix -L format output commit d343b8c (conntrack: add connlabel format attribute) erronously removed _UNKNOWN format, i.e. conntrack -L displayed [UPDATE] tcp 6 114 TIME_WAIT src=.. ^^^^^ Reported-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- src/conntrack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conntrack.c b/src/conntrack.c index 353ff61..af36f78 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1285,7 +1285,7 @@ static int dump_cb(enum nf_conntrack_msg_type type, if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; - nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap); + nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap); printf("%s\n", buf); counter++; -- cgit v1.2.3 From 8e9906b69b03eb12fc75e857a347c4505332b820 Mon Sep 17 00:00:00 2001 From: Clemence Faure Date: Tue, 23 Jul 2013 15:00:18 +0200 Subject: conntrack: fix reporting of unknown arguments short options were always reported as "unknown argument". getopt(3) says: if [it] finds an option character in argv that was not included in optstring, or if it detects a missing option argument, it returns '?' and sets the external variable optopt to the actual option character. If the first character [...] of optstring is a colon (':'), then getopt() returns ':' instead of '?' to indicate a missing option argument. Signed-off-by: Clemence Faure Signed-off-by: Florian Westphal --- src/conntrack.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index af36f78..7d2a365 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -333,7 +333,7 @@ static struct option original_opts[] = { {0, 0, 0, 0} }; -static const char *getopt_str = "L::I::U::D::G::E::F::hVs:d:r:q:" +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:"; @@ -2054,15 +2054,13 @@ int main(int argc, char *argv[]) socketbuffersize = atol(optarg); options |= CT_OPT_BUFFERSIZE; break; + case ':': + exit_error(PARAMETER_PROBLEM, + "option `%s' requires an " + "argument", argv[optind-1]); case '?': - if (optopt) - exit_error(PARAMETER_PROBLEM, - "option `%s' requires an " - "argument", argv[optind-1]); - else - exit_error(PARAMETER_PROBLEM, - "unknown option `%s'", - argv[optind-1]); + exit_error(PARAMETER_PROBLEM, + "unknown option `%s'", argv[optind-1]); break; default: if (h && h->parse_opts -- cgit v1.2.3 From e38ff8f127b61f879f57439e37c1ad9b9bc10a04 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 6 Aug 2013 15:11:27 +0200 Subject: cthelper: fix IPv6 address and mask in newly created expectations Set to zero the entire address if needed, not just 4 bytes. Signed-off-by: Pablo Neira Ayuso --- src/expect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/expect.c b/src/expect.c index 6069770..470b9ae 100644 --- a/src/expect.c +++ b/src/expect.c @@ -54,7 +54,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, nfct_set_attr(expected, ATTR_IPV6_SRC, saddr->ip6); for (i=0; i<4; i++) - memset(addr, 0xffffffff, sizeof(uint32_t)); + memset(&addr[i], 0xffffffff, sizeof(uint32_t)); nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); nfct_set_attr(mask, ATTR_IPV6_SRC, addr); @@ -76,7 +76,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, break; case AF_INET6: for (i=0; i<4; i++) - memset(addr, 0x00000000, sizeof(uint32_t)); + memset(&addr[i], 0x00000000, sizeof(uint32_t)); nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); nfct_set_attr(expected, ATTR_IPV6_SRC, addr); -- cgit v1.2.3 From fbe3181be4f2e33509b1c20b95fd55eb3e7075d7 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 6 Aug 2013 18:24:36 +0200 Subject: conntrack-tools 1.4.2 release bump dependency with libnetfilter_conntrack to 1.0.4, otherwise we don't get the connlabel support. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index c544773..8bb4bec 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(conntrack-tools, 1.4.1, pablo@netfilter.org) +AC_INIT(conntrack-tools, 1.4.2, pablo@netfilter.org) AC_CONFIG_AUX_DIR([build-aux]) AC_CANONICAL_HOST @@ -56,7 +56,7 @@ fi PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.1]) PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3]) -PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.3]) +PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.4]) PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) PKG_CHECK_MODULES([LIBNETFILTER_CTHELPER], [libnetfilter_cthelper >= 1.0.0]) PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.2]) -- cgit v1.2.3 From 5c35c86e2d9d4c522514cceec5e19e08e05ed4db Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 7 Aug 2013 15:22:41 +0200 Subject: conntrack: fix dump of IPv6 entries in the dying and unconfirmed list Use selected the family, instead of inconditionally request for IPv4. Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index 7d2a365..bb4a026 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1601,7 +1601,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 +1613,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 +1626,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 +1652,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) @@ -2114,7 +2115,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 +2125,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; @@ -2389,7 +2390,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 +2435,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 +2454,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(); -- cgit v1.2.3 From 43fbb438858cb704e23763091f1bc97a6c4bb098 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 3 Sep 2013 17:28:40 +0200 Subject: conntrack: minor cleanup Rename get_table to generic "optional argument handling" helper, so it can be re-used in upcoming patch. While at it, avoid copy&paste of "labelmap" handling. Signed-off-by: Florian Westphal --- src/conntrack.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index bb4a026..8da94bf 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -890,20 +890,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 +915,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) @@ -1819,6 +1819,15 @@ 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"); +} + int main(int argc, char *argv[]) { int c, cmd; @@ -1971,12 +1980,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; @@ -1988,12 +1993,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; @@ -2024,10 +2024,7 @@ int main(int argc, char *argv[]) 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(); unsigned int max = parse_label_get_max(optarg); struct nfct_bitmask * b = nfct_bitmask_new(max); -- cgit v1.2.3 From 06454c33f44c0f4d71a88a82b82da7bba5abde2d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 5 Sep 2013 11:27:48 +0200 Subject: conntrack: support multiple -l options Using -l foo -l bar caused the "foo" label to be lost. Merge multiple -l options so "-l foo,bar" and "-l foo -l bar" have same effect. Signed-off-by: Florian Westphal --- conntrack.8 | 7 ++++--- src/conntrack.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/conntrack.8 b/conntrack.8 index f273434..6410e5b 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -144,10 +144,11 @@ the MARK value into the ctmark. Otherwise, the mask is logically ANDed with the existing mark before the comparision. In "--create" mode, the mask is ignored. .TP -.BI "-l, --label " "LABEL,..." -Specify the conntrack labels. +.BI "-l, --label " "LABEL" +Specify a conntrack label. This option is only available in conjunction with "-L, --dump" or "-E, --event". -Match entries whose labels matches at least those specified as arguments. +Match entries whose labels match at least those specified. +Use multiple -l commands to specify multiple labels that need to be set. .TP .BI "-c, --secmark " "SECMARK" Specify the conntrack selinux security mark. diff --git a/src/conntrack.c b/src/conntrack.c index 8da94bf..fe68e42 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1828,6 +1828,31 @@ static void labelmap_init(void) 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); +} + int main(int argc, char *argv[]) { int c, cmd; @@ -2030,7 +2055,9 @@ int main(int argc, char *argv[]) struct nfct_bitmask * b = nfct_bitmask_new(max); parse_label(b, optarg2); - tmpl.label = b; + + /* join "-l foo -l bar" into single bitmask object */ + merge_bitmasks(&tmpl.label, b); free(optarg2); break; case 'a': -- cgit v1.2.3 From 991fc4ae561bfc7c9bc9da9598b0cc704295811f Mon Sep 17 00:00:00 2001 From: Clemence Faure Date: Thu, 5 Sep 2013 11:27:49 +0200 Subject: conntrack: support add/delete of conntrack labels new options "--label-add" and "--label-delete" to alter connlabels assigned to a connection. Signed-off-by: Clemence Faure Signed-off-by: Florian Westphal --- conntrack.8 | 10 +++- include/conntrack.h | 2 +- src/conntrack.c | 154 ++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 140 insertions(+), 26 deletions(-) diff --git a/conntrack.8 b/conntrack.8 index 6410e5b..45e8582 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -146,9 +146,17 @@ In "--create" mode, the mask is ignored. .TP .BI "-l, --label " "LABEL" Specify a conntrack label. -This option is only available in conjunction with "-L, --dump" or "-E, --event". +This option is only available in conjunction with "-L, --dump", "-E, --event", or "-U --update". Match entries whose labels match at least those specified. Use multiple -l commands to specify multiple labels that need to be set. +Match entries whose labels matches at least those specified as arguments. +.BI "--label-add " "LABEL" +Specify the conntrack label to add to to the selected conntracks. +This option is only available in conjunction with "-I, --create" or "-U, --update". +.BI "--label-del " "[LABEL]" +Specify the conntrack label to delete from the selected conntracks. +If no label is given, all labels are deleted. +This option is only available in conjunction with "-U, --update". .TP .BI "-c, --secmark " "SECMARK" Specify the conntrack selinux security mark. diff --git a/include/conntrack.h b/include/conntrack.h index 6cd9962..c2a0c8f 100644 --- a/include/conntrack.h +++ b/include/conntrack.h @@ -10,7 +10,7 @@ #include #define NUMBER_OF_CMD 19 -#define NUMBER_OF_OPT 25 +#define NUMBER_OF_OPT 27 struct ctproto_handler { struct list_head head; diff --git a/src/conntrack.c b/src/conntrack.c index fe68e42..404ecc9 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] = { @@ -425,6 +442,8 @@ static const int opt2attr[] = { ['i'] = ATTR_ID, ['w'] = ATTR_ZONE, ['l'] = ATTR_CONNLABELS, + ['<'] = ATTR_CONNLABELS, + ['>'] = ATTR_CONNLABELS, }; static char exit_msg[NUMBER_OF_CMD][64] = { @@ -472,6 +491,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" @@ -1045,6 +1069,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 +1374,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 +1401,58 @@ 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) { + newmask = xnfct_bitmask_clone(tmpl.label_modify); + 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_set_bit(ctb, i); + } + + newmask = xnfct_bitmask_clone(tmpl.label_modify); + 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 +1472,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 +1483,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)) { @@ -2046,18 +2127,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); 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); /* join "-l foo -l bar" into single bitmask object */ - merge_bitmasks(&tmpl.label, b); + if (c == 'l') { + merge_bitmasks(&tmpl.label, b); + } else { + merge_bitmasks(&tmpl.label_modify, b); + } + free(optarg2); break; case 'a': @@ -2216,6 +2318,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"); -- cgit v1.2.3 From fee95ed0db0745b551dfb15c58800da5c1ca9e5f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 5 Sep 2013 11:27:50 +0200 Subject: conntrack: do not exit when update returns an error If we fail to update an entry, just try to continue with the next one instead of exiting. Can happen f.e. when using "conntrack -U --add-label bla", but the conntrack entry in the kernel does not have the label extension set. Signed-off-by: Florian Westphal --- src/conntrack.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index 404ecc9..1e45ca8 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1492,12 +1492,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); -- cgit v1.2.3 From 8c38d35c3d90d493fdead6d4ead0517ec09fee96 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 7 Aug 2013 19:41:30 +0200 Subject: conntrackd: cthelper: allow to attach expectations via nfqueue This requires the Linux kernel 3.12. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink_queue.h | 13 +++++++++++++ include/myct.h | 1 + src/cthelper.c | 11 +++++++++++ 3 files changed, 25 insertions(+) diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h index e0d8fd8..0132bad 100644 --- a/include/linux/netfilter/nfnetlink_queue.h +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -44,6 +44,9 @@ enum nfqnl_attr_type { NFQA_PAYLOAD, /* opaque data payload */ NFQA_CT, /* nf_conntrack_netlink.h */ NFQA_CT_INFO, /* enum ip_conntrack_info */ + NFQA_CAP_LEN, /* __u32 length of captured packet */ + NFQA_SKB_INFO, /* __u32 skb meta information */ + NFQA_EXP, /* nf_conntrack_netlink.h */ __NFQA_MAX }; @@ -95,5 +98,15 @@ enum nfqnl_attr_config { /* Flags for NFQA_CFG_FLAGS */ #define NFQA_CFG_F_FAIL_OPEN (1 << 0) #define NFQA_CFG_F_CONNTRACK (1 << 1) +#define NFQA_CFG_F_GSO (1 << 2) +#define NFQA_CFG_F_MAX (1 << 3) + +/* flags for NFQA_SKB_INFO */ +/* packet appears to have wrong checksums, but they are ok */ +#define NFQA_SKB_CSUMNOTREADY (1 << 0) +/* packet is GSO (i.e., exceeds device mtu) */ +#define NFQA_SKB_GSO (1 << 1) +/* csum not validated (incoming device doesn't support hw checksum, etc.) */ +#define NFQA_SKB_CSUM_NOTVERIFIED (1 << 2) #endif /* _NFNETLINK_QUEUE_H */ diff --git a/include/myct.h b/include/myct.h index 45d9f29..02d695c 100644 --- a/include/myct.h +++ b/include/myct.h @@ -37,6 +37,7 @@ struct myct_tuple { struct myct { struct nf_conntrack *ct; + struct nf_expect *exp; void *priv_data; }; diff --git a/src/cthelper.c b/src/cthelper.c index 5a8a92a..fec40fb 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -182,6 +182,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; @@ -317,6 +326,8 @@ static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data) if (ct != NULL) nfct_destroy(ct); + if (myct->exp != NULL) + nfexp_destroy(myct->exp); if (myct && myct->priv_data != NULL) free(myct->priv_data); if (myct != NULL) -- cgit v1.2.3 From 36118bfc4901b0978d2c8f17912fe91ec66f35e8 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 6 Aug 2013 14:21:04 +0200 Subject: conntrackd: helpers: add DHCPv6 helper This patch adds support for the DHCPv6 helper. 1) nfct helper add dhcpv6 inet6 udp 2) ip6tables -I OUTPUT -t raw -p udp --sport 546 -j CT --helper dhcpv6 3) run conntrackd You should see: % conntrack -L exp -f ipv6 279 proto=17 src=:: dst=ff02::1:2 sport=0 dport=546 mask-src=:: mask-dst=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff sport=0 dport=65535 master-src=fe80::221:ccff:fe4a:7f9c master-dst=ff02::1:2 sport=546 dport=547 PERMANENT class=0 helper=dhcpv6 Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 8 +++ src/helpers/Makefile.am | 7 ++- src/helpers/dhcpv6.c | 123 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 src/helpers/dhcpv6.c diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 56f5162..358ad10 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -62,6 +62,14 @@ Helper { ExpectTimeout 300 } } + Type dhcpv6 inet6 udp { + QueueNum 4 + QueueLen 10240 + Policy dhcpv6 { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 589b4f3..59524f5 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,9 +1,14 @@ include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_ftp.la \ +pkglib_LTLIBRARIES = ct_helper_dhcpv6.la \ + ct_helper_ftp.la \ ct_helper_rpc.la \ ct_helper_tns.la +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) ct_helper_ftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) 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 + * + * Adapted from: + * + * DHCPv6 multicast connection tracking helper. + * + * (c) 2012 Google Inc. + * + * Original author: Darren Willis + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} -- cgit v1.2.3 From 0cf75aaf19ffd08e7c63fee737423d01343f4cb9 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 26 Sep 2013 18:25:45 +0200 Subject: nfct: modularize extensions Modularize timeout and helper extensions. Signed-off-by: Pablo Neira Ayuso --- include/nfct.h | 25 +++++++++++-------------- src/nfct-extensions/helper.c | 31 +++++++++++++++++++++++-------- src/nfct-extensions/timeout.c | 20 ++++++++++++++++++-- src/nfct.c | 35 +++++++++++++++++++++++++++++------ 4 files changed, 81 insertions(+), 30 deletions(-) diff --git a/include/nfct.h b/include/nfct.h index 5548b03..ddf9038 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -1,6 +1,8 @@ #ifndef _NFCT_H_ #define _NFCT_H_ +#include "linux_list.h" + enum { NFCT_SUBSYS_NONE = 0, NFCT_SUBSYS_TIMEOUT, @@ -19,21 +21,16 @@ enum { NFCT_CMD_DISABLE, }; +#define __init __attribute__((constructor)) + void nfct_perror(const char *msg); -int nfct_cmd_timeout_parse_params(int argc, char *argv[]); -int nfct_cmd_timeout_list(int argc, char *argv[]); -int nfct_cmd_timeout_add(int argc, char *argv[]); -int nfct_cmd_timeout_delete(int argc, char *argv[]); -int nfct_cmd_timeout_get(int argc, char *argv[]); -int nfct_cmd_timeout_flush(int argc, char *argv[]); - -int nfct_cmd_helper_parse_params(int argc, char *argv[]); -int nfct_cmd_helper_list(int argc, char *argv[]); -int nfct_cmd_helper_add(int argc, char *argv[]); -int nfct_cmd_helper_delete(int argc, char *argv[]); -int nfct_cmd_helper_get(int argc, char *argv[]); -int nfct_cmd_helper_flush(int argc, char *argv[]); -int nfct_cmd_helper_disable(int argc, char *argv[]); +struct nfct_extension { + struct list_head head; + int type; + int (*parse_params)(int argc, char *argv[]); +}; + +void nfct_extension_register(struct nfct_extension *ext); #endif diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index f91fc41..98ccde2 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -37,8 +37,14 @@ 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(int argc, char *argv[]); +static int nfct_cmd_helper_add(int argc, char *argv[]); +static int nfct_cmd_helper_delete(int argc, char *argv[]); +static int nfct_cmd_helper_get(int argc, char *argv[]); +static int nfct_cmd_helper_flush(int argc, char *argv[]); +static int nfct_cmd_helper_disable(int argc, char *argv[]); + +static int nfct_cmd_helper_parse_params(int argc, char *argv[]) { int cmd = NFCT_CMD_NONE, ret = 0; @@ -115,7 +121,7 @@ err: return MNL_CB_OK; } -int nfct_cmd_helper_list(int argc, char *argv[]) +static int nfct_cmd_helper_list(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -165,7 +171,7 @@ int nfct_cmd_helper_list(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_add(int argc, char *argv[]) +static int nfct_cmd_helper_add(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -281,7 +287,7 @@ int nfct_cmd_helper_add(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_delete(int argc, char *argv[]) +static int nfct_cmd_helper_delete(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -375,7 +381,7 @@ int nfct_cmd_helper_delete(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_get(int argc, char *argv[]) +static int nfct_cmd_helper_get(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -468,7 +474,7 @@ int nfct_cmd_helper_get(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_flush(int argc, char *argv[]) +static int nfct_cmd_helper_flush(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -519,7 +525,7 @@ int nfct_cmd_helper_flush(int argc, char *argv[]) return 0; } -int nfct_cmd_helper_disable(int argc, char *argv[]) +static int nfct_cmd_helper_disable(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -613,3 +619,12 @@ int nfct_cmd_helper_disable(int argc, char *argv[]) return 0; } +static struct nfct_extension helper = { + .type = NFCT_SUBSYS_HELPER, + .parse_params = nfct_cmd_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..dde489a 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -35,7 +35,13 @@ nfct_cmd_timeout_usage(char *argv[]) "[parameters...]\n", VERSION, argv[0]); } -int nfct_cmd_timeout_parse_params(int argc, char *argv[]) +static int nfct_cmd_timeout_list(int argc, char *argv[]); +static int nfct_cmd_timeout_add(int argc, char *argv[]); +static int nfct_cmd_timeout_delete(int argc, char *argv[]); +static int nfct_cmd_timeout_get(int argc, char *argv[]); +static int nfct_cmd_timeout_flush(int argc, char *argv[]); + +static int nfct_cmd_timeout_parse_params(int argc, char *argv[]) { int cmd = NFCT_CMD_NONE, ret; @@ -105,7 +111,7 @@ err: return MNL_CB_OK; } -int nfct_cmd_timeout_list(int argc, char *argv[]) +static int nfct_cmd_timeout_list(int argc, char *argv[]) { struct mnl_socket *nl; char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -484,3 +490,13 @@ int nfct_cmd_timeout_flush(int argc, char *argv[]) return 0; } + +static struct nfct_extension timeout = { + .type = NFCT_SUBSYS_TIMEOUT, + .parse_params = nfct_cmd_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..4795570 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -25,6 +25,7 @@ #include #include +#include "linux_list.h" #include "nfct.h" static int nfct_cmd_version(int argc, char *argv[]); @@ -46,9 +47,28 @@ 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; +} + int main(int argc, char *argv[]) { int subsys = NFCT_SUBSYS_NONE, ret = 0; + struct nfct_extension *ext; if (argc < 2) { usage(argv); @@ -70,18 +90,21 @@ int main(int argc, char *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; 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; + } + ret = ext->parse_params(argc, argv); + break; } return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } -- cgit v1.2.3 From ecfe6e93016559fdd18013ab5a2e1f200d330310 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 26 Sep 2013 17:53:06 +0200 Subject: build: add --disable-cthelper and --disable-cttimeout This patch allows you to disable userspace helper support and conntrack timeout tuning at build stage. By default, both features are enabled, to avoid breaking backward compatibility. Signed-off-by: Pablo Neira Ayuso --- configure.ac | 26 +++++++++++++++++++++++--- src/Makefile.am | 39 ++++++++++++++++++++++++++++++--------- src/read_config_yy.y | 6 ++++++ src/run.c | 9 ++++++--- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index 8bb4bec..f0800d6 100644 --- a/configure.ac +++ b/configure.ac @@ -54,12 +54,27 @@ else flex.]) fi +AC_ARG_ENABLE([cthelper], + AS_HELP_STRING([--disable-cthelper], [Do not build userspace helper support]), + [enable_cthelper="no"], [enable_cthelper="yes"]) +AC_ARG_ENABLE([cttimeout], + AS_HELP_STRING([--disable-cttimeout], [Do not build timeout support]), + [enable_cttimeout="no"], [enable_cttimeout="yes"]) + PKG_CHECK_MODULES([LIBNFNETLINK], [libnfnetlink >= 1.0.1]) PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.3]) PKG_CHECK_MODULES([LIBNETFILTER_CONNTRACK], [libnetfilter_conntrack >= 1.0.4]) -PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) -PKG_CHECK_MODULES([LIBNETFILTER_CTHELPER], [libnetfilter_cthelper >= 1.0.0]) -PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.2]) +AS_IF([test "x$enable_cttimeout" = "xyes"], [ + PKG_CHECK_MODULES([LIBNETFILTER_CTTIMEOUT], [libnetfilter_cttimeout >= 1.0.0]) +]) +AM_CONDITIONAL([HAVE_CTTIMEOUT], [test "x$enable_cttimeout" = "xyes"]) + +AS_IF([test "x$enable_cthelper" = "xyes"], [ + PKG_CHECK_MODULES([LIBNETFILTER_CTHELPER], [libnetfilter_cthelper >= 1.0.0]) + PKG_CHECK_MODULES([LIBNETFILTER_QUEUE], [libnetfilter_queue >= 1.0.2]) + AC_DEFINE([BUILD_CTHELPER], [1], [Building cthelper support]) +]) +AM_CONDITIONAL([HAVE_CTHELPER], [test "x$enable_cthelper" = "xyes"]) AC_CHECK_HEADERS([linux/capability.h],, [AC_MSG_ERROR([Cannot find linux/capabibility.h])]) @@ -126,3 +141,8 @@ fi AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/linux/Makefile include/linux/netfilter/Makefile extensions/Makefile src/helpers/Makefile]) AC_OUTPUT + +echo " +conntrack-tools configuration: + userspace conntrack helper support: ${enable_cthelper} + conntrack timeout support: ${enable_cttimeout}" diff --git a/src/Makefile.am b/src/Makefile.am index ec03e46..1bc3622 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,29 @@ 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 +43,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 +53,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/read_config_yy.y b/src/read_config_yy.y index b824150..fa517bb 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -1612,12 +1612,18 @@ helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}' exit(EXIT_FAILURE); } +#ifdef BUILD_CTHELPER /* XXX use configure.ac definitions. */ helper = helper_find("/usr/lib/conntrack-tools", $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"); -- cgit v1.2.3 From b495e1d22faff636589a9646fbd4bb30902d3542 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 30 Sep 2013 16:06:58 +0200 Subject: nfct: timeout: use getprotoent The kernel bails out for unsupported protocols. Moreover, we don't need to upgrade to support new protocols. Signed-off-by: Pablo Neira Ayuso --- src/nfct-extensions/timeout.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index dde489a..7811bb2 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -184,6 +185,7 @@ int nfct_cmd_timeout_add(int argc, char *argv[]) uint8_t l4proto; int ret, i; unsigned int j; + struct protoent *pent; if (argc < 6) { nfct_perror("missing parameters\n" @@ -211,28 +213,22 @@ int nfct_cmd_timeout_add(int argc, char *argv[]) } 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"); - return -1; - } + pent = getprotobyname(argv[5]); + if (!pent) { + /* In Debian, /etc/protocols says ipv6-icmp. Support icmpv6 + * as well not to break backward compatibility. + */ + if (strcmp(argv[5], "icmpv6") == 0) + l4proto = IPPROTO_ICMPV6; + else if (strcmp(argv[5], "generic") == 0) + l4proto = IPPROTO_RAW; + else { + nfct_perror("unknown layer 4 protocol"); + return -1; + } + } else + l4proto = pent->p_proto; + nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO, l4proto); for (i=6; i Date: Mon, 30 Sep 2013 16:31:03 +0200 Subject: nfct: timeout: split nfct_cmd_timeout_add in several functions This patch is a cleanup to split this function in smaller chunks. It is required to prepare default protocol timeout tuning via netlink. --- src/nfct-extensions/timeout.c | 123 +++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index 7811bb2..0cf92bb 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -1,5 +1,5 @@ /* - * (C) 2012 by Pablo Neira Ayuso + * (C) 2012-2013 by Pablo Neira Ayuso * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -174,53 +174,34 @@ 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; - struct protoent *pent; - - 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]); + int l3proto; - 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; } - nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO, l3proto); + return l3proto; +} + +static int nfct_cmd_get_l4proto(char *argv[]) +{ + int l4proto; + struct protoent *pent; - pent = getprotobyname(argv[5]); + pent = getprotobyname(*argv); if (!pent) { /* In Debian, /etc/protocols says ipv6-icmp. Support icmpv6 * as well not to break backward compatibility. */ - if (strcmp(argv[5], "icmpv6") == 0) + if (strcmp(*argv, "icmpv6") == 0) l4proto = IPPROTO_ICMPV6; - else if (strcmp(argv[5], "generic") == 0) + else if (strcmp(*argv, "generic") == 0) l4proto = IPPROTO_RAW; else { nfct_perror("unknown layer 4 protocol"); @@ -229,9 +210,35 @@ int nfct_cmd_timeout_add(int argc, char *argv[]) } 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); + + 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; i1; argc-=2, argv+=2) { int matching = -1; for (j=0; j= 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; + } + + return 0; +} + +int nfct_cmd_timeout_add(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 < 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 (nfct_cmd_timeout_parse(t, argc-4, &argv[4]) < 0) + return -1; seq = time(NULL); nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_NEW, -- cgit v1.2.3 From 386968d321d02571b593b3bbbf39891f44397469 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 30 Sep 2013 20:09:57 +0200 Subject: nfct: src: add nfct_mnl_talk and use it Add helper function nfct_mnl_talk and use it. Signed-off-by: Pablo Neira Ayuso --- include/nfct.h | 5 +++ src/nfct-extensions/helper.c | 99 ++++++------------------------------------- src/nfct-extensions/timeout.c | 82 +++++------------------------------ src/nfct.c | 25 +++++++++++ 4 files changed, 53 insertions(+), 158 deletions(-) diff --git a/include/nfct.h b/include/nfct.h index ddf9038..93717c5 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -33,4 +33,9 @@ struct nfct_extension { void nfct_extension_register(struct nfct_extension *ext); +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); + #endif diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index 98ccde2..4171a47 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -127,7 +127,6 @@ static int nfct_cmd_helper_list(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; unsigned int seq, portid; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -150,22 +149,11 @@ static int nfct_cmd_helper_list(int argc, char *argv[]) } 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; @@ -181,7 +169,7 @@ static 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" @@ -266,22 +254,11 @@ static int nfct_cmd_helper_add(int argc, char *argv[]) } 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; @@ -294,7 +271,6 @@ static int nfct_cmd_helper_delete(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_helper *t; - int ret; if (argc < 4) { nfct_perror("missing helper policy name"); @@ -359,20 +335,8 @@ static int nfct_cmd_helper_delete(int argc, char *argv[]) } 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; } @@ -388,7 +352,6 @@ static int nfct_cmd_helper_get(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_helper *t; - int ret; if (argc < 4) { nfct_perror("missing helper policy name"); @@ -453,22 +416,11 @@ static int nfct_cmd_helper_get(int argc, char *argv[]) } 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; @@ -480,7 +432,6 @@ static int nfct_cmd_helper_flush(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -503,20 +454,8 @@ static int nfct_cmd_helper_flush(int argc, char *argv[]) } 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; } @@ -535,7 +474,6 @@ static 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" @@ -598,22 +536,11 @@ static int nfct_cmd_helper_disable(int argc, char *argv[]) } 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; diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index 0cf92bb..c361dab 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -118,7 +118,6 @@ static int nfct_cmd_timeout_list(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; unsigned int seq, portid; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -141,22 +140,11 @@ static int nfct_cmd_timeout_list(int argc, char *argv[]) } 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_timeout_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_timeout_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; @@ -281,7 +269,6 @@ int nfct_cmd_timeout_add(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_timeout *t; - int ret; if (argc < 6) { nfct_perror("missing parameters\n" @@ -321,22 +308,11 @@ int nfct_cmd_timeout_add(int argc, char *argv[]) } 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; @@ -349,7 +325,6 @@ int nfct_cmd_timeout_delete(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_timeout *t; - int ret; if (argc < 4) { nfct_perror("missing timeout policy name"); @@ -386,20 +361,8 @@ int nfct_cmd_timeout_delete(int argc, char *argv[]) } 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; } @@ -415,7 +378,6 @@ int nfct_cmd_timeout_get(int argc, char *argv[]) struct nlmsghdr *nlh; uint32_t portid, seq; struct nfct_timeout *t; - int ret; if (argc < 4) { nfct_perror("missing timeout policy name"); @@ -452,22 +414,11 @@ int nfct_cmd_timeout_get(int argc, char *argv[]) } 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_timeout_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_timeout_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; @@ -479,7 +430,6 @@ int nfct_cmd_timeout_flush(int argc, char *argv[]) char buf[MNL_SOCKET_BUFFER_SIZE]; struct nlmsghdr *nlh; uint32_t portid, seq; - int ret; if (argc > 3) { nfct_perror("too many arguments"); @@ -502,20 +452,8 @@ int nfct_cmd_timeout_flush(int argc, char *argv[]) } 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; } diff --git a/src/nfct.c b/src/nfct.c index 4795570..84bb1b7 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -143,3 +143,28 @@ 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; +} -- cgit v1.2.3 From 3c78a4543e12f5e82bdd771971d3534fa452117b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 1 Oct 2013 13:23:39 +0200 Subject: nfct: src: consolidate netlink socket creation Open the socket from the main function, then pass it as parameter to the corresponding interpreter. Signed-off-by: Pablo Neira Ayuso --- include/nfct.h | 3 +- src/nfct-extensions/helper.c | 125 +++++++++--------------------------------- src/nfct-extensions/timeout.c | 103 ++++++---------------------------- src/nfct.c | 25 ++++++++- 4 files changed, 68 insertions(+), 188 deletions(-) diff --git a/include/nfct.h b/include/nfct.h index 93717c5..682fe3a 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -28,11 +28,12 @@ void nfct_perror(const char *msg); struct nfct_extension { struct list_head head; int type; - int (*parse_params)(int argc, char *argv[]); + int (*parse_params)(struct mnl_socket *nl, int argc, char *argv[]); }; void nfct_extension_register(struct nfct_extension *ext); +struct mnl_socket *nfct_mnl_open(void); 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), diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index 4171a47..7544ed7 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -37,14 +37,15 @@ nfct_cmd_helper_usage(char *argv[]) "[parameters...]\n", VERSION, argv[0]); } -static int nfct_cmd_helper_list(int argc, char *argv[]); -static int nfct_cmd_helper_add(int argc, char *argv[]); -static int nfct_cmd_helper_delete(int argc, char *argv[]); -static int nfct_cmd_helper_get(int argc, char *argv[]); -static int nfct_cmd_helper_flush(int argc, char *argv[]); -static int nfct_cmd_helper_disable(int argc, char *argv[]); - -static 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_cmd_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[]) { int cmd = NFCT_CMD_NONE, ret = 0; @@ -72,24 +73,25 @@ static int nfct_cmd_helper_parse_params(int argc, char *argv[]) nfct_cmd_helper_usage(argv); exit(EXIT_FAILURE); } + 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; } @@ -121,9 +123,8 @@ err: return MNL_CB_OK; } -static 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; @@ -137,18 +138,7 @@ static 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 (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) { nfct_perror("netlink error"); return -1; @@ -159,9 +149,8 @@ static int nfct_cmd_helper_list(int argc, char *argv[]) return 0; } -static 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; @@ -242,31 +231,18 @@ static 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 (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -static 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; @@ -323,31 +299,17 @@ static 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 (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -static 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; @@ -404,31 +366,18 @@ static 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 (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -static 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; @@ -442,18 +391,7 @@ static 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 (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; @@ -464,9 +402,9 @@ static int nfct_cmd_helper_flush(int argc, char *argv[]) return 0; } -static 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; @@ -524,25 +462,12 @@ static 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 (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index c361dab..9f74eca 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -36,13 +36,14 @@ nfct_cmd_timeout_usage(char *argv[]) "[parameters...]\n", VERSION, argv[0]); } -static int nfct_cmd_timeout_list(int argc, char *argv[]); -static int nfct_cmd_timeout_add(int argc, char *argv[]); -static int nfct_cmd_timeout_delete(int argc, char *argv[]); -static int nfct_cmd_timeout_get(int argc, char *argv[]); -static int nfct_cmd_timeout_flush(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_parse_params(int argc, char *argv[]) +static int +nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) { int cmd = NFCT_CMD_NONE, ret; @@ -68,19 +69,19 @@ static int nfct_cmd_timeout_parse_params(int argc, char *argv[]) } 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; } @@ -112,9 +113,8 @@ err: return MNL_CB_OK; } -static 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; @@ -128,25 +128,12 @@ static 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 (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } @@ -262,9 +249,8 @@ nfct_cmd_timeout_parse(struct nfct_timeout *t, int argc, char *argv[]) return 0; } -int nfct_cmd_timeout_add(int argc, char *argv[]) +int nfct_cmd_timeout_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; @@ -296,31 +282,17 @@ int nfct_cmd_timeout_add(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 (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; @@ -349,31 +321,17 @@ 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 (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; @@ -402,31 +360,17 @@ 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"); - 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 (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } -int nfct_cmd_timeout_flush(int argc, char *argv[]) +int nfct_cmd_timeout_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; @@ -440,25 +384,12 @@ int nfct_cmd_timeout_flush(int argc, char *argv[]) nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE, 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 (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { nfct_perror("netlink error"); return -1; } - mnl_socket_close(nl); - return 0; } diff --git a/src/nfct.c b/src/nfct.c index 84bb1b7..19e44be 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -69,6 +69,7 @@ int main(int argc, char *argv[]) { int subsys = NFCT_SUBSYS_NONE, ret = 0; struct nfct_extension *ext; + struct mnl_socket *nl; if (argc < 2) { usage(argv); @@ -103,7 +104,15 @@ int main(int argc, char *argv[]) VERSION, argv[1]); return EXIT_FAILURE; } - ret = ext->parse_params(argc, argv); + + nl = nfct_mnl_open(); + if (nl == NULL) { + nfct_perror("cannot open netlink"); + return -1; + } + + ret = ext->parse_params(nl, argc, argv); + mnl_socket_close(nl); break; } return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS; @@ -168,3 +177,17 @@ int nfct_mnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, 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; +} -- cgit v1.2.3 From 9b99aa2980574f4d3bf26145a1bf8bd69d34e764 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 3 Oct 2013 09:49:25 +0200 Subject: conntrackd: cthelper: add SANE helper This patch adds an userspace port of the SANE helper that is currently implemented in the kernel. This requires Linux kernel 3.12 to work. --- src/helpers/Makefile.am | 7 +- src/helpers/sane.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 src/helpers/sane.c diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 59524f5..ad380e3 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -3,7 +3,8 @@ include $(top_srcdir)/Make_global.am pkglib_LTLIBRARIES = ct_helper_dhcpv6.la \ ct_helper_ftp.la \ ct_helper_rpc.la \ - ct_helper_tns.la + ct_helper_tns.la \ + ct_helper_sane.la ct_helper_dhcpv6_la_SOURCES = dhcpv6.c ct_helper_dhcpv6_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) @@ -20,3 +21,7 @@ ct_helper_rpc_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) diff --git a/src/helpers/sane.c b/src/helpers/sane.c new file mode 100644 index 0000000..79ca948 --- /dev/null +++ b/src/helpers/sane.c @@ -0,0 +1,172 @@ +/* + * (C) 2013 by Pablo Neira Ayuso + * + * 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 + * Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c): + * (C) 1999-2001 Paul `Rusty' Russell + * (C) 2002-2004 Netfilter Core Team + * (C) 2003,2004 USAGI/WIDE Project + * (C) 2003 Yasuyuki Kozakai @USAGI + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} -- cgit v1.2.3 From ea753a152cbf3a2658b5ec5bacfb738c13a4c476 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 2 Oct 2013 19:21:01 +0200 Subject: conntrackd: cthelper: add TFTP helper This patch adds an userspace port of the TFTP helper that is currently implemented in the kernel. This includes NAT support. It requires a Linux kernel 3.12. Signed-off-by: Pablo Neira Ayuso --- include/helper.h | 3 ++ src/expect.c | 24 +++++++++ src/helpers/Makefile.am | 5 ++ src/helpers/tftp.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 170 insertions(+) create mode 100644 src/helpers/tftp.c diff --git a/include/helper.h b/include/helper.h index 9d96fb7..bd69af6 100644 --- a/include/helper.h +++ b/include/helper.h @@ -49,6 +49,9 @@ extern int cthelper_del_expect(struct nf_expect *exp); extern void cthelper_get_addr_src(struct nf_conntrack *ct, int dir, union nfct_attr_grp_addr *addr); extern void cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, union nfct_attr_grp_addr *addr); +void cthelper_get_port_src(struct nf_conntrack *ct, int dir, uint16_t *port); +void cthelper_get_port_dst(struct nf_conntrack *ct, int dir, uint16_t *port); + extern int in4_pton(const char *src, int srclen, uint8_t *dst, int delim, const char **end); extern int in6_pton(const char *src, int srclen, uint8_t *dst, int delim, const char **end); diff --git a/src/expect.c b/src/expect.c index 470b9ae..bba0ed7 100644 --- a/src/expect.c +++ b/src/expect.c @@ -212,3 +212,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/helpers/Makefile.am b/src/helpers/Makefile.am index ad380e3..216a5a7 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -3,6 +3,7 @@ include $(top_srcdir)/Make_global.am pkglib_LTLIBRARIES = ct_helper_dhcpv6.la \ ct_helper_ftp.la \ ct_helper_rpc.la \ + ct_helper_tftp.la \ ct_helper_tns.la \ ct_helper_sane.la @@ -18,6 +19,10 @@ 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) 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 + * + * Adapted from: + * + * (C) 2001-2002 Magnus Boden + * (C) 2006-2012 Patrick McHardy + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} -- cgit v1.2.3 From 808a25129605c9e7de9f86fb8d5a5ed3310edd43 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 7 Oct 2013 14:41:20 +0200 Subject: conntrackd: cthelper: add amanda helper This patch adds a userspace port of the amanda helper that is currently implemented in the kernel. Signed-off-by: Pablo Neira Ayuso --- src/helpers/Makefile.am | 7 +- src/helpers/amanda.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 src/helpers/amanda.c diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 216a5a7..fe28e83 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,12 +1,17 @@ include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_dhcpv6.la \ +pkglib_LTLIBRARIES = ct_helper_amanda.la \ + ct_helper_dhcpv6.la \ ct_helper_ftp.la \ ct_helper_rpc.la \ ct_helper_tftp.la \ ct_helper_tns.la \ ct_helper_sane.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) diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c new file mode 100644 index 0000000..c0cf701 --- /dev/null +++ b/src/helpers/amanda.c @@ -0,0 +1,203 @@ +/* + * (C) 2013 by Pablo Neira Ayuso + * + * Adapted from: + * + * Amanda extension for IP connection tracking + * + * (C) 2002 by Brian J. Murrell + * based on HW's ip_conntrack_irc.c as well as other modules + * (C) 2006 Patrick McHardy + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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")]; + u_int16_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; + u_int16_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); +} -- cgit v1.2.3 From f3a760cad83a30524ef40d55d18fa1489252c8fb Mon Sep 17 00:00:00 2001 From: Hani Benhabiles Date: Fri, 11 Oct 2013 21:05:34 +0100 Subject: nfct: Fix use-after-free / double-free helper's list and flush commands handlers shouldn't call mnl_socket_close on the passed netlink socket as it is done in the main function after parse_params call. Bug introduced in (3c78a45 nfct: src: consolidate netlink socket creation). Signed-off-by: Hani Benhabiles Signed-off-by: Pablo Neira Ayuso --- src/nfct-extensions/helper.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index 7544ed7..bfb153f 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -144,8 +144,6 @@ static int nfct_cmd_helper_list(struct mnl_socket *nl, int argc, char *argv[]) return -1; } - mnl_socket_close(nl); - return 0; } @@ -397,8 +395,6 @@ nfct_cmd_helper_flush(struct mnl_socket *nl, int argc, char *argv[]) return -1; } - mnl_socket_close(nl); - return 0; } -- cgit v1.2.3 From 7594d30611d7c4bec52ab90580b705fbf5d60925 Mon Sep 17 00:00:00 2001 From: Hani Benhabiles Date: Mon, 14 Oct 2013 23:14:24 +0100 Subject: conntrackd: Don't hardcode libs dir path Use CONNTRACKD_LIB_DIR instead of hardcoded path. Signed-off-by: Hani Benhabiles Signed-off-by: Pablo Neira Ayuso --- src/read_config_yy.y | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/read_config_yy.y b/src/read_config_yy.y index fa517bb..73fabbf 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -1613,8 +1613,7 @@ helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}' } #ifdef BUILD_CTHELPER - /* XXX use configure.ac definitions. */ - helper = helper_find("/usr/lib/conntrack-tools", $2, l4proto, RTLD_NOW); + 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); -- cgit v1.2.3 From 91cf387fc61483bff05897fa356cfe183e165558 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 24 Dec 2013 13:22:46 +0100 Subject: build: don't include leftover .orig and .rej files in doc/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And remove reference to .svn, we have been using git for quite some time already. Reported-by: Göran Uddeborg Signed-off-by: Pablo Neira Ayuso --- Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index bd366bf..975c538 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,4 +10,5 @@ DIST_SUBDIRS = include src extensions LIBS = @LIBNETFILTER_CONNTRACK_LIBS@ dist-hook: - rm -rf `find $(distdir)/doc -name .svn` + rm -rf `find $(distdir)/doc -name *.orig` + rm -rf `find $(distdir)/doc -name *.rej` -- cgit v1.2.3 From 92246dcc1fdcf222302a42926e0e95af2c30463e Mon Sep 17 00:00:00 2001 From: Ash Hughes Date: Sat, 8 Mar 2014 21:13:34 +0000 Subject: conntrackd: userspace SSDP helper Here is a patch which adds a userspace conntrack helper for the SSDP protocol. This is based on the code found at: http://marc.info/?t=132945775100001&r=1&w=2 I'm not sure how to get my laptop to play at IPv6, so I've not tested this part, but I've tested the IPv4 section and it works. Signed-off-by: Ash Hughes Signed-off-by: Pablo Neira Ayuso --- doc/helper/conntrackd.conf | 8 +++ doc/manual/conntrack-tools.tmpl | 1 + src/helpers/Makefile.am | 7 ++- src/helpers/ssdp.c | 134 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/helpers/ssdp.c diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index 358ad10..d2d94a9 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -70,6 +70,14 @@ Helper { ExpectTimeout 300 } } + Type ssdp inet udp { + QueueNum 5 + QueueLen 10240 + Policy ssdp { + ExpectMax 1 + ExpectTimeout 300 + } + } } # diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index f21a4ff..d23dec5 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -890,6 +890,7 @@ maintainance. Oracle*TNS, to support its special Redirect message. NFSv3, mind that version 4 does not require this helper. FTP (this helper is also available in kernel-space). +SSDP. The following steps describe how to enable the RPC portmapper helper for NFSv3 (this is similar for other helpers): diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index fe28e83..78ef7aa 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -6,7 +6,8 @@ pkglib_LTLIBRARIES = ct_helper_amanda.la \ ct_helper_rpc.la \ ct_helper_tftp.la \ ct_helper_tns.la \ - ct_helper_sane.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) @@ -35,3 +36,7 @@ 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/ssdp.c b/src/helpers/ssdp.c new file mode 100644 index 0000000..2713f23 --- /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 + * Based on the SSDP conntrack helper (nf_conntrack_ssdp.c), + * :http://marc.info/?t=132945775100001&r=1&w=2 + * (C) 2012 Ian Pilcher + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + u_int16_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); +} -- cgit v1.2.3 From 34a8e200eee54b4bbecadf52bba8901cae129795 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 12 May 2014 17:21:21 +0200 Subject: nfct: remove unneeded included header This fixes a compilation breakage when libnetfilter_cttimeout.h is not installed. Reported-by: Hangbin Liu Signed-off-by: Pablo Neira Ayuso --- src/nfct.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/nfct.c b/src/nfct.c index 19e44be..533d75d 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -22,8 +22,6 @@ #include #include -#include -#include #include "linux_list.h" #include "nfct.h" -- cgit v1.2.3 From 1ecda7339e8678c0b4debe7003b4a42791ad478e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 1 Oct 2013 13:28:11 +0200 Subject: nfct: timeout: add support for default protocol timeout tuning This new interface supersedes the /proc interface: /proc/sys/net/netfilter/nf_conntrack_PROTO_STATE_timeout to tune default conntrack timeout helpers. # nfct timeout default-get inet tcp .l3proto = 2, .l4proto = 6, .policy = { .SYN_SENT = 120, .SYN_RECV = 60, .ESTABLISHED = 432000, .FIN_WAIT = 120, .CLOSE_WAIT = 60, .LAST_ACK = 30, .TIME_WAIT = 120, .CLOSE = 10, .SYN_SENT2 = 120, .RETRANS = 300, .UNACKNOWLEDGED = 300, }, }; # nfct timeout default-set inet tcp ESTABLISHED 100 As replacement for the existing /proc interfaces for timeout tweaking. This feature requires a Linux kernel >= 3.13. Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink_cttimeout.h | 2 + include/nfct.h | 2 + src/nfct-extensions/timeout.c | 106 +++++++++++++++++++++++++- 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/include/linux/netfilter/nfnetlink_cttimeout.h b/include/linux/netfilter/nfnetlink_cttimeout.h index a2810a7..1ab0b97 100644 --- a/include/linux/netfilter/nfnetlink_cttimeout.h +++ b/include/linux/netfilter/nfnetlink_cttimeout.h @@ -6,6 +6,8 @@ enum ctnl_timeout_msg_types { IPCTNL_MSG_TIMEOUT_NEW, IPCTNL_MSG_TIMEOUT_GET, IPCTNL_MSG_TIMEOUT_DELETE, + IPCTNL_MSG_TIMEOUT_DEFAULT_SET, + IPCTNL_MSG_TIMEOUT_DEFAULT_GET, IPCTNL_MSG_TIMEOUT_MAX }; diff --git a/include/nfct.h b/include/nfct.h index 682fe3a..dc103c6 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -19,6 +19,8 @@ enum { NFCT_CMD_GET, NFCT_CMD_FLUSH, NFCT_CMD_DISABLE, + NFCT_CMD_DEFAULT_SET, + NFCT_CMD_DEFAULT_GET, }; #define __init __attribute__((constructor)) diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index 9f74eca..c9aa386 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -32,8 +32,8 @@ 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 timeout " + "[, ...]\n", VERSION, argv[0]); } static int nfct_cmd_timeout_list(struct mnl_socket *nl, int argc, char *argv[]); @@ -41,6 +41,8 @@ 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_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) @@ -61,6 +63,10 @@ nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) cmd = NFCT_CMD_GET; else if (strncmp(argv[2], "flush", strlen(argv[2])) == 0) cmd = NFCT_CMD_FLUSH; + else if (strncmp(argv[2], "default-set", strlen(argv[2])) == 0) + cmd = NFCT_CMD_DEFAULT_SET; + else if (strncmp(argv[2], "default-get", strlen(argv[2])) == 0) + cmd = NFCT_CMD_DEFAULT_GET; else { fprintf(stderr, "nfct v%s: Unknown command: %s\n", VERSION, argv[2]); @@ -83,6 +89,12 @@ nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) case NFCT_CMD_FLUSH: 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; } return ret; @@ -393,6 +405,96 @@ int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[]) return 0; } +static int +nfct_cmd_timeout_default_set(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; + + if (argc < 6) { + nfct_perror("missing parameters\n" + "syntax: nfct timeout default-set " + "family protocol state1 " + "timeout1 state2 timeout2..."); + 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_DEFAULT_SET, + 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 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 timeout default-get " + "family protocol"); + return -1; + } + + t = nfct_timeout_alloc(); + if (t == NULL) + return -1; + + argc-=3; + argv+=3; + + l3proto = nfct_cmd_get_l3proto(argv); + if (l3proto < 0) + return -1; + + 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_cmd_timeout_parse_params, -- cgit v1.2.3 From 20ddbf33277108da86c69bb78890fac09e055627 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 29 May 2014 14:33:29 +0200 Subject: udp: bind UDP sender side to same interface of the receiver side Otherwise, the kernel may select a different interface for the client side. Original patch from Michael Griego. While at it, remove some trailing whitespaces. Signed-off-by: Pablo Neira Ayuso --- src/udp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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; -- cgit v1.2.3 From c392c159605956c7bd4a264ab4490e2b2704c0cd Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 13 Jun 2014 12:53:17 +0200 Subject: conntrackd: build: fix crash when optional kernel modules are not loaded Fix a possible crash if conntrackd sees DCCP, SCTP and ICMPv6 traffic and the corresponding kernel modules that track this traffic are not available. Fixes: http://bugzilla.netfilter.org/show_bug.cgi?id=910 Signed-off-by: Pablo Neira Ayuso --- src/build.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/build.c b/src/build.c index 5799b51..9ba8b57 100644 --- a/src/build.c +++ b/src/build.c @@ -105,14 +105,14 @@ static enum nf_conntrack_attr nat_type[] = ATTR_ORIG_NAT_SEQ_OFFSET_AFTER, ATTR_REPL_NAT_SEQ_CORRECTION_POS, ATTR_REPL_NAT_SEQ_OFFSET_BEFORE, ATTR_REPL_NAT_SEQ_OFFSET_AFTER }; +/* ICMP, UDP and TCP are always loaded with nf_conntrack_ipv4 */ static void build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n) { - ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, - sizeof(struct nfct_attr_grp_port)); - if (!nfct_attr_is_set(ct, ATTR_TCP_STATE)) return; + ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, + sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_TCP_STATE, n, NTA_TCP_STATE); if (CONFIG(sync).tcp_window_tracking) { ct_build_u8(ct, ATTR_TCP_WSCALE_ORIG, n, NTA_TCP_WSCALE_ORIG); @@ -122,12 +122,12 @@ static void build_l4proto_tcp(const struct nf_conntrack *ct, struct nethdr *n) static void build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n) { - ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, - sizeof(struct nfct_attr_grp_port)); - + /* SCTP is optional, make sure nf_conntrack_sctp is loaded */ if (!nfct_attr_is_set(ct, ATTR_SCTP_STATE)) return; + ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, + sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_SCTP_STATE, n, NTA_SCTP_STATE); ct_build_u32(ct, ATTR_SCTP_VTAG_ORIG, n, NTA_SCTP_VTAG_ORIG); ct_build_u32(ct, ATTR_SCTP_VTAG_REPL, n, NTA_SCTP_VTAG_REPL); @@ -135,18 +135,22 @@ static void build_l4proto_sctp(const struct nf_conntrack *ct, struct nethdr *n) static void build_l4proto_dccp(const struct nf_conntrack *ct, struct nethdr *n) { - ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, - sizeof(struct nfct_attr_grp_port)); - + /* DCCP is optional, make sure nf_conntrack_dccp is loaded */ if (!nfct_attr_is_set(ct, ATTR_DCCP_STATE)) return; + ct_build_group(ct, ATTR_GRP_ORIG_PORT, n, NTA_PORT, + sizeof(struct nfct_attr_grp_port)); ct_build_u8(ct, ATTR_DCCP_STATE, n, NTA_DCCP_STATE); ct_build_u8(ct, ATTR_DCCP_ROLE, n, NTA_DCCP_ROLE); } static void build_l4proto_icmp(const struct nf_conntrack *ct, struct nethdr *n) { + /* This is also used by ICMPv6 and nf_conntrack_ipv6 is optional */ + if (!nfct_attr_is_set(ct, ATTR_ICMP_TYPE)) + return; + ct_build_u8(ct, ATTR_ICMP_TYPE, n, NTA_ICMP_TYPE); ct_build_u8(ct, ATTR_ICMP_CODE, n, NTA_ICMP_CODE); ct_build_u16(ct, ATTR_ICMP_ID, n, NTA_ICMP_ID); -- cgit v1.2.3 From e78ce22430f4b1e10daef21d480298d0fdb377d9 Mon Sep 17 00:00:00 2001 From: Arturo Borrero Date: Thu, 25 Sep 2014 11:45:11 +0200 Subject: man: fix hyphen used as minus sign Report from Debian's lintian: This manual page seems to contain a hyphen where a minus sign was intended. By default, "-" chars are interpreted as hyphens (U+2010) by groff, not as minus signs (U+002D). Since options to programs use minus signs (U+002D), this means for example in UTF-8 locales that you cannot cut and paste options, nor search for them easily. [...] Signed-off-by: Arturo Borrero Gonzalez Signed-off-by: Pablo Neira Ayuso --- conntrack.8 | 22 +++++++++++----------- conntrackd.8 | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/conntrack.8 b/conntrack.8 index 45e8582..abc26c5 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -1,4 +1,4 @@ -.TH CONNTRACK 8 "Jul 5, 2010" "" "" +.TH CONNTRACK 8 "Sep 25, 2014" "" "" .\" Man page written by Harald Welte (Dec 2007) @@ -32,7 +32,7 @@ Dump the external cache, i.e. show foreign states .TP .BI "-x " Display output in XML format. This option is only valid in combination -with "-i" and "-e" parameters. +with "\-i" and "\-e" parameters. .TP .BI "-f " "[|internal|external]" Flush the internal and/or external cache -- cgit v1.2.3 From 0ec644c3583b50e68ff23597d8d2c257bfcf7c71 Mon Sep 17 00:00:00 2001 From: Thomas Jarosch Date: Wed, 10 Dec 2014 22:56:35 +0100 Subject: channel: Fix file descriptor leak in channel_open() on error Detected by cppcheck Signed-off-by: Thomas Jarosch Signed-off-by: Pablo Neira Ayuso --- src/channel.c | 1 + 1 file changed, 1 insertion(+) 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); -- cgit v1.2.3 From 1c68250df4ca260392f532bf968fa28c40a7f974 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 21 Dec 2014 22:55:30 +0100 Subject: conntrack: fix doc/cli/test.sh create-expect when I run the test script of conntrack-tools sh doc/cli/test.sh create-expect the output is: conntrack v1.4.1 (conntrack-tools): You need to supply the `--reply-port-src' option for this command. Try `conntrack -h' or 'conntrack --help' for more information. This used to work without the --reply-port-src stuff using version 0.9.13 IIRC. Reported-by: Thomas Jarosch Signed-off-by: Pablo Neira Ayuso --- extensions/libct_proto_dccp.c | 2 +- extensions/libct_proto_sctp.c | 2 +- extensions/libct_proto_tcp.c | 2 +- extensions/libct_proto_udp.c | 2 +- extensions/libct_proto_udplite.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/libct_proto_dccp.c b/extensions/libct_proto_dccp.c index 586c4cc..f37ef68 100644 --- a/extensions/libct_proto_dccp.c +++ b/extensions/libct_proto_dccp.c @@ -78,7 +78,7 @@ static char dccp_commands_v_options[NUMBER_OF_CMD][DCCP_OPT_MAX] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,0,1,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0}, diff --git a/extensions/libct_proto_sctp.c b/extensions/libct_proto_sctp.c index f4c94df..97042c3 100644 --- a/extensions/libct_proto_sctp.c +++ b/extensions/libct_proto_sctp.c @@ -81,7 +81,7 @@ static char sctp_commands_v_options[NUMBER_OF_CMD][SCTP_OPT_MAX] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,0,1,1,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0}, diff --git a/extensions/libct_proto_tcp.c b/extensions/libct_proto_tcp.c index 0b43bf5..65acd5a 100644 --- a/extensions/libct_proto_tcp.c +++ b/extensions/libct_proto_tcp.c @@ -65,7 +65,7 @@ static char tcp_commands_v_options[NUMBER_OF_CMD][TCP_NUMBER_OF_OPT] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,0,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,0,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0}, diff --git a/extensions/libct_proto_udp.c b/extensions/libct_proto_udp.c index d7c4da1..768304b 100644 --- a/extensions/libct_proto_udp.c +++ b/extensions/libct_proto_udp.c @@ -73,7 +73,7 @@ static char udp_commands_v_options[NUMBER_OF_CMD][UDP_NUMBER_OF_OPT] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0}, diff --git a/extensions/libct_proto_udplite.c b/extensions/libct_proto_udplite.c index bffd5fe..9b67bef 100644 --- a/extensions/libct_proto_udplite.c +++ b/extensions/libct_proto_udplite.c @@ -81,7 +81,7 @@ static char udplite_commands_v_options[NUMBER_OF_CMD][UDP_OPT_MAX] = /*CT_VERSION*/ {0,0,0,0,0,0,0,0}, /*CT_HELP*/ {0,0,0,0,0,0,0,0}, /*EXP_LIST*/ {0,0,0,0,0,0,0,0}, -/*EXP_CREATE*/ {1,1,1,1,1,1,1,1}, +/*EXP_CREATE*/ {1,1,0,0,1,1,1,1}, /*EXP_DELETE*/ {1,1,1,1,0,0,0,0}, /*EXP_GET*/ {1,1,1,1,0,0,0,0}, /*EXP_FLUSH*/ {0,0,0,0,0,0,0,0}, -- cgit v1.2.3 From a3cd4dfb85b3ee9194ea82eb5185f9df2cb4ecf8 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Mon, 2 Feb 2015 12:35:12 -0800 Subject: conntrack: fix setting labels in updates When updating labels we always have to send the same sized bitmask as we received, as the bits we do omit will otherwise cleared as "padding". 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 either. Finally, kernel only retains old bit values that we send as zeroes in BOTH the label and the mask, due to XOR used in bit manipulation. This patch fixes all these issues and allows updates to set new labels without accidentally clearing old ones. Signed-off-by: Jarno Rajahalme Signed-off-by: Florian Westphal --- src/conntrack.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index 1e45ca8..f6d7d9a 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1422,17 +1422,31 @@ static void copy_label(struct nf_conntrack *tmp, const struct nf_conntrack *ct) if (options & CT_OPT_ADD_LABEL) { if (ctb == NULL) { - newmask = xnfct_bitmask_clone(tmpl.label_modify); - nfct_set_attr(tmp, ATTR_CONNLABELS, newmask); + 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)) + 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); + } } - - newmask = xnfct_bitmask_clone(tmpl.label_modify); nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask); } else if (ctb != NULL) { /* CT_OPT_DEL_LABEL */ -- cgit v1.2.3 From 175f4af13fa8f2ed05907d1e780ee1f609908daa Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 19 Feb 2015 00:54:42 +0100 Subject: conntrackd: allow strings with underscore from flex scanner Some people use interface names with underscores, so allow them from the flex scanner. Original patch from http://patchwork.ozlabs.org/patch/440600/ Signed-off-by: Pablo Neira Ayuso --- src/read_config_lex.l | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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] -- cgit v1.2.3 From eed61ed57fd2a82b81af9bd2f6895b3aa5221f49 Mon Sep 17 00:00:00 2001 From: Felix Janda Date: Sat, 16 May 2015 11:19:02 +0200 Subject: configure: Add AM_PROG_AR to silence automake warning /usr/share/automake-1.13/am/ltlibrary.am: warning: 'ct_helper_tns.la': linking libtool libraries using a non-POSIX /usr/share/automake-1.13/am/ltlibrary.am: archiver requires 'AM_PROG_AR' in 'configure.ac' src/helpers/Makefile.am:3: while processing Libtool library 'ct_helper_tns.la' Signed-off-by: Felix Janda Signed-off-by: Pablo Neira Ayuso --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index f0800d6..523a192 100644 --- a/configure.ac +++ b/configure.ac @@ -14,6 +14,7 @@ AC_SUBST([libdl_LIBS]) AC_PROG_CC AC_DISABLE_STATIC +AM_PROG_AR AM_PROG_LIBTOOL AC_PROG_INSTALL AC_PROG_LN_S -- cgit v1.2.3 From 3c1b1e54a46ad31e6ee7e5d87eed84bd29d8f460 Mon Sep 17 00:00:00 2001 From: Felix Janda Date: Sat, 16 May 2015 11:26:57 +0200 Subject: include: Sync with kernel headers Signed-off-by: Felix Janda Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/nfnetlink.h | 54 ++++++++----------------------- include/linux/netfilter/nfnetlink_queue.h | 5 ++- 2 files changed, 17 insertions(+), 42 deletions(-) diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index b64454c..c755646 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -18,6 +18,10 @@ enum nfnetlink_groups { #define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_DESTROY, #define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY + NFNLGRP_NFTABLES, +#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES + NFNLGRP_ACCT_QUOTA, +#define NFNLGRP_ACCT_QUOTA NFNLGRP_ACCT_QUOTA __NFNLGRP_MAX, }; #define NFNLGRP_MAX (__NFNLGRP_MAX - 1) @@ -49,46 +53,14 @@ struct nfgenmsg { #define NFNL_SUBSYS_OSF 5 #define NFNL_SUBSYS_IPSET 6 #define NFNL_SUBSYS_ACCT 7 -#define NFNL_SUBSYS_COUNT 8 +#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 +#define NFNL_SUBSYS_CTHELPER 9 +#define NFNL_SUBSYS_NFTABLES 10 +#define NFNL_SUBSYS_NFT_COMPAT 11 +#define NFNL_SUBSYS_COUNT 12 -#ifdef __KERNEL__ +/* Reserved control nfnetlink messages */ +#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE +#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1 -#include -#include -#include - -struct nfnl_callback { - int (*call)(struct sock *nl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const cda[]); - int (*call_rcu)(struct sock *nl, struct sk_buff *skb, - const struct nlmsghdr *nlh, - const struct nlattr * const cda[]); - const struct nla_policy *policy; /* netlink attribute policy */ - const u_int16_t attr_count; /* number of nlattr's */ -}; - -struct nfnetlink_subsystem { - const char *name; - __u8 subsys_id; /* nfnetlink subsystem ID */ - __u8 cb_count; /* number of callbacks */ - const struct nfnl_callback *cb; /* callback for individual types */ -}; - -extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); -extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); - -extern int nfnetlink_has_listeners(struct net *net, unsigned int group); -extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigned group, - int echo, gfp_t flags); -extern int nfnetlink_set_err(struct net *net, u32 pid, u32 group, int error); -extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u_int32_t pid, int flags); - -extern void nfnl_lock(void); -extern void nfnl_unlock(void); - -#define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ - MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) - -#endif /* __KERNEL__ */ -#endif /* _NFNETLINK_H */ +#endif /* _NFNETLINK_H */ diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h index 0132bad..8dd819e 100644 --- a/include/linux/netfilter/nfnetlink_queue.h +++ b/include/linux/netfilter/nfnetlink_queue.h @@ -47,6 +47,8 @@ enum nfqnl_attr_type { NFQA_CAP_LEN, /* __u32 length of captured packet */ NFQA_SKB_INFO, /* __u32 skb meta information */ NFQA_EXP, /* nf_conntrack_netlink.h */ + NFQA_UID, /* __u32 sk uid */ + NFQA_GID, /* __u32 sk gid */ __NFQA_MAX }; @@ -99,7 +101,8 @@ enum nfqnl_attr_config { #define NFQA_CFG_F_FAIL_OPEN (1 << 0) #define NFQA_CFG_F_CONNTRACK (1 << 1) #define NFQA_CFG_F_GSO (1 << 2) -#define NFQA_CFG_F_MAX (1 << 3) +#define NFQA_CFG_F_UID_GID (1 << 3) +#define NFQA_CFG_F_MAX (1 << 4) /* flags for NFQA_SKB_INFO */ /* packet appears to have wrong checksums, but they are ok */ -- cgit v1.2.3 From 1c637fe7ea8a70a77273366d24e221b0d3d64702 Mon Sep 17 00:00:00 2001 From: Felix Janda Date: Sat, 16 May 2015 11:38:53 +0200 Subject: src: Use stdint types Signed-off-by: Felix Janda Signed-off-by: Pablo Neira Ayuso --- extensions/libct_proto_dccp.c | 2 +- extensions/libct_proto_gre.c | 2 +- extensions/libct_proto_icmp.c | 4 ++-- extensions/libct_proto_icmpv6.c | 4 ++-- extensions/libct_proto_sctp.c | 4 ++-- extensions/libct_proto_tcp.c | 2 +- extensions/libct_proto_udp.c | 2 +- extensions/libct_proto_udplite.c | 2 +- include/bitops.h | 14 +++++++------- include/helper.h | 2 +- src/filter.c | 4 ++-- src/helpers/amanda.c | 4 ++-- src/helpers/ftp.c | 2 +- src/helpers/ssdp.c | 2 +- src/internal_bypass.c | 4 ++-- src/sync-notrack.c | 2 +- 16 files changed, 28 insertions(+), 28 deletions(-) diff --git a/extensions/libct_proto_dccp.c b/extensions/libct_proto_dccp.c index f37ef68..f6258ad 100644 --- a/extensions/libct_proto_dccp.c +++ b/extensions/libct_proto_dccp.c @@ -118,7 +118,7 @@ static int parse_options(char c, unsigned int *flags) { int i; - u_int16_t port; + uint16_t port; switch(c) { case 1: diff --git a/extensions/libct_proto_gre.c b/extensions/libct_proto_gre.c index 0274a37..2dc63d1 100644 --- a/extensions/libct_proto_gre.c +++ b/extensions/libct_proto_gre.c @@ -91,7 +91,7 @@ static int parse_options(char c, unsigned int *flags) { switch(c) { - u_int16_t port; + uint16_t port; case '1': port = htons(strtoul(optarg, NULL, 0)); nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, port); diff --git a/extensions/libct_proto_icmp.c b/extensions/libct_proto_icmp.c index d04397f..2ce1c65 100644 --- a/extensions/libct_proto_icmp.c +++ b/extensions/libct_proto_icmp.c @@ -72,8 +72,8 @@ static int parse(char c, unsigned int *flags) { switch(c) { - u_int8_t tmp; - u_int16_t id; + uint8_t tmp; + uint16_t id; case '1': tmp = atoi(optarg); nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, tmp); diff --git a/extensions/libct_proto_icmpv6.c b/extensions/libct_proto_icmpv6.c index f8c2c68..18dd3e5 100644 --- a/extensions/libct_proto_icmpv6.c +++ b/extensions/libct_proto_icmpv6.c @@ -75,8 +75,8 @@ static int parse(char c, unsigned int *flags) { switch(c) { - u_int8_t tmp; - u_int16_t id; + uint8_t tmp; + uint16_t id; case '1': tmp = atoi(optarg); nfct_set_attr_u8(ct, ATTR_ICMP_TYPE, tmp); diff --git a/extensions/libct_proto_sctp.c b/extensions/libct_proto_sctp.c index 97042c3..04828bf 100644 --- a/extensions/libct_proto_sctp.c +++ b/extensions/libct_proto_sctp.c @@ -120,8 +120,8 @@ parse_options(char c, struct nf_conntrack *ct, unsigned int *flags) { int i; - u_int16_t port; - u_int32_t vtag; + uint16_t port; + uint32_t vtag; switch(c) { case 1: diff --git a/extensions/libct_proto_tcp.c b/extensions/libct_proto_tcp.c index 65acd5a..8a37a55 100644 --- a/extensions/libct_proto_tcp.c +++ b/extensions/libct_proto_tcp.c @@ -106,7 +106,7 @@ static int parse_options(char c, unsigned int *flags) { int i; - u_int16_t port; + uint16_t port; switch(c) { case '1': diff --git a/extensions/libct_proto_udp.c b/extensions/libct_proto_udp.c index 768304b..e30637c 100644 --- a/extensions/libct_proto_udp.c +++ b/extensions/libct_proto_udp.c @@ -87,7 +87,7 @@ static int parse_options(char c, unsigned int *flags) { switch(c) { - u_int16_t port; + uint16_t port; case '1': port = htons(atoi(optarg)); nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, port); diff --git a/extensions/libct_proto_udplite.c b/extensions/libct_proto_udplite.c index 9b67bef..f46cef0 100644 --- a/extensions/libct_proto_udplite.c +++ b/extensions/libct_proto_udplite.c @@ -95,7 +95,7 @@ static int parse_options(char c, unsigned int *flags) { switch(c) { - u_int16_t port; + uint16_t port; case '1': port = htons(atoi(optarg)); nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, port); diff --git a/include/bitops.h b/include/bitops.h index 51f4289..27fe58d 100644 --- a/include/bitops.h +++ b/include/bitops.h @@ -1,34 +1,34 @@ #ifndef _BITOPS_H_ #define _BITOPS_H_ -#include +#include -static inline void set_bit_u32(int nr, u_int32_t *addr) +static inline void set_bit_u32(int nr, uint32_t *addr) { addr[nr >> 5] |= (1UL << (nr & 31)); } -static inline void unset_bit_u32(int nr, u_int32_t *addr) +static inline void unset_bit_u32(int nr, uint32_t *addr) { addr[nr >> 5] &= ~(1UL << (nr & 31)); } -static inline int test_bit_u32(int nr, const u_int32_t *addr) +static inline int test_bit_u32(int nr, const uint32_t *addr) { return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0; } -static inline void set_bit_u16(int nr, u_int16_t *addr) +static inline void set_bit_u16(int nr, uint16_t *addr) { addr[nr >> 4] |= (1UL << (nr & 15)); } -static inline void unset_bit_u16(int nr, u_int16_t *addr) +static inline void unset_bit_u16(int nr, uint16_t *addr) { addr[nr >> 4] &= ~(1UL << (nr & 15)); } -static inline int test_bit_u16(int nr, const u_int16_t *addr) +static inline int test_bit_u16(int nr, const uint16_t *addr) { return ((1UL << (nr & 15)) & (addr[nr >> 4])) != 0; } diff --git a/include/helper.h b/include/helper.h index bd69af6..f412e55 100644 --- a/include/helper.h +++ b/include/helper.h @@ -25,7 +25,7 @@ struct ctd_helper { int (*cb)(struct pkt_buff *pkt, uint32_t protoff, struct myct *ct, - u_int32_t ctinfo); + uint32_t ctinfo); struct ctd_helper_policy policy[CTD_HELPER_POLICY_MAX]; 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/amanda.c b/src/helpers/amanda.c index c0cf701..9e6c4e7 100644 --- a/src/helpers/amanda.c +++ b/src/helpers/amanda.c @@ -34,7 +34,7 @@ static int nat_amanda(struct pkt_buff *pkt, uint32_t ctinfo, struct nf_expect *exp) { char buffer[sizeof("65535")]; - u_int16_t port, initial_port; + uint16_t port, initial_port; unsigned int ret; const struct nf_conntrack *expected; struct nf_conntrack *nat_tuple; @@ -111,7 +111,7 @@ amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff, struct nf_expect *exp; char *data, *data_limit, *tmp; unsigned int dataoff, i; - u_int16_t port, len; + uint16_t port, len; int ret = NF_ACCEPT; struct iphdr *iph; union nfct_attr_grp_addr saddr, daddr; diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c index 2c8dcd6..e7fe7f7 100644 --- a/src/helpers/ftp.c +++ b/src/helpers/ftp.c @@ -58,7 +58,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/ssdp.c b/src/helpers/ssdp.c index 2713f23..bc41087 100644 --- a/src/helpers/ssdp.c +++ b/src/helpers/ssdp.c @@ -44,7 +44,7 @@ static int ssdp_helper_cb(struct pkt_buff *pkt, uint32_t protoff, struct iphdr *net_hdr = (struct iphdr *)pktb_network_header(pkt); int good_packet = 0; struct nf_expect *exp; - u_int16_t port; + uint16_t port; unsigned int dataoff; void *sb_ptr; 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/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); -- cgit v1.2.3 From 12a5c50a831176b7f95a3535fe42ecbcf332afb1 Mon Sep 17 00:00:00 2001 From: Felix Janda Date: Sat, 16 May 2015 11:50:25 +0200 Subject: src: Include for fd_set Signed-off-by: Felix Janda Signed-off-by: Pablo Neira Ayuso --- include/mcast.h | 1 + include/tcp.h | 1 + include/udp.h | 1 + 3 files changed, 3 insertions(+) diff --git a/include/mcast.h b/include/mcast.h index 402a033..f0225aa 100644 --- a/include/mcast.h +++ b/include/mcast.h @@ -4,6 +4,7 @@ #include #include #include +#include struct mcast_conf { int ipproto; diff --git a/include/tcp.h b/include/tcp.h index 2f0fd0a..068d43a 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -3,6 +3,7 @@ #include #include +#include struct tcp_conf { int ipproto; diff --git a/include/udp.h b/include/udp.h index 9f9c17a..53d713d 100644 --- a/include/udp.h +++ b/include/udp.h @@ -3,6 +3,7 @@ #include #include +#include struct udp_conf { int ipproto; -- cgit v1.2.3 From fe1f2d58add1e56e651c43de8cd60db8123d49bb Mon Sep 17 00:00:00 2001 From: Felix Janda Date: Sat, 16 May 2015 12:05:33 +0200 Subject: src: Define _GNU_SOURCE to get members of tcphdr&ucphdr The source uses linux names for members of tcphdr. For example "source" instead of "th_sport", ... musl libc's headers need _GNU_SOURCE defined in order to expose these. Signed-off-by: Felix Janda Signed-off-by: Pablo Neira Ayuso --- src/cthelper.c | 1 + src/helpers/ftp.c | 1 + src/helpers/sane.c | 1 + src/helpers/tns.c | 1 + 4 files changed, 4 insertions(+) diff --git a/src/cthelper.c b/src/cthelper.c index fec40fb..bd8b8b7 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -31,6 +31,7 @@ #include #include #include +#define _GNU_SOURCE #include #include #include diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c index e7fe7f7..24ee877 100644 --- a/src/helpers/ftp.c +++ b/src/helpers/ftp.c @@ -25,6 +25,7 @@ #include /* for isdigit */ #include +#define _GNU_SOURCE #include #include diff --git a/src/helpers/sane.c b/src/helpers/sane.c index 79ca948..c30f4ba 100644 --- a/src/helpers/sane.c +++ b/src/helpers/sane.c @@ -30,6 +30,7 @@ #include "log.h" #include #include +#define _GNU_SOURCE #include #include #include 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 /* for isdigit */ #include +#define _GNU_SOURCE #include #include -- cgit v1.2.3 From 28171b61622390f241101cbc4573c443ef9935aa Mon Sep 17 00:00:00 2001 From: Felix Janda Date: Sat, 16 May 2015 12:20:25 +0200 Subject: netlink: Use instead of legacy synonym Signed-off-by: Felix Janda Signed-off-by: Pablo Neira Ayuso --- src/netlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 #include #include -#include +#include #include struct nfct_handle *nl_init_event_handler(void) -- cgit v1.2.3 From 796f592eec3b83b61397f726a4e652a005cae3c2 Mon Sep 17 00:00:00 2001 From: Chas Williams III Date: Wed, 20 May 2015 07:50:50 -0600 Subject: cthelper: don't pass up a 0 length queue If the user didn't specify a queue length in the configuration file it will have a length of 0. Allow the kernel's default to take precedence instead. Signed-off-by: Charles (Chas) Williams Signed-off-by: Pablo Neira Ayuso --- src/cthelper.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cthelper.c b/src/cthelper.c index bd8b8b7..15d5126 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -470,7 +470,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"); -- cgit v1.2.3 From 9d53701f4b1d5db5773513d5f370ca4a89371c9a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 29 May 2015 14:51:22 +0200 Subject: expect: Fix wrong memset usage memset fills bytes, not ulongs - so the second parameter (the fill value) has to be a byte. Reported-by: Paul Aitken Signed-off-by: Pablo Neira Ayuso --- src/expect.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/expect.c b/src/expect.c index bba0ed7..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: -- cgit v1.2.3 From 6dcfe88a27318e313c1cb9130177a839926fe042 Mon Sep 17 00:00:00 2001 From: Paul Aitken Date: Thu, 4 Jun 2015 10:15:00 +0100 Subject: conntrackd: remove unused 'numbytes' 'numbytes' isn't used and can be removed. Signed-off-by: Paul Aitken Signed-off-by: Pablo Neira Ayuso --- src/local.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/local.c b/src/local.c index feff608..453799a 100644 --- a/src/local.c +++ b/src/local.c @@ -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); -- cgit v1.2.3 From 0d48c76df2736fc3ab9b17dd97fa456cf98ee9e6 Mon Sep 17 00:00:00 2001 From: Paul Aitken Date: Thu, 4 Jun 2015 10:15:00 +0100 Subject: cthelper: Optimise nfq_queue_cb ct and myct have both already been checked for non-NULL, so there's no need to check either of them again later. Signed-off-by: Paul Aitken Signed-off-by: Pablo Neira Ayuso --- src/cthelper.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cthelper.c b/src/cthelper.c index 15d5126..6537515 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -325,14 +325,12 @@ static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data) if (pkt_verdict_issue(helper, myct, queue_num, id, verdict, pktb) < 0) goto err_pktb; - if (ct != NULL) - nfct_destroy(ct); + nfct_destroy(ct); if (myct->exp != NULL) nfexp_destroy(myct->exp); - if (myct && myct->priv_data != NULL) + if (myct->priv_data != NULL) free(myct->priv_data); - if (myct != NULL) - free(myct); + free(myct); return MNL_CB_OK; err_pktb: -- cgit v1.2.3 From aa9fd4a1a4cf2e78521c427554f3339f78f7a82b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 26 Jun 2015 10:03:25 +0200 Subject: tests: conntrack: don't overwrite read-only shell variable Signed-off-by: Pablo Neira Ayuso --- tests/conntrack/run-test.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/conntrack/run-test.sh b/tests/conntrack/run-test.sh index 2b7b6f2..1403e2c 100644 --- a/tests/conntrack/run-test.sh +++ b/tests/conntrack/run-test.sh @@ -1,6 +1,5 @@ #!/bin/bash -UID=`id -u` if [ $UID -ne 0 ] then echo "Run this test as root" -- cgit v1.2.3 From d5fdfac4873061620546c2328c55f9c5830fd0f8 Mon Sep 17 00:00:00 2001 From: Szilárd Pfeiffer Date: Thu, 25 Jun 2015 12:22:10 +0200 Subject: conntrack: refactor handling of address options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Szilárd Pfeiffer Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 69 ++++++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index f6d7d9a..1bf5b2b 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -437,6 +437,10 @@ 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, @@ -1946,6 +1950,31 @@ static void merge_bitmasks(struct nfct_bitmask **current, 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; @@ -1953,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; @@ -2024,47 +2053,13 @@ 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 '}': 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; -- cgit v1.2.3 From 3309fdb4413cb32f9b95e05064dc9dbb56550939 Mon Sep 17 00:00:00 2001 From: Szilárd Pfeiffer Date: Thu, 25 Jun 2015 12:22:11 +0200 Subject: conntrack: fix expectation entry creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Szilárd Pfeiffer Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/conntrack.c b/src/conntrack.c index 1bf5b2b..67fcda0 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -2057,6 +2057,8 @@ int main(int argc, char *argv[]) break; case '{': case '}': + nfct_set_addr_from_opt(c, tmpl.exptuple, &ad, &family); + break; case '[': case ']': nfct_set_addr_from_opt(c, tmpl.mask, &ad, &family); -- cgit v1.2.3 From 900d7e80b8d8339622912c88f6faea96af4115d7 Mon Sep 17 00:00:00 2001 From: Szilárd Pfeiffer Date: Fri, 3 Jul 2015 01:04:58 +0200 Subject: conntrack: made the protocol option value case insensitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extensions register protocols by lowercase protocol name, but value of proto command line option may be uppercase. Extension related options cannot be used when protocol name comparision fails. Signed-off-by: Szilárd Pfeiffer Signed-off-by: Florian Westphal --- src/conntrack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conntrack.c b/src/conntrack.c index 67fcda0..00b09b6 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -551,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; } -- cgit v1.2.3 From 38a46caa55ffe1ffee662503ac8abb57522baaa3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Aug 2015 18:51:50 +0200 Subject: conntrackd: fix sanitization of expection attribute in the wire format The maximum number of attribute is NTA_EXP_MAX for expectation sync messages. Signed-off-by: Pablo Neira Ayuso --- src/parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse.c b/src/parse.c index f3ec6ac..878e354 100644 --- a/src/parse.c +++ b/src/parse.c @@ -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; -- cgit v1.2.3 From cf2c6ea6cf1dd2753c431374284e148aba55f947 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Aug 2015 18:53:49 +0200 Subject: conntrackd: NTA_MAX is also an invalid attribute Otherwise this can result in an off-by-one array access. Signed-off-by: Pablo Neira Ayuso --- src/parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parse.c b/src/parse.c index 878e354..3ac4092 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) -- cgit v1.2.3 From be691dc236610ab349c3bffb9a891613f75c6ebe Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Aug 2015 18:56:51 +0200 Subject: conntrackd: fix leak in fork_process_new() Release the child_process structure in case that fork() fails. Signed-off-by: Pablo Neira Ayuso --- src/process.c | 2 ++ 1 file changed, 2 insertions(+) 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; } -- cgit v1.2.3 From 99dc0ba1e12c40a1c69c6f831a78a06248b3e2a4 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Aug 2015 18:59:18 +0200 Subject: conntrackd: fix descriptor leak in do_local_request() Signed-off-by: Pablo Neira Ayuso --- src/local.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/local.c b/src/local.c index 453799a..85e5180 100644 --- a/src/local.c +++ b/src/local.c @@ -147,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; } -- cgit v1.2.3 From 097bb594e6844fe3edc1b01768a8ced37433378b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Aug 2015 19:05:23 +0200 Subject: conntrackd: fix error handling in nfq_queue_cb() Make sure we have a clean exit on error, everything needs to be properly released. Signed-off-by: Pablo Neira Ayuso --- src/cthelper.c | 29 +++++++++++++++-------------- src/local.c | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/cthelper.c b/src/cthelper.c index 6537515..54eb830 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -277,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]); @@ -292,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])); @@ -315,15 +315,15 @@ 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; nfct_destroy(ct); if (myct->exp != NULL) @@ -333,18 +333,19 @@ static int nfq_queue_cb(const struct nlmsghdr *nlh, void *data) 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; } diff --git a/src/local.c b/src/local.c index 85e5180..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; -- cgit v1.2.3 From 743e4948eb3bdbdb3a7751c54f2c715ba829afd2 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Aug 2015 19:08:37 +0200 Subject: conntrackd: simplify branch in tcp_accept() The same code is executed regardless the reason why accept() has failed. Signed-off-by: Pablo Neira Ayuso --- src/tcp.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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) { -- cgit v1.2.3 From 09d14955e436b144bc69b998c172b3ea47683195 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Aug 2015 19:11:42 +0200 Subject: conntrackd: use strncpy to set up the cache name This is not exposed, but use the strncpy() variant to calm down static code validators. Signed-off-by: Pablo Neira Ayuso --- src/cache.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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++) { -- cgit v1.2.3 From 4134f1dafcc981757c40177bb3c5a3a7a144ff30 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Aug 2015 19:16:26 +0200 Subject: conntrackd: missing break in expectation message parser function Fortunately, the TLVs come in order in the message, however, if the order is changed we'll incorrectly set up the expectation. Signed-off-by: Pablo Neira Ayuso --- src/parse.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/parse.c b/src/parse.c index 3ac4092..919d36c 100644 --- a/src/parse.c +++ b/src/parse.c @@ -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)); -- cgit v1.2.3 From 16363ecdc6d0ecfb13702bd1b2b176c96e78b1bb Mon Sep 17 00:00:00 2001 From: Arturo Borrero Gonzalez Date: Thu, 20 Aug 2015 12:45:20 +0200 Subject: list: fix prefetch dummy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [...] CC conntrack.o In file included from ../include/conntrack.h:4:0, from conntrack.c:41: conntrack.c: In function ‘findproto’: ../include/linux_list.h:385:59: warning: right-hand operand of comma expression has no effect [-Wunused-value] for (pos = list_entry((head)->next, typeof(*pos), member), \ ^ [...] The original patch is from Patrick McHardy . Signed-off-by: Arturo Borrero Gonzalez Signed-off-by: Pablo Neira Ayuso --- include/linux_list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux_list.h b/include/linux_list.h index de182a4..efffb91 100644 --- a/include/linux_list.h +++ b/include/linux_list.h @@ -29,7 +29,7 @@ 1; \ }) -#define prefetch(x) 1 +#define prefetch(x) ((void)0) /* empty define to make this work in userspace -HW */ #ifndef smp_wmb -- cgit v1.2.3 From a8f74d021676096eaa40af72e6d91787408fe44d Mon Sep 17 00:00:00 2001 From: Arturo Borrero Gonzalez Date: Thu, 20 Aug 2015 13:56:42 +0200 Subject: doc/debian.conntrackd.init.d: drop file This file is likely dead code. It's outdated. Also I think distributors should manage themselves to integrate daemons in their operating systems. Following this idea, this file doesn't belong here. Signed-off-by: Arturo Borrero Gonzalez Signed-off-by: Pablo Neira Ayuso --- doc/debian.conntrackd.init.d | 48 -------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 doc/debian.conntrackd.init.d diff --git a/doc/debian.conntrackd.init.d b/doc/debian.conntrackd.init.d deleted file mode 100644 index ba847dd..0000000 --- a/doc/debian.conntrackd.init.d +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/sh -# -# /etc/init.d/conntrackd -# -# Maximilian Wilhelm -# -- Mon, 06 Nov 2006 18:39:07 +0100 -# - -export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin - -NAME="conntrackd" -DAEMON=`command -v conntrackd` -CONFIG="/etc/conntrack/conntrackd.conf" -PIDFILE="/var/run/${NAME}.pid" - - -# Gracefully exit if there is no daemon (debian way of life) -if [ ! -x "${DAEMON}" ]; then - exit 0 -fi - -# Check for config file -if [ ! -f /etc/conntrackd/conntrackd.conf ]; then - echo "Error: There is no config file for $NAME" >&2 - exit 1; -fi - -case "$1" in - start) - echo -n "Starting $NAME: " - start-stop-daemon --start --quiet --make-pidfile --pidfile "/var/run/${NAME}.pid" --background --exec "${DAEMON}" && echo "done." || echo "FAILED!" - ;; - stop) - echo -n "Stopping $NAME:" - start-stop-daemon --stop --quiet --oknodo --pidfile "/var/run/${NAME}.pid" && echo "done." || echo "FAILED!" - ;; - - restart) - $0 start - $0 stop - ;; - - *) - echo "Usage: /etc/init.d/conntrackd {start|stop|restart}" - exit 1 -esac - -exit 0 -- cgit v1.2.3 From 6ea080984022c6ece3e465d81b7b0b0f9709d356 Mon Sep 17 00:00:00 2001 From: Arturo Borrero Date: Thu, 20 Aug 2015 13:38:42 +0200 Subject: nfct: don't link against libnetfilter_conntrack The nfct program uses none of the symbols of libnetfilter_conntrack. Linking against it means that distributors have to maintain an useless depedency. This was spotted by the dpkg-shlibdeps tool. Signed-off-by: Arturo Borrero Gonzalez Signed-off-by: Pablo Neira Ayuso --- src/Makefile.am | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index 1bc3622..a1d00f8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,7 +25,6 @@ nfct_SOURCES += nfct-extensions/timeout.c endif nfct_LDADD = ${LIBMNL_LIBS} \ - ${LIBNETFILTER_CONNTRACK_LIBS} \ ${libdl_LIBS} if HAVE_CTTIMEOUT -- cgit v1.2.3 From 1c36d487cda8d1bed799b4daa28c44aa7198bb31 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 21 Aug 2015 20:05:05 +0200 Subject: tests: fix run-test.sh This reports: run-test.sh: line 3: UID: read-only variable rename it to _UID. Signed-off-by: Pablo Neira Ayuso --- tests/nfct/run-test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/nfct/run-test.sh b/tests/nfct/run-test.sh index 9bcad0d..851ee75 100644 --- a/tests/nfct/run-test.sh +++ b/tests/nfct/run-test.sh @@ -1,7 +1,7 @@ #!/bin/bash -UID=`id -u` -if [ $UID -ne 0 ] +_UID=`id -u` +if [ $_UID -ne 0 ] then echo "Run this test as root" exit 1 -- cgit v1.2.3 From dd73ceecdbe87b6ecf9e96643cd5326e520d7a1c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 21 Aug 2015 19:18:38 +0200 Subject: nfct: Update syntax to specify command before subsystem This patch gets the nfct syntax in sync with nft so it looks like this: nfct object ... instead of: nfct object ... This patch retains backward compatibility so you can still use the old syntax. The manpage and tests have been also updated to promote the adoption of this syntax. We should have little existing clients of this tool as we can only use this to configure the cttimeout and cthelper infrastructures. Signed-off-by: Pablo Neira Ayuso --- include/nfct.h | 4 +- nfct.8 | 36 +++++++++------- src/nfct-extensions/helper.c | 46 ++++++++++---------- src/nfct-extensions/timeout.c | 53 ++++++++++------------- src/nfct.c | 97 +++++++++++++++++++++++++++++++++++-------- tests/nfct/test-live.sh | 14 +++---- tests/nfct/timeout/00tcp | 16 +++---- tests/nfct/timeout/01udp | 16 +++---- tests/nfct/timeout/02generic | 16 +++---- tests/nfct/timeout/03udplite | 16 +++---- tests/nfct/timeout/04icmp | 16 +++---- tests/nfct/timeout/05icmpv6 | 16 +++---- tests/nfct/timeout/06sctp | 16 +++---- tests/nfct/timeout/07dccp | 16 +++---- tests/nfct/timeout/08gre | 16 +++---- 15 files changed, 225 insertions(+), 169 deletions(-) diff --git a/include/nfct.h b/include/nfct.h index dc103c6..bfffdd6 100644 --- a/include/nfct.h +++ b/include/nfct.h @@ -9,6 +9,7 @@ enum { NFCT_SUBSYS_HELPER, NFCT_SUBSYS_VERSION, NFCT_SUBSYS_HELP, + NFCT_SUBSYS_MAX }; enum { @@ -21,6 +22,7 @@ enum { NFCT_CMD_DISABLE, NFCT_CMD_DEFAULT_SET, NFCT_CMD_DEFAULT_GET, + NFCT_CMD_MAX, }; #define __init __attribute__((constructor)) @@ -30,7 +32,7 @@ void nfct_perror(const char *msg); struct nfct_extension { struct list_head head; int type; - int (*parse_params)(struct mnl_socket *nl, int argc, char *argv[]); + int (*parse_params)(struct mnl_socket *nl, int argc, char *argv[], int cmd); }; void nfct_extension_register(struct nfct_extension *ext); diff --git a/nfct.8 b/nfct.8 index 6f5190a..863fe12 100644 --- a/nfct.8 +++ b/nfct.8 @@ -3,12 +3,26 @@ .\" Man page written by Pablo Neira Ayuso (Feb 2012) .SH NAME -nfct \- command line tool to interact with the connection tracking system +nfct \- command line tool to configure with the connection tracking system .SH SYNOPSIS -.BR "nfct subsystem command [parameters]" +.BR "nfct command subsystem [parameters]" .SH DESCRIPTION .B nfct -is the command line tool that allows you Netfilter's manipulate Connection Tracking System. +is the command line tool that allows to configure the Connection Tracking +System. +.SH COMMANDS +.TP +.BI "list " +List the existing objects. +.TP +.BI "add " +Add new object. +.TP +.BI "delete " +Delete an object. +.TP +.BI "get " +Get an existing object. .SH SUBSYS By the time this manpage has been written, the supported subsystem are .B timeout @@ -16,24 +30,14 @@ By the time this manpage has been written, the supported subsystem are .BI "timeout " The timeout subsystem allows you to define fine-grain timeout policies. .TP +.BI "helper " +The helper subsystem allows you to configure userspace helpers. +.TP .BI "version " Displays the version information. .TP .BI "help " Displays the help message. -.SH TIMEOUT SUBSYSTEM -.TP -.BI "list " -List the existing timeout policies. -.TP -.BI "add " -Add new timeout policy. -.TP -.BI "delete " -Delete timeout policy. -.TP -.BI "get " -Get existing timeout policy. .SH EXAMPLE .TP .B nfct timeout add test-tcp inet tcp established 100 close 10 close_wait 10 diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c index bfb153f..dfc55e7 100644 --- a/src/nfct-extensions/helper.c +++ b/src/nfct-extensions/helper.c @@ -45,36 +45,31 @@ 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_cmd_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[]) +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(nl, argc, argv); break; @@ -93,6 +88,9 @@ nfct_cmd_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[]) case NFCT_CMD_DISABLE: ret = nfct_cmd_helper_disable(nl, argc, argv); break; + default: + nfct_cmd_helper_usage(argv); + return -1; } return ret; @@ -160,8 +158,7 @@ static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 6) { nfct_perror("missing parameters\n" - "syntax: nfct helper add name " - "family protocol"); + "syntax: nfct add helper name family protocol"); return -1; } @@ -411,8 +408,7 @@ nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 6) { nfct_perror("missing parameters\n" - "syntax: nfct helper add name " - "family protocol"); + "syntax: nfct add helper name family protocol"); return -1; } @@ -469,7 +465,7 @@ nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]) static struct nfct_extension helper = { .type = NFCT_SUBSYS_HELPER, - .parse_params = nfct_cmd_helper_parse_params, + .parse_params = nfct_helper_parse_params, }; static void __init helper_init(void) diff --git a/src/nfct-extensions/timeout.c b/src/nfct-extensions/timeout.c index c9aa386..1cb04a1 100644 --- a/src/nfct-extensions/timeout.c +++ b/src/nfct-extensions/timeout.c @@ -32,7 +32,7 @@ static void nfct_cmd_timeout_usage(char *argv[]) { fprintf(stderr, "nfct v%s: Missing command\n" - "%s timeout " + "%s timeout " "[, ...]\n", VERSION, argv[0]); } @@ -45,35 +45,30 @@ static int nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *a static int nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]); static int -nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) +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 if (strncmp(argv[2], "default-set", strlen(argv[2])) == 0) - cmd = NFCT_CMD_DEFAULT_SET; - else if (strncmp(argv[2], "default-get", strlen(argv[2])) == 0) - cmd = NFCT_CMD_DEFAULT_GET; - 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(nl, argc, argv); break; @@ -95,6 +90,9 @@ nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) 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; @@ -270,9 +268,7 @@ int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 6) { nfct_perror("missing parameters\n" - "syntax: nfct timeout add name " - "family protocol state1 " - "timeout1 state2 timeout2..."); + "syntax: nfct add timeout name family protocol state1 timeout1 ..."); return -1; } @@ -415,9 +411,7 @@ nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 6) { nfct_perror("missing parameters\n" - "syntax: nfct timeout default-set " - "family protocol state1 " - "timeout1 state2 timeout2..."); + "syntax: nfct default-set timeout family protocol state1 timeout1..."); return -1; } @@ -454,8 +448,7 @@ nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]) if (argc < 5) { nfct_perror("missing parameters\n" - "syntax: nfct timeout default-get " - "family protocol"); + "syntax: nfct default-get timeout family protocol"); return -1; } @@ -497,7 +490,7 @@ nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]) static struct nfct_extension timeout = { .type = NFCT_SUBSYS_TIMEOUT, - .parse_params = nfct_cmd_timeout_parse_params, + .parse_params = nfct_timeout_parse_params, }; static void __init timeout_init(void) diff --git a/src/nfct.c b/src/nfct.c index 533d75d..3331e5b 100644 --- a/src/nfct.c +++ b/src/nfct.c @@ -31,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]); } @@ -63,32 +63,93 @@ static struct nfct_extension *nfct_extension_lookup(int type) 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) { + switch (subsys) { case NFCT_SUBSYS_VERSION: ret = nfct_cmd_version(argc, argv); break; @@ -109,7 +170,7 @@ int main(int argc, char *argv[]) return -1; } - ret = ext->parse_params(nl, argc, argv); + ret = ext->parse_params(nl, argc, argv, cmd); mnl_socket_close(nl); break; } diff --git a/tests/nfct/test-live.sh b/tests/nfct/test-live.sh index c338e63..2257087 100644 --- a/tests/nfct/test-live.sh +++ b/tests/nfct/test-live.sh @@ -6,7 +6,7 @@ WAIT_BETWEEN_TESTS=10 # flush cttimeout table -nfct timeout flush +nfct flush timeout # flush the conntrack table conntrack -F @@ -19,7 +19,7 @@ echo "---- test no. 1 ----" conntrack -E -p 13 & -nfct timeout add test-generic inet generic timeout 100 +nfct add timeout test-generic inet generic timeout 100 iptables -I OUTPUT -t raw -p all -j CT --timeout test-generic hping3 -c 1 -V -I eth0 -0 8.8.8.8 -H 13 @@ -30,7 +30,7 @@ echo "---- end test no. 1 ----" sleep $WAIT_BETWEEN_TESTS iptables -D OUTPUT -t raw -p all -j CT --timeout test-generic -nfct timeout del test-generic +nfct del timeout test-generic # # No.2: test TCP timeout policy @@ -40,14 +40,14 @@ echo "---- test no. 2 ----" conntrack -E -p tcp & -nfct timeout add test-tcp inet tcp syn_sent 100 +nfct add timeout test-tcp inet tcp syn_sent 100 iptables -I OUTPUT -t raw -p tcp -j CT --timeout test-tcp hping3 -V -S -p 80 -s 5050 8.8.8.8 -c 1 sleep $WAIT_BETWEEN_TESTS iptables -D OUTPUT -t raw -p tcp -j CT --timeout test-tcp -nfct timeout del test-tcp +nfct del timeout test-tcp killall -15 conntrack @@ -61,12 +61,12 @@ echo "---- test no. 3 ----" conntrack -E -p icmp & -nfct timeout add test-icmp inet icmp timeout 50 +nfct add timeout test-icmp inet icmp timeout 50 iptables -I OUTPUT -t raw -p icmp -j CT --timeout test-icmp hping3 -1 8.8.8.8 -c 2 iptables -D OUTPUT -t raw -p icmp -j CT --timeout test-icmp -nfct timeout del test-icmp +nfct del timeout test-icmp killall -15 conntrack diff --git a/tests/nfct/timeout/00tcp b/tests/nfct/timeout/00tcp index c9d7d24..ab2e6fc 100644 --- a/tests/nfct/timeout/00tcp +++ b/tests/nfct/timeout/00tcp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet tcp established 100 ; OK +nfct add timeout test inet tcp established 100 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet tcp syn_sent 1 syn_recv 2 established 3 fin_wait 4 close_wait 5 last_ack 6 time_wait 7 close 8 syn_sent2 9 retrans 10 unacknowledged 11 ; OK +nfct add timeout test inet tcp syn_sent 1 syn_recv 2 established 3 fin_wait 4 close_wait 5 last_ack 6 time_wait 7 close 8 syn_sent2 9 retrans 10 unacknowledged 11 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/01udp b/tests/nfct/timeout/01udp index 952526c..f8097d6 100644 --- a/tests/nfct/timeout/01udp +++ b/tests/nfct/timeout/01udp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet udp unreplied 10 ; OK +nfct add timeout test inet udp unreplied 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet udp unreplied 1 replied 2 ; OK +nfct add timeout test inet udp unreplied 1 replied 2 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/02generic b/tests/nfct/timeout/02generic index b6ca699..ffba138 100644 --- a/tests/nfct/timeout/02generic +++ b/tests/nfct/timeout/02generic @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet generic timeout 10 ; OK +nfct add timeout test inet generic timeout 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet generic timeout 1 ; OK +nfct add timeout test inet generic timeout 1 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/03udplite b/tests/nfct/timeout/03udplite index 69dda15..8ed3459 100644 --- a/tests/nfct/timeout/03udplite +++ b/tests/nfct/timeout/03udplite @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet udplite unreplied 10 ; OK +nfct add timeout test inet udplite unreplied 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet udplite unreplied 1 replied 2 ; OK +nfct add timeout test inet udplite unreplied 1 replied 2 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/04icmp b/tests/nfct/timeout/04icmp index 606e8b9..edb1c99 100644 --- a/tests/nfct/timeout/04icmp +++ b/tests/nfct/timeout/04icmp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet icmp timeout 10 ; OK +nfct add timeout test inet icmp timeout 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet icmp timeout 1 ; OK +nfct add timeout test inet icmp timeout 1 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/05icmpv6 b/tests/nfct/timeout/05icmpv6 index 16541f5..40ccc49 100644 --- a/tests/nfct/timeout/05icmpv6 +++ b/tests/nfct/timeout/05icmpv6 @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet6 icmpv6 timeout 10 ; OK +nfct add timeout test inet6 icmpv6 timeout 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet6 icmpv6 timeout 1 ; OK +nfct add timeout test inet6 icmpv6 timeout 1 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/06sctp b/tests/nfct/timeout/06sctp index f475215..62b44c6 100644 --- a/tests/nfct/timeout/06sctp +++ b/tests/nfct/timeout/06sctp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet sctp established 100 ; OK +nfct add timeout test inet sctp established 100 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet sctp closed 1 cookie_wait 2 cookie_echoed 3 established 4 shutdown_sent 5 shutdown_recd 6 shutdown_ack_sent 7 ; OK +nfct add timeout test inet sctp closed 1 cookie_wait 2 cookie_echoed 3 established 4 shutdown_sent 5 shutdown_recd 6 shutdown_ack_sent 7 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/07dccp b/tests/nfct/timeout/07dccp index 1bd4fa5..1d88585 100644 --- a/tests/nfct/timeout/07dccp +++ b/tests/nfct/timeout/07dccp @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet dccp request 100 ; OK +nfct add timeout test inet dccp request 100 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet dccp request 1 respond 2 partopen 3 open 4 closereq 5 closing 6 timewait 7 ; OK +nfct add timeout test inet dccp request 1 respond 2 partopen 3 open 4 closereq 5 closing 6 timewait 7 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK diff --git a/tests/nfct/timeout/08gre b/tests/nfct/timeout/08gre index 7ef4bdb..709b943 100644 --- a/tests/nfct/timeout/08gre +++ b/tests/nfct/timeout/08gre @@ -1,16 +1,16 @@ # add policy object `test' -nfct timeout add test inet gre unreplied 10 ; OK +nfct add timeout test inet gre unreplied 10 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK # get unexistent policy object `dummy' -nfct timeout get test ; BAD +nfct get timeout test ; BAD # delete policy object `test', however, it does not exists anymore -nfct timeout delete test ; BAD +nfct delete timeout test ; BAD # add policy object `test' -nfct timeout add test inet gre unreplied 1 replied 2 ; OK +nfct add timeout test inet gre unreplied 1 replied 2 ; OK # get policy object `test' -nfct timeout get test ; OK +nfct get timeout test ; OK # delete policy object `test' -nfct timeout delete test ; OK +nfct delete timeout test ; OK -- cgit v1.2.3 From 882bb111285a3a4465995b4af03040a291145d7b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 21 Aug 2015 19:18:38 +0200 Subject: nfct: update syntax in documentation Since dd73ceecdbe8 ("nfct: Update syntax to specify command before subsystem") the command comes before the object type. Update documentation accordingly. Signed-off-by: Pablo Neira Ayuso --- README.nfct | 6 +++--- doc/helper/conntrackd.conf | 2 +- doc/manual/conntrack-tools.tmpl | 4 ++-- nfct.8 | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.nfct b/README.nfct index 4d8e6cc..89dd328 100644 --- a/README.nfct +++ b/README.nfct @@ -9,11 +9,11 @@ more similar to `ip' and `nftables' tools (in the long run!). The `nfct' command line tool allows you to define custom timeout policies: -# nfct timeout add custom-tcp-policy1 inet tcp established 100 +# nfct add timeout custom-tcp-policy1 inet tcp established 100 You can also retrieve the existing timeout policies with: -# nfct timeout list +# nfct list timeout .tcp-policy = { .l3proto = 2, .l4proto = 6, @@ -39,7 +39,7 @@ Then, you can use the timeout policy with iptables: You can define policies for other protocols as well, eg: -# nfct timeout add custom-udp-policy1 inet udp unreplied 10 replied 20 +# nfct add timeout custom-udp-policy1 inet udp unreplied 10 replied 20 And attach them via iptables: diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf index d2d94a9..5c07509 100644 --- a/doc/helper/conntrackd.conf +++ b/doc/helper/conntrackd.conf @@ -6,7 +6,7 @@ Helper { # Before this, you have to make sure you have registered the `ftp' # user-space helper stub via: # - # nfct helper add ftp inet tcp + # nfct add helper ftp inet tcp # Type ftp inet tcp { # diff --git a/doc/manual/conntrack-tools.tmpl b/doc/manual/conntrack-tools.tmpl index d23dec5..87a792e 100644 --- a/doc/manual/conntrack-tools.tmpl +++ b/doc/manual/conntrack-tools.tmpl @@ -899,8 +899,8 @@ maintainance. Register user-space helper: -nfct helper add rpc inet udp -nfct helper add rpc inet tcp +nfct add helper rpc inet udp +nfct add helper rpc inet tcp This registers the portmapper helper for both UDP and TCP (NFSv3 traffic goes both over TCP and UDP). diff --git a/nfct.8 b/nfct.8 index 863fe12..336d9cd 100644 --- a/nfct.8 +++ b/nfct.8 @@ -40,7 +40,7 @@ Displays the version information. Displays the help message. .SH EXAMPLE .TP -.B nfct timeout add test-tcp inet tcp established 100 close 10 close_wait 10 +.B nfct add timeout test-tcp inet tcp established 100 close 10 close_wait 10 .TP This creates a timeout policy for tcp using 100 seconds for the ESTABLISHED state, 10 seconds for CLOSE state and 10 seconds for the CLOSE_WAIT state. .TP -- cgit v1.2.3 From 5df0941f73bffabd775d1c14e62295cfe46956eb Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 8 Sep 2015 19:39:46 +0200 Subject: conntrack-tools 1.4.3 release Signed-off-by: Pablo Neira Ayuso --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 523a192..70d3729 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT(conntrack-tools, 1.4.2, pablo@netfilter.org) +AC_INIT(conntrack-tools, 1.4.3, pablo@netfilter.org) AC_CONFIG_AUX_DIR([build-aux]) AC_CANONICAL_HOST -- cgit v1.2.3 From 8845f3db20c951fcf1db3229a818cfd185f17f2e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Tue, 25 Aug 2015 15:33:51 +0200 Subject: conntrack: add zone direction support This patch adds support for zone directions. Since all options have the orig/reply as a prefix, I named it --orig-zone and --reply-zone to stay consistent with the rest of the cmdline options. As for the option chars, there was no unallocated reasonable combination, thus only long options are officially exposed in the help, similarly as in other cases. Test suite results, after patch: OK: 79 BAD: 0 Signed-off-by: Daniel Borkmann Signed-off-by: Pablo Neira Ayuso --- conntrack.8 | 10 +++++- include/conntrack.h | 2 +- src/conntrack.c | 67 ++++++++++++++++++++++++++-------------- tests/conntrack/testsuite/04zone | 18 ++++++++++- 4 files changed, 70 insertions(+), 27 deletions(-) diff --git a/conntrack.8 b/conntrack.8 index abc26c5..a981a76 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -1,4 +1,4 @@ -.TH CONNTRACK 8 "Sep 25, 2014" "" "" +.TH CONNTRACK 8 "Aug 24, 2015" "" "" .\" Man page written by Harald Welte #define NUMBER_OF_CMD 19 -#define NUMBER_OF_OPT 27 +#define NUMBER_OF_OPT 29 struct ctproto_handler { struct list_head head; diff --git a/src/conntrack.c b/src/conntrack.c index 00b09b6..3ae4527 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -262,17 +262,24 @@ enum ct_options { 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_ADD_LABEL = (1 << CT_OPT_ADD_LABEL_BIT), CT_OPT_DEL_LABEL_BIT = 26, - CT_OPT_DEL_LABEL = (1 << CT_OPT_DEL_LABEL_BIT), + CT_OPT_DEL_LABEL = (1 << CT_OPT_DEL_LABEL_BIT), + + CT_OPT_ORIG_ZONE_BIT = 27, + CT_OPT_ORIG_ZONE = (1 << CT_OPT_ORIG_ZONE_BIT), + + CT_OPT_REPL_ZONE_BIT = 28, + CT_OPT_REPL_ZONE = (1 << CT_OPT_REPL_ZONE_BIT), }; /* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */ /* Update this mask to allow to filter based on new options. */ #define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | \ CT_OPT_MARK | CT_OPT_SECMARK | CT_OPT_STATUS | \ - CT_OPT_ID | CT_OPT_ZONE | CT_OPT_LABEL) + CT_OPT_ID | CT_OPT_ZONE | CT_OPT_LABEL | \ + CT_OPT_ORIG_ZONE | CT_OPT_REPL_ZONE) static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_ORIG_SRC_BIT] = "src", @@ -302,6 +309,8 @@ static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_LABEL_BIT] = "label", [CT_OPT_ADD_LABEL_BIT] = "label-add", [CT_OPT_DEL_LABEL_BIT] = "label-del", + [CT_OPT_ORIG_ZONE_BIT] = "orig-zone", + [CT_OPT_REPL_ZONE_BIT] = "reply-zone", }; static struct option original_opts[] = { @@ -345,12 +354,14 @@ static struct option original_opts[] = { {"label", 1, 0, 'l'}, {"label-add", 1, 0, '<'}, {"label-del", 2, 0, '>'}, + {"orig-zone", 1, 0, '('}, + {"reply-zone", 1, 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 @@ -365,26 +376,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,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}, + /* 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,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,2,0,2,2}, +/*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,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,2,0,0,2,2}, +/*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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,0,0}, }; static const int cmd2type[][2] = { @@ -419,6 +430,8 @@ static const int opt2type[] = { ['l'] = CT_OPT_LABEL, ['<'] = CT_OPT_ADD_LABEL, ['>'] = CT_OPT_DEL_LABEL, + ['('] = CT_OPT_ORIG_ZONE, + [')'] = CT_OPT_REPL_ZONE, }; static const int opt2family_attr[][2] = { @@ -448,6 +461,8 @@ static const int opt2attr[] = { ['l'] = ATTR_CONNLABELS, ['<'] = ATTR_CONNLABELS, ['>'] = ATTR_CONNLABELS, + ['('] = ATTR_ORIG_ZONE, + [')'] = ATTR_REPL_ZONE, }; static char exit_msg[NUMBER_OF_CMD][64] = { @@ -511,6 +526,8 @@ static const char usage_parameters[] = " -t, --timeout timeout\t\tSet timeout\n" " -u, --status status\t\tSet status, eg. ASSURED\n" " -w, --zone value\t\tSet conntrack zone\n" + " --orig-zone value\t\tSet zone for original direction\n" + " --reply-zone value\t\tSet zone for reply direction\n" " -b, --buffer-size\t\tNetlink socket buffer size\n" ; @@ -2117,6 +2134,8 @@ int main(int argc, char *argv[]) break; } case 'w': + case '(': + case ')': options |= opt2type[c]; nfct_set_attr_u16(tmpl.ct, opt2attr[c], diff --git a/tests/conntrack/testsuite/04zone b/tests/conntrack/testsuite/04zone index 4ff3d34..dc8b691 100644 --- a/tests/conntrack/testsuite/04zone +++ b/tests/conntrack/testsuite/04zone @@ -1,4 +1,4 @@ -# create dummy +# 1) zone, create dummy -I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --zone 1; OK # display dummy -L --zone 1; OK @@ -6,3 +6,19 @@ -L --zone 0; OK # delete dummy -D --zone 1; OK +# 2) orig-zone, create dummy +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --orig-zone 2; OK +# display dummy +-L --orig-zone 2; OK +# display dummy +-L --orig-zone 0; OK +# delete dummy +-D --orig-zone 2; OK +# 3) reply-zone, create dummy +-I -s 1.1.1.1 -d 2.2.2.2 -p tcp --sport 10 --dport 20 --state LISTEN -u SEEN_REPLY -t 50 --reply-zone 3; OK +# display dummy +-L --reply-zone 3; OK +# display dummy +-L --reply-zone 0; OK +# delete dummy +-D --reply-zone 3; OK -- cgit v1.2.3