Blob Blame History Raw
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 <laforge@netfilter.org (Jun 2005)
 .\" Maintained by Pablo Neira Ayuso <pablo@netfilter.org (May 2007)
@@ -86,7 +86,7 @@ Show the in-kernel connection tracking system statistics.
 .TP
 .BI "-z, --zero "
 Atomically zero counters after reading them.  This option is only valid in
-combination with the "-L, --dump" command options.
+combination with the "\-L, \-\-dump" command options.
 .TP
 .BI "-o, --output [extended,xml,timestamp,id,ktimestamp,labels] "
 Display output in a certain format. With the extended output option, this tool
@@ -101,7 +101,7 @@ Set the bitmask of events that are to be generated by the in-kernel ctnetlink
 event code.  Using this parameter, you can reduce the event messages generated
 by the kernel to those types to those that you are actually interested in.
 .
-This option can only be used in conjunction with "-E, --event".
+This option can only be used in conjunction with "\-E, \-\-event".
 .TP
 .BI "-b, --buffer-size " "value (in bytes)"
 Set the Netlink socket buffer size. This option is useful if the command line
@@ -112,7 +112,7 @@ other words, if the amount of events are big enough to overrun the socket
 buffer. Note that using a big buffer reduces the chances to hit ENOBUFS, 
 however, this results in more memory consumption.
 .
-This option can only be used in conjunction with "-E, --event".
+This option can only be used in conjunction with "\-E, \-\-event".
 .SS FILTER PARAMETERS
 .TP
 .BI "-s, --orig-src " IP_ADDRESS
@@ -132,22 +132,31 @@ Specify layer four (TCP, UDP, ...) protocol.
 .TP
 .BI "-f, --family " "PROTO"
 Specify layer three (ipv4, ipv6) protocol
-This option is only required in conjunction with "-L, --dump". If this option is not passed, the default layer 3 protocol will be IPv4.
+This option is only required in conjunction with "\-L, \-\-dump". If this option is not passed, the default layer 3 protocol will be IPv4.
 .TP
 .BI "-t, --timeout " "TIMEOUT"
 Specify the timeout.
 .TP
 .BI "-m, --mark " "MARK[/MASK]"
 Specify the conntrack mark.  Optionally, a mask value can be specified.
-In "--update" mode, this mask specifies the bits that should be zeroed before XORing
+In "\-\-update" mode, this mask specifies the bits that should be zeroed before XORing
 the MARK value into the ctmark.
 Otherwise, the mask is logically ANDed with the existing mark before the comparision.
-In "--create" mode, the mask is ignored.
+In "\-\-create" mode, the mask is ignored.
 .TP
-.BI "-l, --label " "LABEL,..."
-Specify the conntrack labels.
-This option is only available in conjunction with "-L, --dump" or "-E, --event".
+.BI "-l, --label " "LABEL"
+Specify a conntrack label.
+This option is only available in conjunction with "\-L, \-\-dump", "\-E, \-\-event", or "\-U \-\-update".
+Match entries whose labels match at least those specified.
+Use multiple \-l commands to specify multiple labels that need to be set.
 Match entries whose labels matches at least those specified as arguments.
+.BI "--label-add " "LABEL"
+Specify the conntrack label to add to to the selected conntracks.
+This option is only available in conjunction with "\-I, \-\-create" or "\-U, \-\-update".
+.BI "--label-del " "[LABEL]"
+Specify the conntrack label to delete from the selected conntracks.
+If no label is given, all labels are deleted.
+This option is only available in conjunction with "\-U, \-\-update".
 .TP
 .BI "-c, --secmark " "SECMARK"
 Specify the conntrack selinux security mark.
diff --git a/conntrackd.8 b/conntrackd.8
index 131a7ac..455f6c5 100644
--- a/conntrackd.8
+++ b/conntrackd.8
@@ -1,4 +1,4 @@
-.TH CONNTRACKD 8 "Oct 21, 2008" "" ""
+.TH CONNTRACKD 8 "Sep 25, 2014" "" ""
 
 .\" Man page written by Pablo Neira Ayuso <pablo@netfilter.org> (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.</para></listitem>
 <listitem><para>Oracle*TNS, to support its special <emphasis>Redirect</emphasis> message.</para></listitem>
 <listitem><para>NFSv3, mind that version 4 does not require this helper.</para></listitem>
 <listitem><para>FTP (this helper is also available in kernel-space).</para></listitem>
