diff --git a/.conntrack-tools.metadata b/.conntrack-tools.metadata new file mode 100644 index 0000000..fc9a1fd --- /dev/null +++ b/.conntrack-tools.metadata @@ -0,0 +1 @@ +50b89305bb689973d42a163c480dc77a5c0f6fe0 SOURCES/conntrack-tools-1.4.2.tar.bz2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2ab79e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/conntrack-tools-1.4.2.tar.bz2 diff --git a/README.md b/README.md deleted file mode 100644 index 98f42b4..0000000 --- a/README.md +++ /dev/null @@ -1,4 +0,0 @@ -The master branch has no content - -Look at the c7 branch if you are working with CentOS-7, or the c4/c5/c6 branch for CentOS-4, 5 or 6 -If you find this file in a distro specific branch, it means that no content has been checked in yet diff --git a/SOURCES/conntrack-tools-20150818.patch b/SOURCES/conntrack-tools-20150818.patch new file mode 100644 index 0000000..16330d8 --- /dev/null +++ b/SOURCES/conntrack-tools-20150818.patch @@ -0,0 +1,3718 @@ +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` +diff --git a/conntrack.8 b/conntrack.8 +index f273434..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 +diff --git a/doc/helper/conntrackd.conf b/doc/helper/conntrackd.conf +index 56f5162..d2d94a9 100644 +--- a/doc/helper/conntrackd.conf ++++ b/doc/helper/conntrackd.conf +@@ -62,6 +62,22 @@ Helper { + ExpectTimeout 300 + } + } ++ Type dhcpv6 inet6 udp { ++ QueueNum 4 ++ QueueLen 10240 ++ Policy dhcpv6 { ++ ExpectMax 1 ++ 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/extensions/libct_proto_dccp.c b/extensions/libct_proto_dccp.c +index 586c4cc..f6258ad 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}, +@@ -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 f4c94df..04828bf 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}, +@@ -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 0b43bf5..8a37a55 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}, +@@ -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 d7c4da1..e30637c 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}, +@@ -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 bffd5fe..f46cef0 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}, +@@ -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/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/include/helper.h b/include/helper.h +index 9d96fb7..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]; + +@@ -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/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_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/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h +index e0d8fd8..8dd819e 100644 +--- a/include/linux/netfilter/nfnetlink_queue.h ++++ b/include/linux/netfilter/nfnetlink_queue.h +@@ -44,6 +44,11 @@ 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_UID, /* __u32 sk uid */ ++ NFQA_GID, /* __u32 sk gid */ + + __NFQA_MAX + }; +@@ -95,5 +100,16 @@ 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_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 */ ++#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/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/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/include/nfct.h b/include/nfct.h +index 5548b03..dc103c6 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, +@@ -17,23 +19,26 @@ enum { + NFCT_CMD_GET, + NFCT_CMD_FLUSH, + NFCT_CMD_DISABLE, ++ NFCT_CMD_DEFAULT_SET, ++ NFCT_CMD_DEFAULT_GET, + }; + ++#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)(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), ++ void *data); + + #endif +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; +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/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); +diff --git a/src/channel.c b/src/channel.c +index 8b7c319..acbfa7d 100644 +--- a/src/channel.c ++++ b/src/channel.c +@@ -109,6 +109,7 @@ channel_open(struct channel_conf *cfg) + + if (ioctl(fd, SIOCGIFMTU, &ifr) == -1) { + free(c); ++ close(fd); + return NULL; + } + close(fd); +diff --git a/src/conntrack.c b/src/conntrack.c +index 7d2a365..00b09b6 100644 +--- a/src/conntrack.c ++++ b/src/conntrack.c +@@ -82,6 +82,9 @@ static struct { + + /* Allows filtering by ctlabels */ + struct nfct_bitmask *label; ++ ++ /* Allows setting/removing specific ctlabels */ ++ struct nfct_bitmask *label_modify; + } tmpl; + + static int alloc_tmpl_objects(void) +@@ -109,6 +112,8 @@ static void free_tmpl_objects(void) + nfexp_destroy(tmpl.exp); + if (tmpl.label) + nfct_bitmask_destroy(tmpl.label); ++ if (tmpl.label_modify) ++ nfct_bitmask_destroy(tmpl.label_modify); + } + + enum ct_command { +@@ -255,6 +260,12 @@ enum ct_options { + + CT_OPT_LABEL_BIT = 24, + CT_OPT_LABEL = (1 << CT_OPT_LABEL_BIT), ++ ++ CT_OPT_ADD_LABEL_BIT = 25, ++ CT_OPT_ADD_LABEL = (1 << CT_OPT_ADD_LABEL_BIT), ++ ++ CT_OPT_DEL_LABEL_BIT = 26, ++ CT_OPT_DEL_LABEL = (1 << CT_OPT_DEL_LABEL_BIT), + }; + /* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */ + +@@ -289,6 +300,8 @@ static const char *optflags[NUMBER_OF_OPT] = { + [CT_OPT_ANY_NAT_BIT] = "any-nat", + [CT_OPT_ZONE_BIT] = "zone", + [CT_OPT_LABEL_BIT] = "label", ++ [CT_OPT_ADD_LABEL_BIT] = "label-add", ++ [CT_OPT_DEL_LABEL_BIT] = "label-del", + }; + + static struct option original_opts[] = { +@@ -330,12 +343,14 @@ static struct option original_opts[] = { + {"any-nat", 2, 0, 'j'}, + {"zone", 1, 0, 'w'}, + {"label", 1, 0, 'l'}, ++ {"label-add", 1, 0, '<'}, ++ {"label-del", 2, 0, '>'}, + {0, 0, 0, 0} + }; + + static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:" + "p:t:u:e:a:z[:]:{:}:m:i:f:o:n::" +- "g::c:b:C::Sj::w:l:"; ++ "g::c:b:C::Sj::w:l:<:>::"; + + /* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal (applies to +@@ -350,26 +365,26 @@ static const char *getopt_str = ":L::I::U::D::G::E::F::hVs:d:r:q:" + static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = + /* Well, it's better than "Re: Linux vs FreeBSD" */ + { +- /* s d r q p t u z e [ ] { } a m i f n g o c b j w l*/ +-/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2}, +-/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0}, +-/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,0}, +-/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2,0}, +-/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0}, +-/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2}, +-/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0}, +-/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0}, +-/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +-/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++ /* s d r q p t u z e [ ] { } a m i f n g o c b j w l < > */ ++/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2,2,0,0}, ++/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2,0,2,0}, ++/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,0,2,2,2}, ++/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,0,2,2,0,0}, ++/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,2,0,0}, ++/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2,2,2,2,0,0}, ++/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,2,0,0,0,0,0,0,0}, ++/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0}, ++/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*CT_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, ++/*EXP_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, + }; + + static const int cmd2type[][2] = { +@@ -402,6 +417,8 @@ static const int opt2type[] = { + ['j'] = CT_OPT_ANY_NAT, + ['w'] = CT_OPT_ZONE, + ['l'] = CT_OPT_LABEL, ++ ['<'] = CT_OPT_ADD_LABEL, ++ ['>'] = CT_OPT_DEL_LABEL, + }; + + static const int opt2family_attr[][2] = { +@@ -420,11 +437,17 @@ static const int opt2attr[] = { + ['d'] = ATTR_ORIG_L3PROTO, + ['r'] = ATTR_REPL_L3PROTO, + ['q'] = ATTR_REPL_L3PROTO, ++ ['{'] = ATTR_ORIG_L3PROTO, ++ ['}'] = ATTR_ORIG_L3PROTO, ++ ['['] = ATTR_ORIG_L3PROTO, ++ [']'] = ATTR_ORIG_L3PROTO, + ['m'] = ATTR_MARK, + ['c'] = ATTR_SECMARK, + ['i'] = ATTR_ID, + ['w'] = ATTR_ZONE, + ['l'] = ATTR_CONNLABELS, ++ ['<'] = ATTR_CONNLABELS, ++ ['>'] = ATTR_CONNLABELS, + }; + + static char exit_msg[NUMBER_OF_CMD][64] = { +@@ -472,6 +495,11 @@ static const char usage_expectation_parameters[] = + " --mask-src ip\t\tSource mask address\n" + " --mask-dst ip\t\tDestination mask address\n"; + ++static const char usage_update_parameters[] = ++ "Updating parameters and options:\n" ++ " --label-add label\tAdd label\n" ++ " --label-del label\tDelete label\n"; ++ + static const char usage_parameters[] = + "Common parameters and options:\n" + " -s, --orig-src ip\t\tSource address from original direction\n" +@@ -523,7 +551,7 @@ static struct ctproto_handler *findproto(char *name, int *pnum) + + /* is it in the list of supported protocol? */ + list_for_each_entry(cur, &proto_list, head) { +- if (strcmp(cur->name, name) == 0) { ++ if (strcasecmp(cur->name, name) == 0) { + *pnum = cur->protonum; + return cur; + } +@@ -890,20 +918,20 @@ add_command(unsigned int *cmd, const int newcmd) + *cmd |= newcmd; + } + +-static char *get_table(int argc, char *argv[]) ++static char *get_optional_arg(int argc, char *argv[]) + { +- char *table = NULL; ++ char *arg = NULL; + + /* Nasty bug or feature in getopt_long ? + * It seems that it behaves badly with optional arguments. + * Fortunately, I just stole the fix from iptables ;) */ + if (optarg) +- return 0; ++ return arg; + else if (optind < argc && argv[optind][0] != '-' && + argv[optind][0] != '!') +- table = argv[optind++]; ++ arg = argv[optind++]; + +- return table; ++ return arg; + } + + enum { +@@ -915,7 +943,7 @@ enum { + + static unsigned int check_type(int argc, char *argv[]) + { +- const char *table = get_table(argc, argv); ++ const char *table = get_optional_arg(argc, argv); + + /* default to conntrack subsystem if nothing has been specified. */ + if (table == NULL) +@@ -1045,6 +1073,7 @@ usage(char *prog) + fprintf(stdout, "\n%s", usage_tables); + fprintf(stdout, "\n%s", usage_conntrack_parameters); + fprintf(stdout, "\n%s", usage_expectation_parameters); ++ fprintf(stdout, "\n%s", usage_update_parameters); + fprintf(stdout, "\n%s\n", usage_parameters); + } + +@@ -1349,7 +1378,7 @@ static int print_cb(enum nf_conntrack_msg_type type, + if (output_mask & _O_ID) + op_flags |= NFCT_OF_ID; + +- nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags); ++ nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap); + printf("%s\n", buf); + + return NFCT_CB_CONTINUE; +@@ -1376,6 +1405,72 @@ static void copy_status(struct nf_conntrack *tmp, const struct nf_conntrack *ct) + } + } + ++static struct nfct_bitmask *xnfct_bitmask_clone(const struct nfct_bitmask *a) ++{ ++ struct nfct_bitmask *b = nfct_bitmask_clone(a); ++ if (!b) ++ exit_error(OTHER_PROBLEM, "out of memory"); ++ return b; ++} ++ ++static void copy_label(struct nf_conntrack *tmp, const struct nf_conntrack *ct) ++{ ++ struct nfct_bitmask *ctb, *newmask; ++ unsigned int i; ++ ++ if ((options & (CT_OPT_ADD_LABEL|CT_OPT_DEL_LABEL)) == 0) ++ return; ++ ++ nfct_copy_attr(tmp, ct, ATTR_CONNLABELS); ++ ctb = (void *) nfct_get_attr(tmp, ATTR_CONNLABELS); ++ ++ if (options & CT_OPT_ADD_LABEL) { ++ if (ctb == NULL) { ++ nfct_set_attr(tmp, ATTR_CONNLABELS, ++ xnfct_bitmask_clone(tmpl.label_modify)); ++ return; ++ } ++ /* If we send a bitmask shorter than the kernel sent to us, the bits we ++ * omit will be cleared (as "padding"). So we always have to send the ++ * same sized bitmask as we received. ++ * ++ * Mask has to have the same size as the labels, otherwise it will not ++ * be encoded by libnetfilter_conntrack, as different sizes are not ++ * accepted by the kernel. ++ */ ++ newmask = nfct_bitmask_new(nfct_bitmask_maxbit(ctb)); ++ ++ for (i = 0; i <= nfct_bitmask_maxbit(ctb); i++) { ++ if (nfct_bitmask_test_bit(tmpl.label_modify, i)) { ++ nfct_bitmask_set_bit(ctb, i); ++ nfct_bitmask_set_bit(newmask, i); ++ } else if (nfct_bitmask_test_bit(ctb, i)) { ++ /* Kernel only retains old bit values that are sent as ++ * zeroes in BOTH labels and mask. ++ */ ++ nfct_bitmask_unset_bit(ctb, i); ++ } ++ } ++ nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask); ++ } else if (ctb != NULL) { ++ /* CT_OPT_DEL_LABEL */ ++ if (tmpl.label_modify == NULL) { ++ newmask = nfct_bitmask_new(0); ++ if (newmask) ++ nfct_set_attr(tmp, ATTR_CONNLABELS, newmask); ++ return; ++ } ++ ++ for (i = 0; i <= nfct_bitmask_maxbit(ctb); i++) { ++ if (nfct_bitmask_test_bit(tmpl.label_modify, i)) ++ nfct_bitmask_unset_bit(ctb, i); ++ } ++ ++ newmask = xnfct_bitmask_clone(tmpl.label_modify); ++ nfct_set_attr(tmp, ATTR_CONNLABELS_MASK, newmask); ++ } ++} ++ + static int update_cb(enum nf_conntrack_msg_type type, + struct nf_conntrack *ct, + void *data) +@@ -1395,6 +1490,9 @@ static int update_cb(enum nf_conntrack_msg_type type, + if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL)) + return NFCT_CB_CONTINUE; + ++ if (filter_label(ct)) ++ return NFCT_CB_CONTINUE; ++ + tmp = nfct_new(); + if (tmp == NULL) + exit_error(OTHER_PROBLEM, "out of memory"); +@@ -1403,6 +1501,7 @@ static int update_cb(enum nf_conntrack_msg_type type, + nfct_copy(tmp, obj, NFCT_CP_META); + copy_mark(tmp, ct, &tmpl.mark); + copy_status(tmp, ct); ++ copy_label(tmp, ct); + + /* do not send NFCT_Q_UPDATE if ct appears unchanged */ + if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) { +@@ -1411,12 +1510,10 @@ static int update_cb(enum nf_conntrack_msg_type type, + } + + res = nfct_query(ith, NFCT_Q_UPDATE, tmp); +- if (res < 0) { +- nfct_destroy(tmp); +- exit_error(OTHER_PROBLEM, +- "Operation failed: %s", ++ if (res < 0) ++ fprintf(stderr, ++ "Operation failed: %s\n", + err2str(errno, CT_UPDATE)); +- } + nfct_callback_register(ith, NFCT_T_ALL, print_cb, NULL); + + res = nfct_query(ith, NFCT_Q_GET, tmp); +@@ -1601,7 +1698,8 @@ static int nfct_mnl_socket_open(void) + } + + static struct nlmsghdr * +-nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type) ++nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type, ++ uint8_t family) + { + struct nlmsghdr *nlh; + struct nfgenmsg *nfh; +@@ -1612,7 +1710,7 @@ nfct_mnl_nlmsghdr_put(char *buf, uint16_t subsys, uint16_t type) + nlh->nlmsg_seq = time(NULL); + + nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); +- nfh->nfgen_family = AF_INET; ++ nfh->nfgen_family = family; + nfh->version = NFNETLINK_V0; + nfh->res_id = 0; + +@@ -1625,13 +1723,13 @@ static void nfct_mnl_socket_close(void) + } + + static int +-nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb) ++nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb, uint8_t family) + { + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int res; + +- nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type); ++ nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, family); + + res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len); + if (res < 0) +@@ -1651,13 +1749,13 @@ nfct_mnl_dump(uint16_t subsys, uint16_t type, mnl_cb_t cb) + } + + static int +-nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb) ++nfct_mnl_get(uint16_t subsys, uint16_t type, mnl_cb_t cb, uint8_t family) + { + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int res; + +- nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type); ++ nlh = nfct_mnl_nlmsghdr_put(buf, subsys, type, family); + + res = mnl_socket_sendto(sock.mnl, nlh, nlh->nlmsg_len); + if (res < 0) +@@ -1818,6 +1916,65 @@ static int mnl_nfct_dump_cb(const struct nlmsghdr *nlh, void *data) + + static struct ctproto_handler *h; + ++static void labelmap_init(void) ++{ ++ if (labelmap) ++ return; ++ labelmap = nfct_labelmap_new(NULL); ++ if (!labelmap) ++ perror("nfct_labelmap_new"); ++} ++ ++static void merge_bitmasks(struct nfct_bitmask **current, ++ struct nfct_bitmask *src) ++{ ++ unsigned int i; ++ ++ if (*current == NULL) { ++ *current = src; ++ return; ++ } ++ ++ /* "current" must be the larger bitmask object */ ++ if (nfct_bitmask_maxbit(src) > nfct_bitmask_maxbit(*current)) { ++ struct nfct_bitmask *tmp = *current; ++ *current = src; ++ src = tmp; ++ } ++ ++ for (i = 0; i <= nfct_bitmask_maxbit(src); i++) { ++ if (nfct_bitmask_test_bit(src, i)) ++ nfct_bitmask_set_bit(*current, i); ++ } ++ ++ nfct_bitmask_destroy(src); ++} ++ ++static void ++nfct_set_addr_from_opt(int opt, struct nf_conntrack *ct, union ct_address *ad, ++ int *family) ++{ ++ int l3protonum; ++ ++ options |= opt2type[opt]; ++ l3protonum = parse_addr(optarg, ad); ++ if (l3protonum == AF_UNSPEC) { ++ exit_error(PARAMETER_PROBLEM, ++ "Invalid IP address `%s'", optarg); ++ } ++ set_family(family, l3protonum); ++ if (l3protonum == AF_INET) { ++ nfct_set_attr_u32(ct, ++ opt2family_attr[opt][0], ++ ad->v4); ++ } else if (l3protonum == AF_INET6) { ++ nfct_set_attr(ct, ++ opt2family_attr[opt][1], ++ &ad->v6); ++ } ++ nfct_set_attr_u8(ct, opt2attr[opt], l3protonum); ++} ++ + int main(int argc, char *argv[]) + { + int c, cmd; +@@ -1825,7 +1982,7 @@ int main(int argc, char *argv[]) + int res = 0, partial; + size_t socketbuffersize = 0; + int family = AF_UNSPEC; +- int l3protonum, protonum = 0; ++ int protonum = 0; + union ct_address ad; + unsigned int command = 0; + +@@ -1896,47 +2053,15 @@ int main(int argc, char *argv[]) + case 'd': + case 'r': + case 'q': +- options |= opt2type[c]; +- +- l3protonum = parse_addr(optarg, &ad); +- if (l3protonum == AF_UNSPEC) { +- exit_error(PARAMETER_PROBLEM, +- "Invalid IP address `%s'", optarg); +- } +- set_family(&family, l3protonum); +- if (l3protonum == AF_INET) { +- nfct_set_attr_u32(tmpl.ct, +- opt2family_attr[c][0], +- ad.v4); +- } else if (l3protonum == AF_INET6) { +- nfct_set_attr(tmpl.ct, +- opt2family_attr[c][1], +- &ad.v6); +- } +- nfct_set_attr_u8(tmpl.ct, opt2attr[c], l3protonum); ++ nfct_set_addr_from_opt(c, tmpl.ct, &ad, &family); + break; + case '{': + case '}': ++ nfct_set_addr_from_opt(c, tmpl.exptuple, &ad, &family); ++ break; + case '[': + case ']': +- options |= opt2type[c]; +- l3protonum = parse_addr(optarg, &ad); +- if (l3protonum == AF_UNSPEC) { +- exit_error(PARAMETER_PROBLEM, +- "Invalid IP address `%s'", optarg); +- } +- set_family(&family, l3protonum); +- if (l3protonum == AF_INET) { +- nfct_set_attr_u32(tmpl.mask, +- opt2family_attr[c][0], +- ad.v4); +- } else if (l3protonum == AF_INET6) { +- nfct_set_attr(tmpl.mask, +- opt2family_attr[c][1], +- &ad.v6); +- } +- nfct_set_attr_u8(tmpl.mask, +- ATTR_ORIG_L3PROTO, l3protonum); ++ nfct_set_addr_from_opt(c, tmpl.mask, &ad, &family); + break; + case 'p': + options |= CT_OPT_PROTO; +@@ -1970,12 +2095,8 @@ int main(int argc, char *argv[]) + case 'o': + options |= CT_OPT_OUTPUT; + parse_parameter(optarg, &output_mask, PARSE_OUTPUT); +- if (output_mask & _O_CL) { +- if (!labelmap) +- labelmap = nfct_labelmap_new(NULL); +- if (!labelmap) +- perror("nfct_labelmap_new"); +- } ++ if (output_mask & _O_CL) ++ labelmap_init(); + break; + case 'z': + options |= CT_OPT_ZERO; +@@ -1987,12 +2108,7 @@ int main(int argc, char *argv[]) + + options |= opt2type[c]; + +- if (optarg) +- continue; +- else if (optind < argc && argv[optind][0] != '-' +- && argv[optind][0] != '!') +- tmp = argv[optind++]; +- ++ tmp = get_optional_arg(argc, argv); + if (tmp == NULL) + continue; + +@@ -2020,19 +2136,39 @@ int main(int argc, char *argv[]) + tmpl.filter_mark_kernel.mask = tmpl.mark.mask; + break; + case 'l': ++ case '<': ++ case '>': + options |= opt2type[c]; +- char *optarg2 = strdup(optarg); + +- if (!labelmap) +- labelmap = nfct_labelmap_new(NULL); +- if (!labelmap) +- exit_error(OTHER_PROBLEM, "unable to open labelmap file"); ++ labelmap_init(); + ++ if ((options & (CT_OPT_DEL_LABEL|CT_OPT_ADD_LABEL)) == ++ (CT_OPT_DEL_LABEL|CT_OPT_ADD_LABEL)) ++ exit_error(OTHER_PROBLEM, "cannot use --label-add and " ++ "--label-del at the same time"); ++ ++ if (c == '>') { /* DELETE */ ++ char *tmp = get_optional_arg(argc, argv); ++ if (tmp == NULL) /* delete all labels */ ++ break; ++ optarg = tmp; ++ } ++ ++ char *optarg2 = strdup(optarg); + unsigned int max = parse_label_get_max(optarg); + struct nfct_bitmask * b = nfct_bitmask_new(max); ++ if (!b) ++ exit_error(OTHER_PROBLEM, "out of memory"); + + parse_label(b, optarg2); +- tmpl.label = b; ++ ++ /* join "-l foo -l bar" into single bitmask object */ ++ if (c == 'l') { ++ merge_bitmasks(&tmpl.label, b); ++ } else { ++ merge_bitmasks(&tmpl.label_modify, b); ++ } ++ + free(optarg2); + break; + case 'a': +@@ -2114,7 +2250,7 @@ int main(int argc, char *argv[]) + + res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_DYING, +- mnl_nfct_dump_cb); ++ mnl_nfct_dump_cb, family); + + nfct_mnl_socket_close(); + break; +@@ -2124,7 +2260,7 @@ int main(int argc, char *argv[]) + + res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_UNCONFIRMED, +- mnl_nfct_dump_cb); ++ mnl_nfct_dump_cb, family); + + nfct_mnl_socket_close(); + break; +@@ -2191,6 +2327,10 @@ int main(int argc, char *argv[]) + if (options & CT_OPT_MARK) + nfct_set_attr_u32(tmpl.ct, ATTR_MARK, tmpl.mark.value); + ++ if (options & CT_OPT_ADD_LABEL) ++ nfct_set_attr(tmpl.ct, ATTR_CONNLABELS, ++ xnfct_bitmask_clone(tmpl.label_modify)); ++ + cth = nfct_open(CONNTRACK, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); +@@ -2389,7 +2529,7 @@ int main(int argc, char *argv[]) + + res = nfct_mnl_get(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_STATS, +- nfct_global_stats_cb); ++ nfct_global_stats_cb, AF_UNSPEC); + + nfct_mnl_socket_close(); + +@@ -2434,7 +2574,7 @@ try_proc_count: + + res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK, + IPCTNL_MSG_CT_GET_STATS_CPU, +- nfct_stats_cb); ++ nfct_stats_cb, AF_UNSPEC); + + nfct_mnl_socket_close(); + +@@ -2453,7 +2593,7 @@ try_proc_count: + + res = nfct_mnl_dump(NFNL_SUBSYS_CTNETLINK_EXP, + IPCTNL_MSG_EXP_GET_STATS_CPU, +- nfexp_stats_cb); ++ nfexp_stats_cb, AF_UNSPEC); + + nfct_mnl_socket_close(); + +diff --git a/src/cthelper.c b/src/cthelper.c +index 5a8a92a..6537515 100644 +--- a/src/cthelper.c ++++ b/src/cthelper.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#define _GNU_SOURCE + #include + #include + #include +@@ -182,6 +183,15 @@ pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct, + nfct_nlmsg_build(nlh, myct->ct); + mnl_attr_nest_end(nlh, nest); + ++ if (myct->exp) { ++ nest = mnl_attr_nest_start(nlh, NFQA_EXP); ++ if (nest == NULL) ++ return -1; ++ ++ nfexp_nlmsg_build(nlh, myct->exp); ++ mnl_attr_nest_end(nlh, nest); ++ } ++ + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send verdict: %s", strerror(errno)); + return -1; +@@ -315,12 +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); +- if (myct && myct->priv_data != NULL) ++ nfct_destroy(ct); ++ if (myct->exp != NULL) ++ nfexp_destroy(myct->exp); ++ if (myct->priv_data != NULL) + free(myct->priv_data); +- if (myct != NULL) +- free(myct); ++ free(myct); + + return MNL_CB_OK; + err_pktb: +@@ -458,7 +468,10 @@ static int cthelper_nfqueue_setup(struct ctd_helper_instance *cur) + nfq_nlmsg_cfg_put_params(nlh, NFQNL_COPY_PACKET, 0xffff); + mnl_attr_put_u32(nlh, NFQA_CFG_FLAGS, htonl(NFQA_CFG_F_CONNTRACK)); + mnl_attr_put_u32(nlh, NFQA_CFG_MASK, htonl(0xffffffff)); +- mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, htonl(cur->queue_len)); ++ if (cur->queue_len > 0) { ++ mnl_attr_put_u32(nlh, NFQA_CFG_QUEUE_MAXLEN, ++ htonl(cur->queue_len)); ++ } + + if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) { + dlog(LOG_ERR, "failed to send configuration"); +diff --git a/src/expect.c b/src/expect.c +index 470b9ae..5add7be 100644 +--- a/src/expect.c ++++ b/src/expect.c +@@ -39,8 +39,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + + if (saddr) { + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { +- int i; +- uint32_t addr[4] = {}; ++ uint32_t addr[4]; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); +@@ -52,10 +51,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + case AF_INET6: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(expected, ATTR_IPV6_SRC, saddr->ip6); +- +- for (i=0; i<4; i++) +- memset(&addr[i], 0xffffffff, sizeof(uint32_t)); +- ++ memset(addr, 0xff, sizeof(addr)); + nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(mask, ATTR_IPV6_SRC, addr); + break; +@@ -64,8 +60,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + } + } else { + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { +- int i; +- uint32_t addr[4] = {}; ++ uint32_t addr[4]; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); +@@ -75,9 +70,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + nfct_set_attr_u32(mask, ATTR_IPV4_SRC, 0x00000000); + break; + case AF_INET6: +- for (i=0; i<4; i++) +- memset(&addr[i], 0x00000000, sizeof(uint32_t)); +- ++ memset(addr, 0x00, sizeof(addr)); + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(expected, ATTR_IPV6_SRC, addr); + +@@ -116,8 +109,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + } + + switch(nfct_get_attr_u8(master, ATTR_L3PROTO)) { +- uint32_t addr[4] = {}; +- int i; ++ uint32_t addr[4]; + + case AF_INET: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET); +@@ -127,10 +119,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, + case AF_INET6: + nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); + nfct_set_attr(expected, ATTR_IPV6_DST, daddr->ip6); +- +- for (i=0; i<4; i++) +- memset(addr, 0xffffffff, sizeof(uint32_t)); +- ++ memset(addr, 0xff, sizeof(addr)); + nfct_set_attr(mask, ATTR_IPV6_DST, addr); + break; + default: +@@ -212,3 +201,27 @@ cthelper_get_addr_dst(struct nf_conntrack *ct, int dir, + break; + } + } ++ ++void cthelper_get_port_src(struct nf_conntrack *ct, int dir, uint16_t *port) ++{ ++ switch (dir) { ++ case MYCT_DIR_ORIG: ++ *port = nfct_get_attr_u16(ct, ATTR_PORT_SRC); ++ break; ++ case MYCT_DIR_REPL: ++ *port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC); ++ break; ++ } ++} ++ ++void cthelper_get_port_dst(struct nf_conntrack *ct, int dir, uint16_t *port) ++{ ++ switch (dir) { ++ case MYCT_DIR_ORIG: ++ *port = nfct_get_attr_u16(ct, ATTR_PORT_DST); ++ break; ++ case MYCT_DIR_REPL: ++ *port = nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST); ++ break; ++ } ++} +diff --git a/src/filter.c b/src/filter.c +index 8fac71b..1ae2cc5 100644 +--- a/src/filter.c ++++ b/src/filter.c +@@ -33,8 +33,8 @@ + + struct ct_filter { + int logic[CT_FILTER_MAX]; +- u_int32_t l4protomap[IPPROTO_MAX/32]; +- u_int16_t statemap[IPPROTO_MAX]; ++ uint32_t l4protomap[IPPROTO_MAX/32]; ++ uint16_t statemap[IPPROTO_MAX]; + struct hashtable *h; + struct hashtable *h6; + struct vector *v; +diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am +index 589b4f3..78ef7aa 100644 +--- a/src/helpers/Makefile.am ++++ b/src/helpers/Makefile.am +@@ -1,8 +1,21 @@ + include $(top_srcdir)/Make_global.am + +-pkglib_LTLIBRARIES = ct_helper_ftp.la \ ++pkglib_LTLIBRARIES = ct_helper_amanda.la \ ++ ct_helper_dhcpv6.la \ ++ ct_helper_ftp.la \ + ct_helper_rpc.la \ +- ct_helper_tns.la ++ ct_helper_tftp.la \ ++ ct_helper_tns.la \ ++ ct_helper_sane.la \ ++ ct_helper_ssdp.la ++ ++ct_helper_amanda_la_SOURCES = amanda.c ++ct_helper_amanda_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ++ct_helper_amanda_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) ++ ++ct_helper_dhcpv6_la_SOURCES = dhcpv6.c ++ct_helper_dhcpv6_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ++ct_helper_dhcpv6_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + + ct_helper_ftp_la_SOURCES = ftp.c + ct_helper_ftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +@@ -12,6 +25,18 @@ ct_helper_rpc_la_SOURCES = rpc.c + ct_helper_rpc_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) + ct_helper_rpc_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + ++ct_helper_tftp_la_SOURCES = tftp.c ++ct_helper_tftp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ++ct_helper_tftp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) ++ + ct_helper_tns_la_SOURCES = tns.c + ct_helper_tns_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) + ct_helper_tns_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) ++ ++ct_helper_sane_la_SOURCES = sane.c ++ct_helper_sane_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ++ct_helper_sane_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) ++ ++ct_helper_ssdp_la_SOURCES = ssdp.c ++ct_helper_ssdp_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ++ct_helper_ssdp_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) +diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c +new file mode 100644 +index 0000000..9e6c4e7 +--- /dev/null ++++ b/src/helpers/amanda.c +@@ -0,0 +1,203 @@ ++/* ++ * (C) 2013 by Pablo Neira Ayuso ++ * ++ * 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")]; ++ uint16_t port, initial_port; ++ unsigned int ret; ++ const struct nf_conntrack *expected; ++ struct nf_conntrack *nat_tuple; ++ ++ nat_tuple = nfct_new(); ++ if (nat_tuple == NULL) ++ return NF_ACCEPT; ++ ++ expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); ++ ++ /* Connection comes from client. */ ++ initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); ++ nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, IP_CT_DIR_ORIGINAL); ++ ++ /* libnetfilter_conntrack needs this */ ++ nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET); ++ nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0); ++ nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0); ++ nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP); ++ nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0); ++ ++ /* When you see the packet, we need to NAT it the same as the ++ * this one (ie. same IP: it will be TCP and master is UDP). */ ++ nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master"); ++ ++ /* Try to get same port: if not, try to change it. */ ++ for (port = ntohs(initial_port); port != 0; port++) { ++ int res; ++ ++ nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); ++ nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); ++ ++ res = cthelper_add_expect(exp); ++ if (res == 0) ++ break; ++ else if (res != -EBUSY) { ++ port = 0; ++ break; ++ } ++ } ++ ++ if (port == 0) { ++ pr_debug("all ports in use\n"); ++ return NF_DROP; ++ } ++ ++ sprintf(buffer, "%u", port); ++ ret = nfq_udp_mangle_ipv4(pkt, matchoff, matchlen, buffer, ++ strlen(buffer)); ++ if (ret != NF_ACCEPT) { ++ pr_debug("cannot mangle packet\n"); ++ cthelper_del_expect(exp); ++ } ++ return ret; ++} ++ ++static char amanda_buffer[65536]; ++static unsigned int master_timeout = 300; ++ ++enum amanda_strings { ++ SEARCH_CONNECT, ++ SEARCH_NEWLINE, ++ SEARCH_DATA, ++ SEARCH_MESG, ++ SEARCH_INDEX, ++}; ++ ++static const char *conns[] = { "DATA ", "MESG ", "INDEX " }; ++ ++static int ++amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff, ++ struct myct *myct, uint32_t ctinfo) ++{ ++ struct nf_expect *exp; ++ char *data, *data_limit, *tmp; ++ unsigned int dataoff, i; ++ uint16_t port, len; ++ int ret = NF_ACCEPT; ++ struct iphdr *iph; ++ union nfct_attr_grp_addr saddr, daddr; ++ ++ /* Only look at packets from the Amanda server */ ++ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ++ return NF_ACCEPT; ++ ++ /* increase the UDP timeout of the master connection as replies from ++ * Amanda clients to the server can be quite delayed */ ++ nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, master_timeout); ++ ++ /* No data? */ ++ iph = (struct iphdr *)pktb_network_header(pkt); ++ dataoff = iph->ihl*4 + sizeof(struct udphdr); ++ if (dataoff >= pktb_len(pkt)) { ++ pr_debug("amanda_help: pktlen = %u\n", pktb_len(pkt)); ++ return NF_ACCEPT; ++ } ++ ++ memcpy(amanda_buffer, pktb_network_header(pkt) + dataoff, ++ pktb_len(pkt) - dataoff); ++ data = amanda_buffer; ++ data_limit = amanda_buffer + pktb_len(pkt) - dataoff; ++ *data_limit = '\0'; ++ ++ /* Search for the CONNECT string */ ++ data = strstr(data, "CONNECT "); ++ if (!data) ++ goto out; ++ data += strlen("CONNECT "); ++ ++ /* Only search first line. */ ++ if ((tmp = strchr(data, '\n'))) ++ *tmp = '\0'; ++ ++ for (i = 0; i < ARRAY_SIZE(conns); i++) { ++ char *match = strstr(data, conns[i]); ++ if (!match) ++ continue; ++ tmp = data = match + strlen(conns[i]); ++ port = strtoul(data, &data, 10); ++ len = data - tmp; ++ if (port == 0 || len > 5) ++ break; ++ ++ exp = nfexp_new(); ++ if (exp == NULL) ++ return NF_DROP; ++ ++ cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); ++ cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); ++ cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); ++ ++ if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, ++ IPPROTO_TCP, NULL, &port, 0)) { ++ nfexp_destroy(exp); ++ return NF_DROP; ++ } ++ ++ if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { ++ ret = nat_amanda(pkt, ctinfo, tmp - amanda_buffer, ++ len, exp); ++ } else ++ myct->exp = exp; ++ } ++out: ++ return ret; ++} ++ ++static struct ctd_helper amanda_helper = { ++ .name = "amanda", ++ .l4proto = IPPROTO_UDP, ++ .cb = amanda_helper_cb, ++ .policy = { ++ [0] = { ++ .name = "amanda", ++ .expect_max = ARRAY_SIZE(conns), ++ .expect_timeout = 180, ++ }, ++ }, ++}; ++ ++void __attribute__ ((constructor)) amanda_init(void); ++ ++void amanda_init(void) ++{ ++ helper_register(&amanda_helper); ++} +diff --git a/src/helpers/dhcpv6.c b/src/helpers/dhcpv6.c +new file mode 100644 +index 0000000..73632ec +--- /dev/null ++++ b/src/helpers/dhcpv6.c +@@ -0,0 +1,123 @@ ++/* ++ * (C) 2013 by Pablo Neira Ayuso ++ * ++ * 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); ++} +diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c +index 2c8dcd6..24ee877 100644 +--- a/src/helpers/ftp.c ++++ b/src/helpers/ftp.c +@@ -25,6 +25,7 @@ + #include /* for isdigit */ + #include + ++#define _GNU_SOURCE + #include + + #include +@@ -58,7 +59,7 @@ enum nf_ct_ftp_type { + }; + + static int +-get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term) ++get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, uint8_t term) + { + const char *end; + int ret = in6_pton(src, min_t(size_t, dlen, 0xffff), +diff --git a/src/helpers/sane.c b/src/helpers/sane.c +new file mode 100644 +index 0000000..c30f4ba +--- /dev/null ++++ b/src/helpers/sane.c +@@ -0,0 +1,173 @@ ++/* ++ * (C) 2013 by Pablo Neira Ayuso ++ * ++ * 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 ++#define _GNU_SOURCE ++#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); ++} +diff --git a/src/helpers/ssdp.c b/src/helpers/ssdp.c +new file mode 100644 +index 0000000..bc41087 +--- /dev/null ++++ b/src/helpers/ssdp.c +@@ -0,0 +1,134 @@ ++/* ++ * SSDP connection tracking helper ++ * (SSDP = Simple Service Discovery Protocol) ++ * For documentation about SSDP see ++ * http://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol ++ * ++ * Copyright (C) 2014 Ashley Hughes ++ * 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; ++ uint16_t port; ++ unsigned int dataoff; ++ void *sb_ptr; ++ ++ cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); ++ switch (nfct_get_attr_u8(myct->ct, ATTR_L3PROTO)) { ++ case AF_INET: ++ inet_pton(AF_INET, SSDP_MCAST_ADDR, &(taddr.ip)); ++ if (daddr.ip == taddr.ip) ++ good_packet = 1; ++ break; ++ case AF_INET6: ++ inet_pton(AF_INET6, UPNP_MCAST_LL_ADDR, &(taddr.ip6)); ++ if (daddr.ip6[0] == taddr.ip6[0] && ++ daddr.ip6[1] == taddr.ip6[1] && ++ daddr.ip6[2] == taddr.ip6[2] && ++ daddr.ip6[3] == taddr.ip6[3]) { ++ good_packet = 1; ++ break; ++ } ++ inet_pton(AF_INET6, UPNP_MCAST_SL_ADDR, &(taddr.ip6)); ++ if (daddr.ip6[0] == taddr.ip6[0] && ++ daddr.ip6[1] == taddr.ip6[1] && ++ daddr.ip6[2] == taddr.ip6[2] && ++ daddr.ip6[3] == taddr.ip6[3]) { ++ good_packet = 1; ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ if (!good_packet) { ++ pr_debug("ssdp_help: destination address not multicast; ignoring\n"); ++ return NF_ACCEPT; ++ } ++ ++ /* No data? Ignore */ ++ dataoff = net_hdr->ihl*4 + sizeof(struct udphdr); ++ if (dataoff >= pktb_len(pkt)) { ++ pr_debug("ssdp_help: UDP payload too small for M-SEARCH; ignoring\n"); ++ return NF_ACCEPT; ++ } ++ ++ sb_ptr = pktb_network_header(pkt) + dataoff; ++ ++ if (memcmp(sb_ptr, SSDP_M_SEARCH, SSDP_M_SEARCH_SIZE) != 0) { ++ pr_debug("ssdp_help: UDP payload does not begin with 'M-SEARCH'; ignoring\n"); ++ return NF_ACCEPT; ++ } ++ ++ cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); ++ cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); ++ ++ exp = nfexp_new(); ++ if (exp == NULL) ++ return NF_DROP; ++ ++ if (cthelper_expect_init(exp, myct->ct, 0, NULL, &saddr, ++ IPPROTO_UDP, NULL, &port, ++ NF_CT_EXPECT_PERMANENT)) { ++ nfexp_destroy(exp); ++ return NF_DROP; ++ } ++ myct->exp = exp; ++ ++ return ret; ++} ++ ++static struct ctd_helper ssdp_helper = { ++ .name = "ssdp", ++ .l4proto = IPPROTO_UDP, ++ .priv_data_len = 0, ++ .cb = ssdp_helper_cb, ++ .policy = { ++ [0] = { ++ .name = "ssdp", ++ .expect_max = 1, ++ .expect_timeout = 5 * 60, ++ }, ++ }, ++}; ++ ++static void __attribute__ ((constructor)) ssdp_init(void) ++{ ++ helper_register(&ssdp_helper); ++} +diff --git a/src/helpers/tftp.c b/src/helpers/tftp.c +new file mode 100644 +index 0000000..45591c6 +--- /dev/null ++++ b/src/helpers/tftp.c +@@ -0,0 +1,138 @@ ++/* ++ * (C) 2013 by Pablo Neira Ayuso ++ * ++ * 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); ++} +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 +diff --git a/src/internal_bypass.c b/src/internal_bypass.c +index ce2ae46..61988c7 100644 +--- a/src/internal_bypass.c ++++ b/src/internal_bypass.c +@@ -49,7 +49,7 @@ internal_bypass_ct_dump_cb(enum nf_conntrack_msg_type type, + static void internal_bypass_ct_dump(int fd, int type) + { + struct nfct_handle *h; +- u_int32_t family = AF_UNSPEC; ++ uint32_t family = AF_UNSPEC; + int ret; + + h = nfct_open(CONFIG(netlink).subsys_id, 0); +@@ -180,7 +180,7 @@ internal_bypass_exp_dump_cb(enum nf_conntrack_msg_type type, + static void internal_bypass_exp_dump(int fd, int type) + { + struct nfct_handle *h; +- u_int32_t family = AF_UNSPEC; ++ uint32_t family = AF_UNSPEC; + int ret; + + h = nfct_open(CONFIG(netlink).subsys_id, 0); +diff --git a/src/local.c b/src/local.c +index feff608..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); +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) +diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c +index f91fc41..bfb153f 100644 +--- a/src/nfct-extensions/helper.c ++++ b/src/nfct-extensions/helper.c +@@ -37,8 +37,15 @@ nfct_cmd_helper_usage(char *argv[]) + "[parameters...]\n", VERSION, argv[0]); + } + +-int +-nfct_cmd_helper_parse_params(int argc, char *argv[]) ++static int nfct_cmd_helper_list(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_helper_delete(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_helper_get(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_helper_flush(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]); ++ ++static int ++nfct_cmd_helper_parse_params(struct mnl_socket *nl, int argc, char *argv[]) + { + int cmd = NFCT_CMD_NONE, ret = 0; + +@@ -66,24 +73,25 @@ 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; + } + +@@ -115,13 +123,11 @@ err: + return MNL_CB_OK; + } + +-int nfct_cmd_helper_list(int argc, char *argv[]) ++static int nfct_cmd_helper_list(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + unsigned int seq, portid; +- int ret; + + if (argc > 3) { + nfct_perror("too many arguments"); +@@ -132,42 +138,17 @@ int nfct_cmd_helper_list(int argc, char *argv[]) + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_GET, + NLM_F_DUMP, seq); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); +- return -1; +- } +- +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } + portid = mnl_socket_get_portid(nl); +- +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); +- return -1; +- } +- +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, nfct_helper_cb, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } +- mnl_socket_close(nl); + + return 0; + } + +-int nfct_cmd_helper_add(int argc, char *argv[]) ++static int nfct_cmd_helper_add(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; +@@ -175,7 +156,7 @@ 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" +@@ -248,47 +229,22 @@ int nfct_cmd_helper_add(int argc, char *argv[]) + + nfct_helper_free(t); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); +- return -1; +- } +- +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } + portid = mnl_socket_get_portid(nl); +- +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } + +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); +- return -1; +- } +- mnl_socket_close(nl); +- + return 0; + } + +-int nfct_cmd_helper_delete(int argc, char *argv[]) ++static int ++nfct_cmd_helper_delete(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_helper *t; +- int ret; + + if (argc < 4) { + nfct_perror("missing helper policy name"); +@@ -341,48 +297,21 @@ int nfct_cmd_helper_delete(int argc, char *argv[]) + + nfct_helper_free(t); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); +- return -1; +- } +- +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } + portid = mnl_socket_get_portid(nl); +- +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } + +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); +- return -1; +- } +- +- mnl_socket_close(nl); +- + return 0; + } + +-int nfct_cmd_helper_get(int argc, char *argv[]) ++static int nfct_cmd_helper_get(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_helper *t; +- int ret; + + if (argc < 4) { + nfct_perror("missing helper policy name"); +@@ -435,46 +364,21 @@ int nfct_cmd_helper_get(int argc, char *argv[]) + + nfct_helper_free(t); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); +- return -1; +- } +- +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } + portid = mnl_socket_get_portid(nl); +- +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); +- return -1; +- } +- +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, nfct_helper_cb, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_helper_cb, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } +- mnl_socket_close(nl); + + return 0; + } + +-int nfct_cmd_helper_flush(int argc, char *argv[]) ++static int ++nfct_cmd_helper_flush(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; +- int ret; + + if (argc > 3) { + nfct_perror("too many arguments"); +@@ -485,43 +389,18 @@ int nfct_cmd_helper_flush(int argc, char *argv[]) + nlh = nfct_helper_nlmsg_build_hdr(buf, NFNL_MSG_CTHELPER_DEL, + NLM_F_ACK, seq); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); +- return -1; +- } +- +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } + portid = mnl_socket_get_portid(nl); +- +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); +- return -1; +- } +- +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } + +- mnl_socket_close(nl); +- + return 0; + } + +-int nfct_cmd_helper_disable(int argc, char *argv[]) ++static int ++nfct_cmd_helper_disable(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; +@@ -529,7 +408,6 @@ 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" +@@ -580,36 +458,21 @@ int nfct_cmd_helper_disable(int argc, char *argv[]) + + nfct_helper_free(t); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); +- return -1; +- } +- +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } + portid = mnl_socket_get_portid(nl); +- +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } + +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); +- return -1; +- } +- mnl_socket_close(nl); +- + return 0; + } + ++static struct nfct_extension helper = { ++ .type = NFCT_SUBSYS_HELPER, ++ .parse_params = nfct_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..c9aa386 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 +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -31,11 +32,20 @@ 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]); + } + +-int nfct_cmd_timeout_parse_params(int argc, char *argv[]) ++static int nfct_cmd_timeout_list(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_timeout_delete(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_timeout_get(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *argv[]); ++static int nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]); ++ ++static int ++nfct_cmd_timeout_parse_params(struct mnl_socket *nl, int argc, char *argv[]) + { + int cmd = NFCT_CMD_NONE, ret; + +@@ -53,6 +63,10 @@ int nfct_cmd_timeout_parse_params(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]); +@@ -61,19 +75,25 @@ 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; ++ 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; + } + +@@ -105,13 +125,11 @@ err: + return MNL_CB_OK; + } + +-int nfct_cmd_timeout_list(int argc, char *argv[]) ++static int nfct_cmd_timeout_list(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + unsigned int seq, portid; +- int ret; + + if (argc > 3) { + nfct_perror("too many arguments"); +@@ -122,35 +140,11 @@ int nfct_cmd_timeout_list(int argc, char *argv[]) + nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_GET, + NLM_F_DUMP, seq); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); +- return -1; +- } +- +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } + portid = mnl_socket_get_portid(nl); +- +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); +- return -1; +- } +- +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, nfct_timeout_cb, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } +- mnl_socket_close(nl); + + return 0; + } +@@ -167,69 +161,71 @@ static uint32_t nfct_timeout_attr_max[IPPROTO_MAX] = { + [IPPROTO_RAW] = NFCT_TIMEOUT_ATTR_GENERIC_MAX, + }; + +-int nfct_cmd_timeout_add(int argc, char *argv[]) ++static int nfct_cmd_get_l3proto(char *argv[]) + { +- struct mnl_socket *nl; +- char buf[MNL_SOCKET_BUFFER_SIZE]; +- struct nlmsghdr *nlh; +- uint32_t portid, seq; +- struct nfct_timeout *t; +- uint16_t l3proto; +- uint8_t l4proto; +- int ret, i; +- unsigned int j; +- +- 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; +- } ++ int l3proto; + +- nfct_timeout_attr_set(t, NFCT_TIMEOUT_ATTR_NAME, argv[3]); +- +- if (strcmp(argv[4], "inet") == 0) ++ if (strcmp(*argv, "inet") == 0) + l3proto = AF_INET; +- else if (strcmp(argv[4], "inet6") == 0) ++ else if (strcmp(*argv, "inet6") == 0) + l3proto = AF_INET6; + else { + nfct_perror("unknown layer 3 protocol"); + return -1; + } ++ return l3proto; ++} ++ ++static int nfct_cmd_get_l4proto(char *argv[]) ++{ ++ int l4proto; ++ struct protoent *pent; ++ ++ pent = getprotobyname(*argv); ++ if (!pent) { ++ /* In Debian, /etc/protocols says ipv6-icmp. Support icmpv6 ++ * as well not to break backward compatibility. ++ */ ++ if (strcmp(*argv, "icmpv6") == 0) ++ l4proto = IPPROTO_ICMPV6; ++ else if (strcmp(*argv, "generic") == 0) ++ l4proto = IPPROTO_RAW; ++ else { ++ nfct_perror("unknown layer 4 protocol"); ++ return -1; ++ } ++ } else ++ l4proto = pent->p_proto; ++ ++ return l4proto; ++} ++ ++static int ++nfct_cmd_timeout_parse(struct nfct_timeout *t, int argc, char *argv[]) ++{ ++ int l3proto, l4proto; ++ unsigned int j; ++ const char *proto_name; ++ ++ l3proto = nfct_cmd_get_l3proto(argv); ++ if (l3proto < 0) ++ return -1; ++ + nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO, l3proto); + +- if (strcmp(argv[5], "tcp") == 0) +- l4proto = IPPROTO_TCP; +- else if (strcmp(argv[5], "udp") == 0) +- l4proto = IPPROTO_UDP; +- else if (strcmp(argv[5], "udplite") == 0) +- l4proto = IPPROTO_UDPLITE; +- else if (strcmp(argv[5], "sctp") == 0) +- l4proto = IPPROTO_SCTP; +- else if (strcmp(argv[5], "dccp") == 0) +- l4proto = IPPROTO_DCCP; +- else if (strcmp(argv[5], "icmp") == 0) +- l4proto = IPPROTO_ICMP; +- else if (strcmp(argv[5], "icmpv6") == 0) +- l4proto = IPPROTO_ICMPV6; +- else if (strcmp(argv[5], "gre") == 0) +- l4proto = IPPROTO_GRE; +- else if (strcmp(argv[5], "generic") == 0) +- l4proto = IPPROTO_RAW; +- else { +- nfct_perror("unknown layer 4 protocol"); ++ argc--; ++ argv++; ++ proto_name = *argv; ++ ++ l4proto = nfct_cmd_get_l4proto(argv); ++ if (l4proto < 0) + return -1; +- } ++ + nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO, l4proto); ++ argc--; ++ argv++; + +- for (i=6; 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; ++ } + +- seq = time(NULL); +- nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_NEW, +- NLM_F_CREATE | NLM_F_ACK, seq); +- nfct_timeout_nlmsg_build_payload(nlh, t); ++ return 0; ++} + +- nfct_timeout_free(t); ++int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]) ++{ ++ char buf[MNL_SOCKET_BUFFER_SIZE]; ++ struct nlmsghdr *nlh; ++ uint32_t portid, seq; ++ struct nfct_timeout *t; + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); ++ if (argc < 6) { ++ nfct_perror("missing parameters\n" ++ "syntax: nfct timeout add name " ++ "family protocol state1 " ++ "timeout1 state2 timeout2..."); + return -1; + } + +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); ++ t = nfct_timeout_alloc(); ++ if (t == NULL) { ++ nfct_perror("OOM"); + return -1; + } +- portid = mnl_socket_get_portid(nl); + +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); ++ nfct_timeout_attr_set(t, NFCT_TIMEOUT_ATTR_NAME, argv[3]); ++ ++ if (nfct_cmd_timeout_parse(t, argc-4, &argv[4]) < 0) + return -1; +- } + +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); ++ seq = time(NULL); ++ nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_NEW, ++ NLM_F_CREATE | NLM_F_ACK, seq); ++ nfct_timeout_nlmsg_build_payload(nlh, t); ++ ++ nfct_timeout_free(t); ++ ++ portid = mnl_socket_get_portid(nl); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } +- mnl_socket_close(nl); + + return 0; + } + +-int nfct_cmd_timeout_delete(int argc, char *argv[]) ++int nfct_cmd_timeout_delete(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_timeout *t; +- int ret; + + if (argc < 4) { + nfct_perror("missing timeout policy name"); +@@ -335,48 +333,21 @@ int nfct_cmd_timeout_delete(int argc, char *argv[]) + + nfct_timeout_free(t); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); +- return -1; +- } +- +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } + portid = mnl_socket_get_portid(nl); +- +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); +- return -1; +- } +- +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } + +- mnl_socket_close(nl); +- + return 0; + } + +-int nfct_cmd_timeout_get(int argc, char *argv[]) ++int nfct_cmd_timeout_get(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; + struct nfct_timeout *t; +- int ret; + + if (argc < 4) { + nfct_perror("missing timeout policy name"); +@@ -401,86 +372,135 @@ int nfct_cmd_timeout_get(int argc, char *argv[]) + + nfct_timeout_free(t); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); ++ portid = mnl_socket_get_portid(nl); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } + +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); +- return -1; +- } +- portid = mnl_socket_get_portid(nl); ++ return 0; ++} ++ ++int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[]) ++{ ++ char buf[MNL_SOCKET_BUFFER_SIZE]; ++ struct nlmsghdr *nlh; ++ uint32_t portid, seq; + +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); ++ if (argc > 3) { ++ nfct_perror("too many arguments"); + return -1; + } + +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, nfct_timeout_cb, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); ++ seq = time(NULL); ++ nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DELETE, ++ NLM_F_ACK, seq); ++ ++ portid = mnl_socket_get_portid(nl); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, NULL, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } +- mnl_socket_close(nl); + + return 0; + } + +-int nfct_cmd_timeout_flush(int argc, char *argv[]) ++static int ++nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *argv[]) + { +- struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq; +- int ret; ++ struct nfct_timeout *t; + +- if (argc > 3) { +- nfct_perror("too many arguments"); ++ if (argc < 6) { ++ nfct_perror("missing parameters\n" ++ "syntax: nfct 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_DELETE, ++ nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DEFAULT_SET, + NLM_F_ACK, seq); ++ nfct_timeout_nlmsg_build_payload(nlh, t); ++ nfct_timeout_free(t); + +- nl = mnl_socket_open(NETLINK_NETFILTER); +- if (nl == NULL) { +- nfct_perror("mnl_socket_open"); ++ portid = mnl_socket_get_portid(nl); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { ++ nfct_perror("netlink error"); + return -1; + } + +- if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { +- nfct_perror("mnl_socket_bind"); ++ return 0; ++} ++ ++static int ++nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]) ++{ ++ char buf[MNL_SOCKET_BUFFER_SIZE]; ++ struct nlmsghdr *nlh; ++ uint32_t portid, seq; ++ struct nfct_timeout *t; ++ int l3proto, l4proto; ++ ++ if (argc < 5) { ++ nfct_perror("missing parameters\n" ++ "syntax: nfct timeout default-get " ++ "family protocol"); + return -1; + } +- portid = mnl_socket_get_portid(nl); + +- if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { +- nfct_perror("mnl_socket_send"); ++ t = nfct_timeout_alloc(); ++ if (t == NULL) + return -1; +- } + +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- while (ret > 0) { +- ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); +- if (ret <= 0) +- break; +- ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); +- } +- if (ret == -1) { +- nfct_perror("error"); ++ argc-=3; ++ argv+=3; ++ ++ l3proto = nfct_cmd_get_l3proto(argv); ++ if (l3proto < 0) + return -1; +- } + +- mnl_socket_close(nl); ++ nfct_timeout_attr_set_u16(t, NFCT_TIMEOUT_ATTR_L3PROTO, l3proto); ++ argc--; ++ argv++; ++ ++ l4proto = nfct_cmd_get_l4proto(argv); ++ if (l4proto < 0) ++ return -1; ++ ++ nfct_timeout_attr_set_u8(t, NFCT_TIMEOUT_ATTR_L4PROTO, l4proto); ++ ++ seq = time(NULL); ++ nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_DEFAULT_GET, ++ NLM_F_ACK, seq); ++ nfct_timeout_nlmsg_build_payload(nlh, t); ++ nfct_timeout_free(t); ++ ++ portid = mnl_socket_get_portid(nl); ++ if (nfct_mnl_talk(nl, nlh, seq, portid, nfct_timeout_cb, NULL) < 0) { ++ nfct_perror("netlink error"); ++ return -1; ++ } + + return 0; + } ++ ++static struct nfct_extension timeout = { ++ .type = NFCT_SUBSYS_TIMEOUT, ++ .parse_params = nfct_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..533d75d 100644 +--- a/src/nfct.c ++++ b/src/nfct.c +@@ -22,9 +22,8 @@ + #include + + #include +-#include +-#include + ++#include "linux_list.h" + #include "nfct.h" + + static int nfct_cmd_version(int argc, char *argv[]); +@@ -46,9 +45,29 @@ 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; ++ struct mnl_socket *nl; + + if (argc < 2) { + usage(argv); +@@ -70,18 +89,29 @@ 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; ++ } ++ ++ 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; + } +@@ -120,3 +150,42 @@ static int nfct_cmd_help(int argc, char *argv[]) + printf(help_msg, VERSION, argv[0]); + return 0; + } ++ ++int nfct_mnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, ++ uint32_t seq, uint32_t portid, ++ int (*cb)(const struct nlmsghdr *nlh, void *data), ++ void *data) ++{ ++ int ret; ++ char buf[MNL_SOCKET_BUFFER_SIZE]; ++ ++ if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) ++ return -1; ++ ++ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); ++ while (ret > 0) { ++ ret = mnl_cb_run(buf, ret, seq, portid, cb, data); ++ if (ret <= 0) ++ break; ++ ++ ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); ++ } ++ if (ret == -1) ++ return -1; ++ ++ return 0; ++} ++ ++struct mnl_socket *nfct_mnl_open(void) ++{ ++ struct mnl_socket *nl; ++ ++ nl = mnl_socket_open(NETLINK_NETFILTER); ++ if (nl == NULL) ++ return NULL; ++ ++ if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) ++ return NULL; ++ ++ return nl; ++} +diff --git a/src/read_config_lex.l b/src/read_config_lex.l +index b4d11d4..8350069 100644 +--- a/src/read_config_lex.l ++++ b/src/read_config_lex.l +@@ -47,7 +47,7 @@ ip6_part {hex_255}":"? + ip6_form1 {ip6_part}{0,7}"::"{ip6_part}{0,7} + ip6_form2 ({hex_255}":"){0,7}{hex_255} + ip6 {ip6_form1}{ip6_cidr}?|{ip6_form2}{ip6_cidr}? +-string [a-zA-Z][a-zA-Z0-9\.\-]* ++string [a-zA-Z][a-zA-Z0-9\.\-\_]* + persistent [P|p][E|e][R|r][S|s][I|i][S|s][T|t][E|e][N|n][T|T] + nack [N|n][A|a][C|c][K|k] + alarm [A|a][L|l][A|a][R|r][M|m] +diff --git a/src/read_config_yy.y b/src/read_config_yy.y +index b824150..73fabbf 100644 +--- a/src/read_config_yy.y ++++ b/src/read_config_yy.y +@@ -1612,12 +1612,17 @@ helper_type: T_TYPE T_STRING T_STRING T_STRING '{' helper_type_list '}' + exit(EXIT_FAILURE); + } + +- /* XXX use configure.ac definitions. */ +- helper = helper_find("/usr/lib/conntrack-tools", $2, l4proto, RTLD_NOW); ++#ifdef BUILD_CTHELPER ++ helper = helper_find(CONNTRACKD_LIB_DIR, $2, l4proto, RTLD_NOW); + if (helper == NULL) { + print_err(CTD_CFG_ERROR, "Unknown `%s' helper", $2); + exit(EXIT_FAILURE); + } ++#else ++ print_err(CTD_CFG_ERROR, "Helper support is disabled, recompile " ++ "conntrackd"); ++ exit(EXIT_FAILURE); ++#endif + + helper_inst = calloc(1, sizeof(struct ctd_helper_instance)); + if (helper_inst == NULL) +diff --git a/src/run.c b/src/run.c +index 7fa6889..a9d4862 100644 +--- a/src/run.c ++++ b/src/run.c +@@ -55,9 +55,10 @@ void killer(int signo) + if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) + ctnl_kill(); + ++#ifdef BUILD_CTHELPER + if (CONFIG(flags) & CTD_HELPER) + cthelper_kill(); +- ++#endif + destroy_fds(STATE(fds)); + unlink(CONFIG(lockfile)); + dlog(LOG_NOTICE, "---- shutdown received ----"); +@@ -205,9 +206,10 @@ static int local_handler(int fd, void *data) + if (CONFIG(flags) & (CTD_SYNC_MODE | CTD_STATS_MODE)) + return ctnl_local(fd, type, data); + ++#ifdef BUILD_CTHELPER + if (CONFIG(flags) & CTD_HELPER) + return cthelper_local(fd, type, data); +- ++#endif + return ret; + } + +@@ -259,11 +261,12 @@ init(void) + if (ctnl_init() < 0) + return -1; + ++#ifdef BUILD_CTHELPER + if (CONFIG(flags) & CTD_HELPER) { + if (cthelper_init() < 0) + return -1; + } +- ++#endif + time(&STATE(stats).daemon_start_time); + + dlog(LOG_NOTICE, "initialization completed"); +diff --git a/src/sync-notrack.c b/src/sync-notrack.c +index a7df4e7..c810bbb 100644 +--- a/src/sync-notrack.c ++++ b/src/sync-notrack.c +@@ -99,7 +99,7 @@ static int kernel_resync_cb(enum nf_conntrack_msg_type type, + static void kernel_resync(void) + { + struct nfct_handle *h; +- u_int32_t family = AF_UNSPEC; ++ uint32_t family = AF_UNSPEC; + int ret; + + h = nfct_open(CONFIG(netlink).subsys_id, 0); +diff --git a/src/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; diff --git a/SOURCES/conntrack-tools-coverity20150818.patch b/SOURCES/conntrack-tools-coverity20150818.patch new file mode 100644 index 0000000..ad306bb --- /dev/null +++ b/SOURCES/conntrack-tools-coverity20150818.patch @@ -0,0 +1,217 @@ +diff --git a/src/cache.c b/src/cache.c +index 7c41e54..79a024f 100644 +--- a/src/cache.c ++++ b/src/cache.c +@@ -34,7 +34,7 @@ struct cache_feature *cache_feature[CACHE_MAX_FEATURE] = { + }; + + struct cache *cache_create(const char *name, enum cache_type type, +- unsigned int features, ++ unsigned int features, + struct cache_extra *extra, + struct cache_ops *ops) + { +@@ -53,7 +53,8 @@ struct cache *cache_create(const char *name, enum cache_type type, + return NULL; + memset(c, 0, sizeof(struct cache)); + +- strcpy(c->name, name); ++ strncpy(c->name, name, CACHE_MAX_NAMELEN); ++ c->name[CACHE_MAX_NAMELEN - 1] = '\0'; + c->type = type; + + for (i = 0; i < CACHE_MAX_FEATURE; i++) { +diff --git a/src/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 453799a..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; +@@ -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; + } +diff --git a/src/parse.c b/src/parse.c +index f3ec6ac..919d36c 100644 +--- a/src/parse.c ++++ b/src/parse.c +@@ -297,7 +297,7 @@ int msg2ct(struct nf_conntrack *ct, struct nethdr *net, size_t remain) + return -1; + if (attr->nta_len < NTA_LENGTH(0)) + return -1; +- if (attr->nta_attr > NTA_MAX) ++ if (attr->nta_attr >= NTA_MAX) + return -1; + if (h[attr->nta_attr].size && + attr->nta_len != h[attr->nta_attr].size) +@@ -510,7 +510,7 @@ int msg2exp(struct nf_expect *exp, struct nethdr *net, size_t remain) + ATTR_NETWORK2HOST(attr); + if (attr->nta_len > len) + goto err; +- if (attr->nta_attr > NTA_MAX) ++ if (attr->nta_attr >= NTA_EXP_MAX) + goto err; + if (attr->nta_len < NTA_LENGTH(0)) + goto err; +@@ -524,13 +524,15 @@ int msg2exp(struct nf_expect *exp, struct nethdr *net, size_t remain) + attr = NTA_NEXT(attr, len); + continue; + } +- switch(exp_h[attr->nta_attr].exp_attr) { ++ switch (exp_h[attr->nta_attr].exp_attr) { + case ATTR_EXP_MASTER: + exp_h[attr->nta_attr].parse(master, attr->nta_attr, + NTA_DATA(attr)); ++ break; + case ATTR_EXP_EXPECTED: + exp_h[attr->nta_attr].parse(expected, attr->nta_attr, + NTA_DATA(attr)); ++ break; + case ATTR_EXP_MASK: + exp_h[attr->nta_attr].parse(mask, attr->nta_attr, + NTA_DATA(attr)); +diff --git a/src/process.c b/src/process.c +index 7f0a395..3ddad5f 100644 +--- a/src/process.c ++++ b/src/process.c +@@ -48,6 +48,8 @@ int fork_process_new(int type, int flags, void (*cb)(void *data), void *data) + + if (c->pid > 0) + list_add(&c->head, &process_list); ++ else ++ free(c); + + return pid; + } +diff --git a/src/tcp.c b/src/tcp.c +index af27c46..e570880 100644 +--- a/src/tcp.c ++++ b/src/tcp.c +@@ -247,13 +247,11 @@ int tcp_accept(struct tcp_sock *m) + /* the other peer wants to connect ... */ + ret = accept(m->fd, NULL, NULL); + if (ret == -1) { +- if (errno != EAGAIN) { +- /* unexpected error. Give us another try. */ +- m->state = TCP_SERVER_ACCEPTING; +- } else { +- /* waiting for new connections. */ +- m->state = TCP_SERVER_ACCEPTING; +- } ++ /* unexpected error: Give us another try. Or we have hit ++ * -EAGAIN, in that case we remain in the accepting connections ++ * state. ++ */ ++ m->state = TCP_SERVER_ACCEPTING; + } else { + /* the peer finally got connected. */ + if (fcntl(ret, F_SETFL, O_NONBLOCK) == -1) { diff --git a/SOURCES/conntrackd.conf b/SOURCES/conntrackd.conf new file mode 100644 index 0000000..3970e91 --- /dev/null +++ b/SOURCES/conntrackd.conf @@ -0,0 +1,419 @@ + +# See also: http://conntrack-tools.netfilter.org/support.html +# +# There are 3 different modes of running conntrackd: "alarm", "notrack" and "ftfw" +# +# The default package ships with a FTFW configuration, see /usr/share/doc/conntrackd* +# for example configurations for other modes. + + +# +# Synchronizer settings +# +Sync { + Mode FTFW { + # + # Size of the resend queue (in objects). This is the maximum + # number of objects that can be stored waiting to be confirmed + # via acknoledgment. If you keep this value low, the daemon + # will have less chances to recover state-changes under message + # omission. On the other hand, if you keep this value high, + # the daemon will consume more memory to store dead objects. + # Default is 131072 objects. + # + # ResendQueueSize 131072 + + # + # This parameter allows you to set an initial fixed timeout + # for the committed entries when this node goes from backup + # to primary. This mechanism provides a way to purge entries + # that were not recovered appropriately after the specified + # fixed timeout. If you set a low value, TCP entries in + # Established states with no traffic may hang. For example, + # an SSH connection without KeepAlive enabled. If not set, + # the daemon uses an approximate timeout value calculation + # mechanism. By default, this option is not set. + # + # CommitTimeout 180 + + # + # If the firewall replica goes from primary to backup, + # the conntrackd -t command is invoked in the script. + # This command schedules a flush of the table in N seconds. + # This is useful to purge the connection tracking table of + # zombie entries and avoid clashes with old entries if you + # trigger several consecutive hand-overs. Default is 60 seconds. + # + # PurgeTimeout 60 + + # Set the acknowledgement window size. If you decrease this + # value, the number of acknowlegdments increases. More + # acknowledgments means more overhead as conntrackd has to + # handle more control messages. On the other hand, if you + # increase this value, the resend queue gets more populated. + # This results in more overhead in the queue releasing. + # The following value is based on some practical experiments + # measuring the cycles spent by the acknowledgment handling + # with oprofile. If not set, default window size is 300. + # + # ACKWindowSize 300 + + # + # This clause allows you to disable the external cache. Thus, + # the state entries are directly injected into the kernel + # conntrack table. As a result, you save memory in user-space + # but you consume slots in the kernel conntrack table for + # backup state entries. Moreover, disabling the external cache + # means more CPU consumption. You need a Linux kernel + # >= 2.6.29 to use this feature. By default, this clause is + # set off. If you are installing conntrackd for first time, + # please read the user manual and I encourage you to consider + # using the fail-over scripts instead of enabling this option! + # + # DisableExternalCache Off + } + + # + # Multicast IP and interface where messages are + # broadcasted (dedicated link). IMPORTANT: Make sure + # that iptables accepts traffic for destination + # 225.0.0.50, eg: + # + # iptables -I INPUT -d 225.0.0.50 -j ACCEPT + # iptables -I OUTPUT -d 225.0.0.50 -j ACCEPT + # + Multicast { + # + # Multicast address: The address that you use as destination + # in the synchronization messages. You do not have to add + # this IP to any of your existing interfaces. If any doubt, + # do not modify this value. + # + IPv4_address 225.0.0.50 + + # + # The multicast group that identifies the cluster. If any + # doubt, do not modify this value. + # + Group 3780 + + # + # IP address of the interface that you are going to use to + # send the synchronization messages. Remember that you must + # use a dedicated link for the synchronization messages. + # + IPv4_interface 192.168.100.100 + + # + # The name of the interface that you are going to use to + # send the synchronization messages. + # + Interface eth2 + + # The multicast sender uses a buffer to enqueue the packets + # that are going to be transmitted. The default size of this + # socket buffer is available at /proc/sys/net/core/wmem_default. + # This value determines the chances to have an overrun in the + # sender queue. The overrun results packet loss, thus, losing + # state information that would have to be retransmitted. If you + # notice some packet loss, you may want to increase the size + # of the sender buffer. The default size is usually around + # ~100 KBytes which is fairly small for busy firewalls. + # + SndSocketBuffer 1249280 + + # The multicast receiver uses a buffer to enqueue the packets + # that the socket is pending to handle. The default size of this + # socket buffer is available at /proc/sys/net/core/rmem_default. + # This value determines the chances to have an overrun in the + # receiver queue. The overrun results packet loss, thus, losing + # state information that would have to be retransmitted. If you + # notice some packet loss, you may want to increase the size of + # the receiver buffer. The default size is usually around + # ~100 KBytes which is fairly small for busy firewalls. + # + RcvSocketBuffer 1249280 + + # + # Enable/Disable message checksumming. This is a good + # property to achieve fault-tolerance. In case of doubt, do + # not modify this value. + # + Checksum on + } + # + # You can specify more than one dedicated link. Thus, if one dedicated + # link fails, conntrackd can fail-over to another. Note that adding + # more than one dedicated link does not mean that state-updates will + # be sent to all of them. There is only one active dedicated link at + # a given moment. The `Default' keyword indicates that this interface + # will be selected as the initial dedicated link. You can have + # up to 4 redundant dedicated links. Note: Use different multicast + # groups for every redundant link. + # + # Multicast Default { + # IPv4_address 225.0.0.51 + # Group 3781 + # IPv4_interface 192.168.100.101 + # Interface eth3 + # # SndSocketBuffer 1249280 + # # RcvSocketBuffer 1249280 + # Checksum on + # } + + # + # You can use Unicast UDP instead of Multicast to propagate events. + # Note that you cannot use unicast UDP and Multicast at the same + # time, you can only select one. + # + # UDP { + # + # UDP address that this firewall uses to listen to events. + # + # IPv4_address 192.168.2.100 + # + # or you may want to use an IPv6 address: + # + # IPv6_address fe80::215:58ff:fe28:5a27 + + # + # Destination UDP address that receives events, ie. the other + # firewall's dedicated link address. + # + # IPv4_Destination_Address 192.168.2.101 + # + # or you may want to use an IPv6 address: + # + # IPv6_Destination_Address fe80::2d0:59ff:fe2a:775c + + # + # UDP port used + # + # Port 3780 + + # + # The name of the interface that you are going to use to + # send the synchronization messages. + # + # Interface eth2 + + # + # The sender socket buffer size + # + # SndSocketBuffer 1249280 + + # + # The receiver socket buffer size + # + # RcvSocketBuffer 1249280 + + # + # Enable/Disable message checksumming. + # + # Checksum on + # } + + # + # Other unsorted options that are related to the synchronization. + # + # Options { + # + # TCP state-entries have window tracking disabled by default, + # you can enable it with this option. As said, default is off. + # This feature requires a Linux kernel >= 2.6.36. + # + # TCPWindowTracking Off + # } +} + +# +# 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 + # } + + # + # Number of buckets in the cache hashtable. The bigger it is, + # the closer it gets to O(1) at the cost of consuming more memory. + # Read some documents about tuning hashtables for further reference. + # + HashSize 32768 + + # + # Maximum number of conntracks, it should be double of: + # $ cat /proc/sys/net/netfilter/nf_conntrack_max + # since the daemon may keep some dead entries cached for possible + # retransmission during state synchronization. + # + HashLimit 131072 + + # + # 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 + } + + # + # Netlink event socket buffer size. If you do not specify this clause, + # the default buffer size value in /proc/net/core/rmem_default is + # used. This default value is usually around 100 Kbytes which is + # fairly small for busy firewalls. This leads to event message dropping + # and high CPU consumption. This example configuration file sets the + # size to 2 MBytes to avoid this sort of problems. + # + NetlinkBufferSize 2097152 + + # + # The daemon doubles the size of the netlink event socket buffer size + # if it detects netlink event message dropping. This clause sets the + # maximum buffer size growth that can be reached. This example file + # sets the size to 8 MBytes. + # + NetlinkBufferSizeMaxGrowth 8388608 + + # + # If the daemon detects that Netlink is dropping state-change events, + # it automatically schedules a resynchronization against the Kernel + # after 30 seconds (default value). Resynchronizations are expensive + # in terms of CPU consumption since the daemon has to get the full + # kernel state-table and purge state-entries that do not exist anymore. + # Be careful of setting a very small value here. You have the following + # choices: On (enabled, use default 30 seconds value), Off (disabled) + # or Value (in seconds, to set a specific amount of time). If not + # specified, the daemon assumes that this option is enabled. + # + # NetlinkOverrunResync On + + # + # If you want reliable event reporting over Netlink, set on this + # option. If you set on this clause, it is a good idea to set off + # NetlinkOverrunResync. This option is off by default and you need + # a Linux kernel >= 2.6.31. + # + # NetlinkEventsReliable Off + + # + # By default, the daemon receives state updates following an + # event-driven model. You can modify this behaviour by switching to + # polling mode with the PollSecs clause. This clause tells conntrackd + # to dump the states in the kernel every N seconds. With regards to + # synchronization mode, the polling mode can only guarantee that + # long-lifetime states are recovered. The main advantage of this method + # is the reduction in the state replication at the cost of reducing the + # chances of recovering connections. + # + # PollSecs 15 + + # + # The daemon prioritizes the handling of state-change events coming + # from the core. With this clause, you can set the maximum number of + # state-change events (those coming from kernel-space) that the daemon + # will handle after which it will handle other events coming from the + # network or userspace. A low value improves interactivity (in terms of + # real-time behaviour) at the cost of extra CPU consumption. + # Default (if not set) is 100. + # + # EventIterationLimit 100 + + # + # Event filtering: This clause allows you to filter certain traffic, + # There are currently three filter-sets: Protocol, Address and + # State. The filter is attached to an action that can be: Accept or + # Ignore. Thus, you can define the event filtering policy of the + # filter-sets in positive or negative logic depending on your needs. + # You can select if conntrackd filters the event messages from + # user-space or kernel-space. The kernel-space event filtering + # saves some CPU cycles by avoiding the copy of the event message + # from kernel-space to user-space. The kernel-space event filtering + # is prefered, however, you require a Linux kernel >= 2.6.29 to + # filter from kernel-space. If you want to select kernel-space + # event filtering, use the keyword 'Kernelspace' instead of + # 'Userspace'. + # + Filter From Userspace { + # + # Accept only certain protocols: You may want to replicate + # the state of flows depending on their layer 4 protocol. + # + Protocol Accept { + TCP + SCTP + DCCP + # UDP + # ICMP # This requires a Linux kernel >= 2.6.31 + # IPv6-ICMP # This requires a Linux kernel >= 2.6.31 + } + + # + # Ignore traffic for a certain set of IP's: Usually all the + # IP assigned to the firewall since local traffic must be + # ignored, only forwarded connections are worth to replicate. + # Note that these values depends on the local IPs that are + # assigned to the firewall. + # + Address Ignore { + IPv4_address 127.0.0.1 # loopback + IPv4_address 192.168.0.100 # virtual IP 1 + IPv4_address 192.168.1.100 # virtual IP 2 + IPv4_address 192.168.0.1 + IPv4_address 192.168.1.1 + IPv4_address 192.168.100.100 # dedicated link ip + # + # You can also specify networks in format IP/cidr. + # IPv4_address 192.168.0.0/24 + # + # You can also specify an IPv6 address + # IPv6_address ::1 + } + + # + # Uncomment this line below if you want to filter by flow state. + # This option introduces a trade-off in the replication: it + # reduces CPU consumption at the cost of having lazy backup + # firewall replicas. The existing TCP states are: SYN_SENT, + # SYN_RECV, ESTABLISHED, FIN_WAIT, CLOSE_WAIT, LAST_ACK, + # TIME_WAIT, CLOSED, LISTEN. + # + # State Accept { + # ESTABLISHED CLOSED TIME_WAIT CLOSE_WAIT for TCP + # } + } +} diff --git a/SOURCES/conntrackd.service b/SOURCES/conntrackd.service new file mode 100644 index 0000000..95a360b --- /dev/null +++ b/SOURCES/conntrackd.service @@ -0,0 +1,13 @@ +[Unit] +Description=connection tracking daemon for debugging and High Availablity +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +# See rhbz#1255578 - daemon will not start if lock file is left dangling +ExecStartPre=/bin/rm -f /var/lock/conntrack.lock +ExecStart=/usr/sbin/conntrackd -C /etc/conntrackd/conntrackd.conf + +[Install] +WantedBy=multi-user.target diff --git a/SPECS/conntrack-tools.spec b/SPECS/conntrack-tools.spec new file mode 100644 index 0000000..9c252c3 --- /dev/null +++ b/SPECS/conntrack-tools.spec @@ -0,0 +1,93 @@ +Name: conntrack-tools +Version: 1.4.2 +Release: 9%{?dist} +Summary: Manipulate netfilter connection tracking table and run High Availability +Group: System Environment/Base +License: GPLv2 +URL: http://netfilter.org +Source0: http://netfilter.org/projects/%{name}/files/%{name}-%{version}.tar.bz2 +Source1: conntrackd.service +Source2: conntrackd.conf +Patch1: conntrack-tools-20150818.patch +Patch2: conntrack-tools-coverity20150818.patch +BuildRequires: libnfnetlink-devel >= 1.0.1, libnetfilter_conntrack-devel >= 1.0.4 +BuildRequires: libnetfilter_cttimeout-devel >= 1.0.0, libnetfilter_cthelper-devel >= 1.0.0 +BuildRequires: libmnl-devel >= 1.0.3, libnetfilter_queue-devel >= 1.0.2 +BuildRequires: pkgconfig bison flex +Provides: conntrack = 1.0-1 +Obsoletes: conntrack < 1.0-1 +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +BuildRequires: systemd + +%description +With conntrack-tools you can setup a High Availability cluster and +synchronize conntrack state between multiple firewalls. + +The conntrack-tools package contains two programs: +- conntrack: the command line interface to interact with the connection + tracking system. +- conntrackd: the connection tracking userspace daemon that can be used to + deploy highly available GNU/Linux firewalls and collect + statistics of the firewall use. + +conntrack is used to search, list, inspect and maintain the netfilter +connection tracking subsystem of the Linux kernel. +Using conntrack, you can dump a list of all (or a filtered selection of) +currently tracked connections, delete connections from the state table, +and even add new ones. +In addition, you can also monitor connection tracking events, e.g. +show an event message (one line) per newly established connection. + +%prep +%setup -q +%patch1 -p1 +%patch2 -p1 + +%build +%configure --disable-static +%{__make} %{?_smp_mflags} +chmod 644 doc/sync/primary-backup.sh +rm -f doc/sync/notrack/conntrackd.conf.orig doc/sync/alarm/conntrackd.conf.orig doc/helper/conntrackd.conf.orig + +%install +%{__make} install DESTDIR=%{buildroot} +find $RPM_BUILD_ROOT -type f -name "*.la" -exec rm -f {} ';' +mkdir -p %{buildroot}%{_sysconfdir}/conntrackd +install -d 0755 %{buildroot}%{_unitdir} +install -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/ +install -m 0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/conntrackd/ + +%files +%doc COPYING AUTHORS TODO doc +%dir %{_sysconfdir}/conntrackd +%config(noreplace) %{_sysconfdir}/conntrackd/conntrackd.conf +%{_unitdir}/conntrackd.service +%{_sbindir}/conntrack +%{_sbindir}/conntrackd +%{_sbindir}/nfct +%{_mandir}/man8/* +%dir %{_libdir}/conntrack-tools +%{_libdir}/conntrack-tools/* + +%post +%systemd_post conntrackd.service + +%preun +%systemd_preun conntrackd.service + +%postun +%systemd_postun conntrackd.service + +%changelog +* Fri Aug 21 2015 Paul Wouters - 1.4.2-9 +- Resolves: rhbz#1255578 conntrackd could neither be started nor be stopped + +* Tue Aug 18 2015 Paul Wouters - 1.4.2-8 +- Resolves: rhbz#CVE-2015-6496 +- Fold in upstream patches since 1.4.2 release up to git 900d7e8 +- Fold in upstream patch set of 2015-08-18 for coverity issues + +* Thu May 21 2015 Paul Wouters - 1.4.2-7 +- Resolves: rhbz#1122611 [BNE] Add conntrack-tools package to RHEL-7