+<listitem><para>SSDP.</para></listitem>
 </itemizedlist>
 
 <para>The following steps describe how to enable the RPC portmapper helper for NFSv3 (this is similar for other helpers):</para>
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 <stdlib.h>
+#include <stdint.h>
 
-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 <netinet/in.h>
 
 #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 <linux/netlink.h>
-#include <linux/capability.h>
-#include <net/netlink.h>
-
-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 <stdint.h>
 #include <netinet/in.h>
 #include <net/if.h>
+#include <sys/select.h>
 
 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 <stdint.h>
 #include <netinet/in.h>
+#include <sys/select.h>
 
 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 <stdint.h>
 #include <netinet/in.h>
+#include <sys/select.h>
 
 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 <netinet/in.h>
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
+#define _GNU_SOURCE
 #include <netinet/tcp.h>
 #include <netinet/udp.h>
 #include <net/ethernet.h>
@@ -182,6 +183,15 @@ pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct,
 	nfct_nlmsg_build(nlh, myct->ct);
 	mnl_attr_nest_end(nlh, nest);
 
+	if (myct->exp) {
+		nest = mnl_attr_nest_start(nlh, NFQA_EXP);
+		if (nest == NULL)
+			return -1;
+
+		nfexp_nlmsg_build(nlh, myct->exp);
+		mnl_attr_nest_end(nlh, nest);
+	}
+
 	if (mnl_socket_sendto(STATE_CTH(nl), nlh, nlh->nlmsg_len) < 0) {
 		dlog(LOG_ERR, "failed to send verdict: %s", strerror(errno));
 		return -1;
@@ -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 <pablo@netfilter.org>
+ *
+ * Adapted from:
+ *
+ * Amanda extension for IP connection tracking
+ *
+ * (C) 2002 by Brian J. Murrell <netfilter@interlinx.bc.ca>
+ * based on HW's ip_conntrack_irc.c as well as other modules
+ * (C) 2006 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_udp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+static int nat_amanda(struct pkt_buff *pkt, uint32_t ctinfo,
+		      unsigned int matchoff, unsigned int matchlen,
+		      struct nf_expect *exp)
+{
+	char buffer[sizeof("65535")];
+	uint16_t port, initial_port;
+	unsigned int ret;
+	const struct nf_conntrack *expected;
+	struct nf_conntrack *nat_tuple;
+
+	nat_tuple = nfct_new();
+	if (nat_tuple == NULL)
+		return NF_ACCEPT;
+
+	expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED);
+
+	/* Connection comes from client. */
+	initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST);
+	nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, IP_CT_DIR_ORIGINAL);
+
+	/* libnetfilter_conntrack needs this */
+	nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET);
+	nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0);
+	nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0);
+	nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP);
+	nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0);
+
+	/* When you see the packet, we need to NAT it the same as the
+	 * this one (ie. same IP: it will be TCP and master is UDP). */
+	nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master");
+
+	/* Try to get same port: if not, try to change it. */
+	for (port = ntohs(initial_port); port != 0; port++) {
+		int res;
+
+		nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port));
+		nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple);
+
+		res = cthelper_add_expect(exp);
+		if (res == 0)
+			break;
+		else if (res != -EBUSY) {
+			port = 0;
+			break;
+		}
+	}
+
+	if (port == 0) {
+		pr_debug("all ports in use\n");
+		return NF_DROP;
+	}
+
+	sprintf(buffer, "%u", port);
+	ret = nfq_udp_mangle_ipv4(pkt, matchoff, matchlen, buffer,
+				  strlen(buffer));
+	if (ret != NF_ACCEPT) {
+		pr_debug("cannot mangle packet\n");
+		cthelper_del_expect(exp);
+	}
+	return ret;
+}
+
+static char amanda_buffer[65536];
+static unsigned int master_timeout = 300;
+
+enum amanda_strings {
+	SEARCH_CONNECT,
+	SEARCH_NEWLINE,
+	SEARCH_DATA,
+	SEARCH_MESG,
+	SEARCH_INDEX,
+};
+
+static const char *conns[] = { "DATA ", "MESG ", "INDEX " };
+
+static int
+amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+		 struct myct *myct, uint32_t ctinfo)
+{
+	struct nf_expect *exp;
+	char *data, *data_limit, *tmp;
+	unsigned int dataoff, i;
+	uint16_t port, len;
+	int ret = NF_ACCEPT;
+	struct iphdr *iph;
+	union nfct_attr_grp_addr saddr, daddr;
+
+	/* Only look at packets from the Amanda server */
+	if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL)
+		return NF_ACCEPT;
+
+	/* increase the UDP timeout of the master connection as replies from
+	 * Amanda clients to the server can be quite delayed */
+	nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, master_timeout);
+
+	/* No data? */
+	iph = (struct iphdr *)pktb_network_header(pkt);
+	dataoff = iph->ihl*4 + sizeof(struct udphdr);
+	if (dataoff >= pktb_len(pkt)) {
+		pr_debug("amanda_help: pktlen = %u\n", pktb_len(pkt));
+		return NF_ACCEPT;
+	}
+
+	memcpy(amanda_buffer, pktb_network_header(pkt) + dataoff,
+	       pktb_len(pkt) - dataoff);
+	data = amanda_buffer;
+	data_limit = amanda_buffer + pktb_len(pkt) - dataoff;
+	*data_limit = '\0';
+
+	/* Search for the CONNECT string */
+	data = strstr(data, "CONNECT ");
+	if (!data)
+		goto out;
+	data += strlen("CONNECT ");
+
+	/* Only search first line. */
+	if ((tmp = strchr(data, '\n')))
+		*tmp = '\0';
+
+	for (i = 0; i < ARRAY_SIZE(conns); i++) {
+		char *match = strstr(data, conns[i]);
+		if (!match)
+			continue;
+		tmp = data = match + strlen(conns[i]);
+		port = strtoul(data, &data, 10);
+		len = data - tmp;
+		if (port == 0 || len > 5)
+			break;
+
+		exp = nfexp_new();
+		if (exp == NULL)
+			return NF_DROP;
+
+		cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr);
+		cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr);
+		cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port);
+
+		if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
+					 IPPROTO_TCP, NULL, &port, 0)) {
+			nfexp_destroy(exp);
+			return NF_DROP;
+		}
+
+		if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) {
+			ret = nat_amanda(pkt, ctinfo, tmp - amanda_buffer,
+					 len, exp);
+		} else
+			myct->exp = exp;
+	}
+out:
+	return ret;
+}
+
+static struct ctd_helper amanda_helper = {
+	.name		= "amanda",
+	.l4proto	= IPPROTO_UDP,
+	.cb		= amanda_helper_cb,
+	.policy		= {
+		[0] = {
+			.name			= "amanda",
+			.expect_max		= ARRAY_SIZE(conns),
+			.expect_timeout		= 180,
+		},
+	},
+};
+
+void __attribute__ ((constructor)) amanda_init(void);
+
+void amanda_init(void)
+{
+	helper_register(&amanda_helper);
+}
diff --git a/src/helpers/dhcpv6.c b/src/helpers/dhcpv6.c
new file mode 100644
index 0000000..73632ec
--- /dev/null
+++ b/src/helpers/dhcpv6.c
@@ -0,0 +1,123 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Adapted from:
+ *
+ * DHCPv6 multicast connection tracking helper.
+ *
+ * (c) 2012 Google Inc.
+ *
+ * Original author: Darren Willis <djw@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_udp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+#define DHCPV6_CLIENT_PORT 546
+
+static uint16_t dhcpv6_port;
+
+/* Timeouts for DHCPv6 replies, in seconds, indexed by message type. */
+static const int dhcpv6_timeouts[] = {
+	0,	/* No message has type 0. */
+	120,	/* Solicit. */
+	0,	/* Advertise. */
+	30,	/* Request. */
+	4,	/* Confirm. */
+	600,	/* Renew. */
+	600,	/* Rebind. */
+	0,	/* Reply. */
+	1,	/* Release. */
+	1,	/* Decline. */
+	0,	/* Reconfigure. */
+	120,	/* Information Request. */
+	0,	/* Relay-forward. */
+	0	/* Relay-reply. */
+};
+
+static inline int ipv6_addr_is_multicast(const struct in6_addr *addr)
+{
+	return (addr->s6_addr32[0] & htonl(0xFF000000)) == htonl(0xFF000000);
+}
+
+static int
+dhcpv6_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+		 struct myct *myct, uint32_t ctinfo)
+{
+	struct iphdr *iph = (struct iphdr *)pktb_network_header(pkt);
+	struct ip6_hdr *ip6h = (struct ip6_hdr *)pktb_network_header(pkt);
+	int dir = CTINFO2DIR(ctinfo);
+	union nfct_attr_grp_addr addr;
+	struct nf_expect *exp;
+	uint8_t *dhcpv6_msg_type;
+
+	if (iph->version != 6 || !ipv6_addr_is_multicast(&ip6h->ip6_dst))
+		return NF_ACCEPT;
+
+	dhcpv6_msg_type = pktb_network_header(pkt) + protoff + sizeof(struct udphdr);
+	if (*dhcpv6_msg_type > ARRAY_SIZE(dhcpv6_timeouts)) {
+		printf("Dropping DHCPv6 message with bad type %u\n",
+			*dhcpv6_msg_type);
+		return NF_DROP;
+	}
+
+	exp = nfexp_new();
+	if (exp == NULL)
+		return NF_ACCEPT;
+
+	cthelper_get_addr_src(myct->ct, dir, &addr);
+
+	if (cthelper_expect_init(exp, myct->ct, 0, NULL, &addr,
+				 IPPROTO_UDP, NULL, &dhcpv6_port,
+				 NF_CT_EXPECT_PERMANENT)) {
+		nfexp_destroy(exp);
+		return NF_DROP;
+	}
+
+	myct->exp = exp;
+
+	if (dhcpv6_timeouts[*dhcpv6_msg_type] > 0) {
+		nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT,
+				  dhcpv6_timeouts[*dhcpv6_msg_type]);
+	}
+
+	return NF_ACCEPT;
+}
+
+static struct ctd_helper dhcpv6_helper = {
+	.name		= "dhcpv6",
+	.l4proto	= IPPROTO_UDP,
+	.cb		= dhcpv6_helper_cb,
+	.policy		= {
+		[0] = {
+			.name			= "dhcpv6",
+			.expect_max		= 1,
+			.expect_timeout		= 300,
+		},
+	},
+};
+
+void __attribute__ ((constructor)) dhcpv6_init(void);
+
+void dhcpv6_init(void)
+{
+	dhcpv6_port = htons(DHCPV6_CLIENT_PORT);
+	helper_register(&dhcpv6_helper);
+}
diff --git a/src/helpers/ftp.c b/src/helpers/ftp.c
index 2c8dcd6..24ee877 100644
--- a/src/helpers/ftp.c
+++ b/src/helpers/ftp.c
@@ -25,6 +25,7 @@
 #include <ctype.h>	/* for isdigit */
 #include <errno.h>
 
+#define _GNU_SOURCE
 #include <netinet/tcp.h>
 
 #include <libmnl/libmnl.h>
@@ -58,7 +59,7 @@ enum nf_ct_ftp_type {
 };
 
 static int
-get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, u_int8_t term)
+get_ipv6_addr(const char *src, size_t dlen, struct in6_addr *dst, uint8_t term)
 {
 	const char *end;
 	int ret = in6_pton(src, min_t(size_t, dlen, 0xffff),
diff --git a/src/helpers/sane.c b/src/helpers/sane.c
new file mode 100644
index 0000000..c30f4ba
--- /dev/null
+++ b/src/helpers/sane.c
@@ -0,0 +1,173 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Port this helper to userspace.
+ */
+
+/* SANE connection tracking helper
+ * (SANE = Scanner Access Now Easy)
+ * For documentation about the SANE network protocol see
+ * http://www.sane-project.org/html/doc015.html
+ */
+
+/*
+ * Copyright (C) 2007 Red Hat, Inc.
+ * Author: Michal Schmidt <mschmidt@redhat.com>
+ * Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c):
+ *  (C) 1999-2001 Paul `Rusty' Russell
+ *  (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
+ *  (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
+ *  (C) 2003 Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <netinet/ip.h>
+#define _GNU_SOURCE
+#include <netinet/tcp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+enum sane_state {
+	SANE_STATE_NORMAL,
+	SANE_STATE_START_REQUESTED,
+};
+
+struct sane_request {
+	uint32_t RPC_code;
+#define SANE_NET_START      7   /* RPC code */
+
+	uint32_t handle;
+};
+
+struct sane_reply_net_start {
+	uint32_t status;
+#define SANE_STATUS_SUCCESS 0
+
+	uint16_t zero;
+	uint16_t port;
+	/* other fields aren't interesting for conntrack */
+};
+
+struct nf_ct_sane_master {
+	enum sane_state state;
+};
+
+static int
+sane_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+		 struct myct *myct, uint32_t ctinfo)
+{
+	unsigned int dataoff, datalen;
+	const struct tcphdr *th;
+	void *sb_ptr;
+	int ret = NF_ACCEPT;
+	int dir = CTINFO2DIR(ctinfo);
+	struct nf_ct_sane_master *ct_sane_info = myct->priv_data;
+	struct nf_expect *exp;
+	struct sane_request *req;
+	struct sane_reply_net_start *reply;
+	union nfct_attr_grp_addr saddr;
+	union nfct_attr_grp_addr daddr;
+
+	/* Until there's been traffic both ways, don't look in packets. */
+	if (ctinfo != IP_CT_ESTABLISHED &&
+	    ctinfo != IP_CT_ESTABLISHED_REPLY)
+		return NF_ACCEPT;
+
+	th = (struct tcphdr *)(pktb_network_header(pkt) + protoff);
+
+	/* No data? */
+	dataoff = protoff + th->doff * 4;
+	if (dataoff >= pktb_len(pkt))
+		return NF_ACCEPT;
+
+	datalen = pktb_len(pkt) - dataoff;
+
+	sb_ptr = pktb_network_header(pkt) + dataoff;
+
+	if (dir == MYCT_DIR_ORIG) {
+		if (datalen != sizeof(struct sane_request))
+			goto out;
+
+		req = sb_ptr;
+		if (req->RPC_code != htonl(SANE_NET_START)) {
+			/* Not an interesting command */
+			ct_sane_info->state = SANE_STATE_NORMAL;
+			goto out;
+		}
+
+		/* We're interested in the next reply */
+		ct_sane_info->state = SANE_STATE_START_REQUESTED;
+		goto out;
+	}
+
+	/* Is it a reply to an uninteresting command? */
+	if (ct_sane_info->state != SANE_STATE_START_REQUESTED)
+		goto out;
+
+	/* It's a reply to SANE_NET_START. */
+	ct_sane_info->state = SANE_STATE_NORMAL;
+
+	if (datalen < sizeof(struct sane_reply_net_start)) {
+		pr_debug("nf_ct_sane: NET_START reply too short\n");
+		goto out;
+	}
+
+	reply = sb_ptr;
+	if (reply->status != htonl(SANE_STATUS_SUCCESS)) {
+		/* saned refused the command */
+		pr_debug("nf_ct_sane: unsuccessful SANE_STATUS = %u\n",
+			 ntohl(reply->status));
+		goto out;
+	}
+
+	/* Invalid saned reply? Ignore it. */
+	if (reply->zero != 0)
+		goto out;
+
+	exp = nfexp_new();
+	if (exp == NULL)
+		return NF_DROP;
+
+	cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr);
+	cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr);
+
+	if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
+				 IPPROTO_TCP, NULL, &reply->port, 0)) {
+		nfexp_destroy(exp);
+		return NF_DROP;
+	}
+	myct->exp = exp;
+out:
+	return ret;
+}
+
+static struct ctd_helper sane_helper = {
+	.name		= "sane",
+	.l4proto	= IPPROTO_TCP,
+	.priv_data_len	= sizeof(struct nf_ct_sane_master),
+	.cb		= sane_helper_cb,
+	.policy		= {
+		[0] = {
+			.name			= "sane",
+			.expect_max		= 1,
+			.expect_timeout		= 5 * 60,
+		},
+	},
+};
+
+static void __attribute__ ((constructor)) sane_init(void)
+{
+	helper_register(&sane_helper);
+}
diff --git a/src/helpers/ssdp.c b/src/helpers/ssdp.c
new file mode 100644
index 0000000..bc41087
--- /dev/null
+++ b/src/helpers/ssdp.c
@@ -0,0 +1,134 @@
+/*
+ * SSDP connection tracking helper
+ * (SSDP = Simple Service Discovery Protocol)
+ * For documentation about SSDP see
+ * http://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol
+ *
+ * Copyright (C) 2014 Ashley Hughes <ashley.hughes@blueyonder.co.uk>
+ * Based on the SSDP conntrack helper (nf_conntrack_ssdp.c),
+ * :http://marc.info/?t=132945775100001&r=1&w=2
+ *  (C) 2012 Ian Pilcher <arequipeno@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+#define SSDP_MCAST_ADDR		"239.255.255.250"
+#define UPNP_MCAST_LL_ADDR	"FF02::C" /* link-local */
+#define UPNP_MCAST_SL_ADDR	"FF05::C" /* site-local */
+
+#define SSDP_M_SEARCH		"M-SEARCH"
+#define SSDP_M_SEARCH_SIZE	(sizeof SSDP_M_SEARCH - 1)
+
+static int ssdp_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+			  struct myct *myct, uint32_t ctinfo)
+{
+	int ret = NF_ACCEPT;
+	union nfct_attr_grp_addr daddr, saddr, taddr;
+	struct iphdr *net_hdr = (struct iphdr *)pktb_network_header(pkt);
+	int good_packet = 0;
+	struct nf_expect *exp;
+	uint16_t port;
+	unsigned int dataoff;
+	void *sb_ptr;
+
+	cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr);
+	switch (nfct_get_attr_u8(myct->ct, ATTR_L3PROTO)) {
+	case AF_INET:
+		inet_pton(AF_INET, SSDP_MCAST_ADDR, &(taddr.ip));
+		if (daddr.ip == taddr.ip)
+			good_packet = 1;
+		break;
+	case AF_INET6:
+		inet_pton(AF_INET6, UPNP_MCAST_LL_ADDR, &(taddr.ip6));
+		if (daddr.ip6[0] == taddr.ip6[0] &&
+		    daddr.ip6[1] == taddr.ip6[1] &&
+		    daddr.ip6[2] == taddr.ip6[2] &&
+		    daddr.ip6[3] == taddr.ip6[3]) {
+			good_packet = 1;
+			break;
+		}
+		inet_pton(AF_INET6, UPNP_MCAST_SL_ADDR, &(taddr.ip6));
+		if (daddr.ip6[0] == taddr.ip6[0] &&
+		    daddr.ip6[1] == taddr.ip6[1] &&
+		    daddr.ip6[2] == taddr.ip6[2] &&
+		    daddr.ip6[3] == taddr.ip6[3]) {
+			good_packet = 1;
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (!good_packet) {
+		pr_debug("ssdp_help: destination address not multicast; ignoring\n");
+		return NF_ACCEPT;
+	}
+
+	/* No data? Ignore */
+	dataoff = net_hdr->ihl*4 + sizeof(struct udphdr);
+	if (dataoff >= pktb_len(pkt)) {
+		pr_debug("ssdp_help: UDP payload too small for M-SEARCH; ignoring\n");
+		return NF_ACCEPT;
+	}
+
+	sb_ptr = pktb_network_header(pkt) + dataoff;
+
+	if (memcmp(sb_ptr, SSDP_M_SEARCH, SSDP_M_SEARCH_SIZE) != 0) {
+		pr_debug("ssdp_help: UDP payload does not begin with 'M-SEARCH'; ignoring\n");
+		return NF_ACCEPT;
+	}
+
+	cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr);
+	cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port);
+
+	exp = nfexp_new();
+	if (exp == NULL)
+		return NF_DROP;
+
+	if (cthelper_expect_init(exp, myct->ct, 0, NULL, &saddr,
+				 IPPROTO_UDP, NULL, &port,
+				 NF_CT_EXPECT_PERMANENT)) {
+		nfexp_destroy(exp);
+		return NF_DROP;
+	}
+	myct->exp = exp;
+
+	return ret;
+}
+
+static struct ctd_helper ssdp_helper = {
+	.name		= "ssdp",
+	.l4proto	= IPPROTO_UDP,
+	.priv_data_len	= 0,
+	.cb		= ssdp_helper_cb,
+	.policy		= {
+		[0] = {
+			.name		= "ssdp",
+			.expect_max	= 1,
+			.expect_timeout	= 5 * 60,
+		},
+	},
+};
+
+static void __attribute__ ((constructor)) ssdp_init(void)
+{
+	helper_register(&ssdp_helper);
+}
diff --git a/src/helpers/tftp.c b/src/helpers/tftp.c
new file mode 100644
index 0000000..45591c6
--- /dev/null
+++ b/src/helpers/tftp.c
@@ -0,0 +1,138 @@
+/*
+ * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * Adapted from:
+ *
+ * (C) 2001-2002 Magnus Boden <mb@ozaba.mine.nu>
+ * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "conntrackd.h"
+#include "helper.h"
+#include "myct.h"
+#include "log.h"
+#include <errno.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <libmnl/libmnl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_queue/libnetfilter_queue.h>
+#include <libnetfilter_queue/libnetfilter_queue_udp.h>
+#include <libnetfilter_queue/pktbuff.h>
+#include <linux/netfilter.h>
+
+struct tftphdr {
+	uint16_t opcode;
+};
+
+#define TFTP_OPCODE_READ	1
+#define TFTP_OPCODE_WRITE	2
+#define TFTP_OPCODE_DATA	3
+#define TFTP_OPCODE_ACK		4
+#define TFTP_OPCODE_ERROR	5
+
+static unsigned int nat_tftp(struct pkt_buff *pkt, uint32_t ctinfo,
+			     struct nf_conntrack *ct, struct nf_expect *exp)
+{
+	struct nf_conntrack *nat_tuple;
+	static uint32_t zero[4] = { 0, 0, 0, 0 };
+
+	nat_tuple = nfct_new();
+	if (nat_tuple == NULL)
+		return NF_ACCEPT;
+
+	switch (nfct_get_attr_u8(ct, ATTR_L3PROTO)) {
+	case AF_INET:
+		nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET);
+		nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0);
+		nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0);
+		break;
+	case AF_INET6:
+		nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET6);
+		nfct_set_attr(nat_tuple, ATTR_IPV6_SRC, &zero);
+		nfct_set_attr(nat_tuple, ATTR_IPV6_DST, &zero);
+		break;
+	}
+	nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_UDP);
+	nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC,
+			  nfct_get_attr_u16(ct, ATTR_PORT_SRC));
+	nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0);
+
+	nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, MYCT_DIR_REPL);
+	nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master");
+	nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple);
+
+	return NF_ACCEPT;
+}
+
+static int
+tftp_helper_cb(struct pkt_buff *pkt, uint32_t protoff,
+	       struct myct *myct, uint32_t ctinfo)
+{
+	const struct tftphdr *tfh;
+	struct nf_expect *exp;
+	unsigned int ret = NF_ACCEPT;
+	union nfct_attr_grp_addr saddr, daddr;
+	uint16_t dport;
+
+	tfh = (struct tftphdr *)(pktb_network_header(pkt) + protoff + sizeof(struct udphdr));
+
+	switch (ntohs(tfh->opcode)) {
+	case TFTP_OPCODE_READ:
+	case TFTP_OPCODE_WRITE:
+		/* RRQ and WRQ works the same way */
+		exp = nfexp_new();
+		if (exp == NULL) {
+			pr_debug("cannot alloc expectation\n");
+			return NF_DROP;
+		}
+
+		cthelper_get_addr_src(myct->ct, MYCT_DIR_REPL, &saddr);
+		cthelper_get_addr_dst(myct->ct, MYCT_DIR_REPL, &daddr);
+		cthelper_get_port_dst(myct->ct, MYCT_DIR_REPL, &dport);
+
+		if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr,
+					 IPPROTO_UDP, NULL, &dport, 0)) {
+			nfexp_destroy(exp);
+			return NF_DROP;
+		}
+
+		if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK)
+			ret = nat_tftp(pkt, ctinfo, myct->ct, exp);
+
+		myct->exp = exp;
+		break;
+	case TFTP_OPCODE_DATA:
+	case TFTP_OPCODE_ACK:
+		pr_debug("Data/ACK opcode\n");
+		break;
+	case TFTP_OPCODE_ERROR:
+		pr_debug("Error opcode\n");
+		break;
+	default:
+		pr_debug("Unknown opcode\n");
+	}
+	return ret;
+}
+
+static struct ctd_helper tftp_helper = {
+	.name		= "tftp",
+	.l4proto	= IPPROTO_UDP,
+	.cb		= tftp_helper_cb,
+	.policy		= {
+		[0] = {
+			.name			= "tftp",
+			.expect_max		= 1,
+			.expect_timeout		= 5 * 60,
+		},
+	},
+};
+
+static void __attribute__ ((constructor)) tftp_init(void)
+{
+	helper_register(&tftp_helper);
+}
diff --git a/src/helpers/tns.c b/src/helpers/tns.c
index 5833fea..2b4fed4 100644
--- a/src/helpers/tns.c
+++ b/src/helpers/tns.c
@@ -18,6 +18,7 @@
 #include <ctype.h>	/* for isdigit */
 #include <errno.h>
 
+#define _GNU_SOURCE
 #include <netinet/tcp.h>
 
 #include <libmnl/libmnl.h>
diff --git a/src/internal_bypass.c b/src/internal_bypass.c
index ce2ae46..61988c7 100644
--- a/src/internal_bypass.c
+++ b/src/internal_bypass.c
@@ -49,7 +49,7 @@ internal_bypass_ct_dump_cb(enum nf_conntrack_msg_type type,
 static void internal_bypass_ct_dump(int fd, int type)
 {
 	struct nfct_handle *h;
-	u_int32_t family = AF_UNSPEC;
+	uint32_t family = AF_UNSPEC;
 	int ret;
 
 	h = nfct_open(CONFIG(netlink).subsys_id, 0);
@@ -180,7 +180,7 @@ internal_bypass_exp_dump_cb(enum nf_conntrack_msg_type type,
 static void internal_bypass_exp_dump(int fd, int type)
 {
 	struct nfct_handle *h;
-	u_int32_t family = AF_UNSPEC;
+	uint32_t family = AF_UNSPEC;
 	int ret;
 
 	h = nfct_open(CONFIG(netlink).subsys_id, 0);
diff --git a/src/local.c b/src/local.c
index feff608..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 <errno.h>
 #include <sys/types.h>
 #include <sys/socket.h>
-#include <sys/fcntl.h>
+#include <fcntl.h>
 #include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
 
 struct nfct_handle *nl_init_event_handler(void)
diff --git a/src/nfct-extensions/helper.c b/src/nfct-extensions/helper.c
index f91fc41..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 <pablo@netfilter.org>
+ * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published
@@ -19,6 +19,7 @@
 #include <unistd.h>
 #include <time.h>
 #include <netinet/in.h>
+#include <netdb.h>
 #include <errno.h>
 
 #include <libmnl/libmnl.h>
@@ -31,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 <list|add|delete|get|flush|set> "
+			"[<parameters>, ...]\n", VERSION, argv[0]);
 }
 
-int nfct_cmd_timeout_parse_params(int argc, char *argv[])
+static int nfct_cmd_timeout_list(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_delete(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_get(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_flush(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_default_set(struct mnl_socket *nl, int argc, char *argv[]);
+static int nfct_cmd_timeout_default_get(struct mnl_socket *nl, int argc, char *argv[]);
+
+static int
+nfct_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; i<argc; i+=2) {
+	for (; argc>1; argc-=2, argv+=2) {
 		int matching = -1;
 
 		for (j=0; j<nfct_timeout_attr_max[l4proto]; j++) {
@@ -241,76 +237,78 @@ int nfct_cmd_timeout_add(int argc, char *argv[])
 				nfct_perror("state name is NULL");
 				return -1;
 			}
-			if (strcasecmp(argv[i], state_name) != 0)
+			if (strcasecmp(*argv, state_name) != 0)
 				continue;
 
 			matching = j;
 			break;
 		}
 		if (matching != -1) {
-			if (i+1 >= argc) {
-				nfct_perror("missing value for this timeout");
-				return -1;
-			}
 			nfct_timeout_policy_attr_set_u32(t, matching,
-							 atoi(argv[i+1]));
-			matching = -1;
+							 atoi(*(argv+1)));
 		} else {
 			fprintf(stderr, "nfct v%s: Wrong state name: `%s' "
 					"for protocol `%s'\n",
-					VERSION, argv[i], argv[5]);
+					VERSION, *argv, proto_name);
 			return -1;
 		}
 	}
+	if (argc > 0) {
+		nfct_perror("missing value for this timeout");
+		return -1;
+	}
 
-	seq = time(NULL);
-	nlh = nfct_timeout_nlmsg_build_hdr(buf, IPCTNL_MSG_TIMEOUT_NEW,
-				     NLM_F_CREATE | NLM_F_ACK, seq);
-	nfct_timeout_nlmsg_build_payload(nlh, t);
+	return 0;
+}
 
-	nfct_timeout_free(t);
+int nfct_cmd_timeout_add(struct mnl_socket *nl, int argc, char *argv[])
+{
+	char buf[MNL_SOCKET_BUFFER_SIZE];
+	struct nlmsghdr *nlh;
+	uint32_t portid, seq;
+	struct nfct_timeout *t;
 
-	nl = mnl_socket_open(NETLINK_NETFILTER);
-	if (nl == NULL) {
-		nfct_perror("mnl_socket_open");
+	if (argc < 6) {
+		nfct_perror("missing parameters\n"
+			    "syntax: nfct 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 <errno.h>
 
 #include <libmnl/libmnl.h>
-#include <linux/netfilter/nfnetlink_cttimeout.h>
-#include <libnetfilter_cttimeout/libnetfilter_cttimeout.h>
 
+#include "linux_list.h"
 #include "nfct.h"
 
 static int nfct_cmd_version(int argc, char *argv[]);
@@ -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;