Merge branch 'master' of git://1984.lsi.us.es/nf-next
authorDavid S. Miller <davem@davemloft.net>
Tue, 19 Feb 2013 04:42:09 +0000 (23:42 -0500)
committerDavid S. Miller <davem@davemloft.net>
Tue, 19 Feb 2013 04:42:09 +0000 (23:42 -0500)
Pablo Neira Ayuso says:

====================
The following patchset contain updates for your net-next tree, they are:

* Fix (for just added) connlabel dependencies, from Florian Westphal.

* Add aliasing support for conntrack, thus users can either use -m state
  or -m conntrack from iptables while using the same kernel module, from
  Jozsef Kadlecsik.

* Some code refactoring for the CT target to merge common code in
  revision 0 and 1, from myself.

* Add aliasing support for CT, based on patch from Jozsef Kadlecsik.

* Add one mutex per nfnetlink subsystem, from myself.

* Improved logging for packets that are dropped by helpers, from myself.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
25 files changed:
include/linux/netfilter/nfnetlink.h
include/net/netfilter/nf_conntrack_helper.h
include/uapi/linux/netfilter/xt_CT.h
include/uapi/linux/netfilter/xt_conntrack.h
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/netfilter/Kconfig
net/netfilter/ipset/ip_set_core.c
net/netfilter/nf_conntrack_amanda.c
net/netfilter/nf_conntrack_ftp.c
net/netfilter/nf_conntrack_h323_main.c
net/netfilter/nf_conntrack_helper.c
net/netfilter/nf_conntrack_irc.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_pptp.c
net/netfilter/nf_conntrack_sane.c
net/netfilter/nf_conntrack_sip.c
net/netfilter/nf_conntrack_tftp.c
net/netfilter/nf_nat_amanda.c
net/netfilter/nf_nat_ftp.c
net/netfilter/nf_nat_irc.c
net/netfilter/nf_nat_sip.c
net/netfilter/nf_nat_tftp.c
net/netfilter/nfnetlink.c
net/netfilter/xt_CT.c

index 4966dde..ecbb8e4 100644 (file)
@@ -34,8 +34,8 @@ extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 pid, unsigne
 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);
+extern void nfnl_lock(__u8 subsys_id);
+extern void nfnl_unlock(__u8 subsys_id);
 
 #define MODULE_ALIAS_NFNL_SUBSYS(subsys) \
        MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys))
index ce27edf..26c4ae5 100644 (file)
@@ -100,6 +100,10 @@ struct nf_ct_helper_expectfn {
        void (*expectfn)(struct nf_conn *ct, struct nf_conntrack_expect *exp);
 };
 
+__printf(3,4)
+void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
+                     const char *fmt, ...);
+
 void nf_ct_helper_expectfn_register(struct nf_ct_helper_expectfn *n);
 void nf_ct_helper_expectfn_unregister(struct nf_ct_helper_expectfn *n);
 struct nf_ct_helper_expectfn *
index a064b8a..5a688c1 100644 (file)
@@ -3,7 +3,11 @@
 
 #include <linux/types.h>
 
-#define XT_CT_NOTRACK  0x1
+enum {
+       XT_CT_NOTRACK           = 1 << 0,
+       XT_CT_NOTRACK_ALIAS     = 1 << 1,
+       XT_CT_MASK              = XT_CT_NOTRACK | XT_CT_NOTRACK_ALIAS,
+};
 
 struct xt_ct_target_info {
        __u16 flags;
index e3c041d..e5bd308 100644 (file)
@@ -31,6 +31,7 @@ enum {
        XT_CONNTRACK_REPLSRC_PORT = 1 << 10,
        XT_CONNTRACK_REPLDST_PORT = 1 << 11,
        XT_CONNTRACK_DIRECTION    = 1 << 12,
+       XT_CONNTRACK_STATE_ALIAS  = 1 << 13,
 };
 
 struct xt_conntrack_mtinfo1 {
index 48990ad..2820aa1 100644 (file)
@@ -100,7 +100,6 @@ static unsigned int ipv4_helper(unsigned int hooknum,
        enum ip_conntrack_info ctinfo;
        const struct nf_conn_help *help;
        const struct nf_conntrack_helper *helper;
-       unsigned int ret;
 
        /* This is where we call the helper: as the packet goes out. */
        ct = nf_ct_get(skb, &ctinfo);
@@ -116,13 +115,8 @@ static unsigned int ipv4_helper(unsigned int hooknum,
        if (!helper)
                return NF_ACCEPT;
 
-       ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
-                          ct, ctinfo);
-       if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
-               nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL,
-                             "nf_ct_%s: dropping packet", helper->name);
-       }
-       return ret;
+       return helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
+                           ct, ctinfo);
 }
 
 static unsigned int ipv4_confirm(unsigned int hooknum,
index 8a45bb2..2b6c226 100644 (file)
@@ -104,7 +104,6 @@ static unsigned int ipv6_helper(unsigned int hooknum,
        const struct nf_conn_help *help;
        const struct nf_conntrack_helper *helper;
        enum ip_conntrack_info ctinfo;
-       unsigned int ret;
        __be16 frag_off;
        int protoff;
        u8 nexthdr;
@@ -130,12 +129,7 @@ static unsigned int ipv6_helper(unsigned int hooknum,
                return NF_ACCEPT;
        }
 
-       ret = helper->help(skb, protoff, ct, ctinfo);
-       if (ret != NF_ACCEPT && (ret & NF_VERDICT_MASK) != NF_QUEUE) {
-               nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL,
-                             "nf_ct_%s: dropping packet", helper->name);
-       }
-       return ret;
+       return helper->help(skb, protoff, ct, ctinfo);
 }
 
 static unsigned int ipv6_confirm(unsigned int hooknum,
index eb2c8eb..d4dd702 100644 (file)
@@ -860,6 +860,7 @@ config NETFILTER_XT_MATCH_CONNBYTES
 config NETFILTER_XT_MATCH_CONNLABEL
        tristate '"connlabel" match support'
        select NF_CONNTRACK_LABELS
+       depends on NF_CONNTRACK
        depends on NETFILTER_ADVANCED
        ---help---
          This match allows you to test and assign userspace-defined labels names
index 6d6d8f2..f82b2e6 100644 (file)
@@ -88,14 +88,14 @@ find_set_type(const char *name, u8 family, u8 revision)
 static bool
 load_settype(const char *name)
 {
-       nfnl_unlock();
+       nfnl_unlock(NFNL_SUBSYS_IPSET);
        pr_debug("try to load ip_set_%s\n", name);
        if (request_module("ip_set_%s", name) < 0) {
                pr_warning("Can't find ip_set type %s\n", name);
-               nfnl_lock();
+               nfnl_lock(NFNL_SUBSYS_IPSET);
                return false;
        }
-       nfnl_lock();
+       nfnl_lock(NFNL_SUBSYS_IPSET);
        return true;
 }
 
@@ -532,7 +532,7 @@ ip_set_nfnl_get(const char *name)
        ip_set_id_t i, index = IPSET_INVALID_ID;
        struct ip_set *s;
 
-       nfnl_lock();
+       nfnl_lock(NFNL_SUBSYS_IPSET);
        for (i = 0; i < ip_set_max; i++) {
                s = nfnl_set(i);
                if (s != NULL && STREQ(s->name, name)) {
@@ -541,7 +541,7 @@ ip_set_nfnl_get(const char *name)
                        break;
                }
        }
-       nfnl_unlock();
+       nfnl_unlock(NFNL_SUBSYS_IPSET);
 
        return index;
 }
@@ -561,13 +561,13 @@ ip_set_nfnl_get_byindex(ip_set_id_t index)
        if (index > ip_set_max)
                return IPSET_INVALID_ID;
 
-       nfnl_lock();
+       nfnl_lock(NFNL_SUBSYS_IPSET);
        set = nfnl_set(index);
        if (set)
                __ip_set_get(set);
        else
                index = IPSET_INVALID_ID;
-       nfnl_unlock();
+       nfnl_unlock(NFNL_SUBSYS_IPSET);
 
        return index;
 }
@@ -584,11 +584,11 @@ void
 ip_set_nfnl_put(ip_set_id_t index)
 {
        struct ip_set *set;
-       nfnl_lock();
+       nfnl_lock(NFNL_SUBSYS_IPSET);
        set = nfnl_set(index);
        if (set != NULL)
                __ip_set_put(set);
-       nfnl_unlock();
+       nfnl_unlock(NFNL_SUBSYS_IPSET);
 }
 EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
 
@@ -1763,10 +1763,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
                        goto done;
                }
                req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
-               nfnl_lock();
+               nfnl_lock(NFNL_SUBSYS_IPSET);
                find_set_and_id(req_get->set.name, &id);
                req_get->set.index = id;
-               nfnl_unlock();
+               nfnl_unlock(NFNL_SUBSYS_IPSET);
                goto copy;
        }
        case IP_SET_OP_GET_BYINDEX: {
@@ -1778,11 +1778,11 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
                        ret = -EINVAL;
                        goto done;
                }
-               nfnl_lock();
+               nfnl_lock(NFNL_SUBSYS_IPSET);
                set = nfnl_set(req_get->set.index);
                strncpy(req_get->set.name, set ? set->name : "",
                        IPSET_MAXNAMELEN);
-               nfnl_unlock();
+               nfnl_unlock(NFNL_SUBSYS_IPSET);
                goto copy;
        }
        default:
index c514fe6..dbdaa11 100644 (file)
@@ -145,6 +145,7 @@ static int amanda_help(struct sk_buff *skb,
 
                exp = nf_ct_expect_alloc(ct);
                if (exp == NULL) {
+                       nf_ct_helper_log(skb, ct, "cannot alloc expectation");
                        ret = NF_DROP;
                        goto out;
                }
@@ -158,8 +159,10 @@ static int amanda_help(struct sk_buff *skb,
                if (nf_nat_amanda && ct->status & IPS_NAT_MASK)
                        ret = nf_nat_amanda(skb, ctinfo, protoff,
                                            off - dataoff, len, exp);
-               else if (nf_ct_expect_related(exp) != 0)
+               else if (nf_ct_expect_related(exp) != 0) {
+                       nf_ct_helper_log(skb, ct, "cannot add expectation");
                        ret = NF_DROP;
+               }
                nf_ct_expect_put(exp);
        }
 
index 1ce3bef..62fb8fa 100644 (file)
@@ -435,8 +435,8 @@ skip_nl_seq:
                   connection tracking, not packet filtering.
                   However, it is necessary for accurate tracking in
                   this case. */
-               pr_debug("conntrack_ftp: partial %s %u+%u\n",
-                        search[dir][i].pattern,  ntohl(th->seq), datalen);
+               nf_ct_helper_log(skb, ct, "partial matching of `%s'",
+                                search[dir][i].pattern);
                ret = NF_DROP;
                goto out;
        } else if (found == 0) { /* No match */
@@ -450,6 +450,7 @@ skip_nl_seq:
 
        exp = nf_ct_expect_alloc(ct);
        if (exp == NULL) {
+               nf_ct_helper_log(skb, ct, "cannot alloc expectation");
                ret = NF_DROP;
                goto out;
        }
@@ -500,9 +501,10 @@ skip_nl_seq:
                                 protoff, matchoff, matchlen, exp);
        else {
                /* Can't expect this?  Best to drop packet now. */
-               if (nf_ct_expect_related(exp) != 0)
+               if (nf_ct_expect_related(exp) != 0) {
+                       nf_ct_helper_log(skb, ct, "cannot add expectation");
                        ret = NF_DROP;
-               else
+               else
                        ret = NF_ACCEPT;
        }
 
index 962795e..7df7b36 100644 (file)
@@ -623,7 +623,7 @@ static int h245_help(struct sk_buff *skb, unsigned int protoff,
 
       drop:
        spin_unlock_bh(&nf_h323_lock);
-       net_info_ratelimited("nf_ct_h245: packet dropped\n");
+       nf_ct_helper_log(skb, ct, "cannot process H.245 message");
        return NF_DROP;
 }
 
@@ -1197,7 +1197,7 @@ static int q931_help(struct sk_buff *skb, unsigned int protoff,
 
       drop:
        spin_unlock_bh(&nf_h323_lock);
-       net_info_ratelimited("nf_ct_q931: packet dropped\n");
+       nf_ct_helper_log(skb, ct, "cannot process Q.931 message");
        return NF_DROP;
 }
 
@@ -1795,7 +1795,7 @@ static int ras_help(struct sk_buff *skb, unsigned int protoff,
 
       drop:
        spin_unlock_bh(&nf_h323_lock);
-       net_info_ratelimited("nf_ct_ras: packet dropped\n");
+       nf_ct_helper_log(skb, ct, "cannot process RAS message");
        return NF_DROP;
 }
 
index 1343a4b..013cdf6 100644 (file)
@@ -28,6 +28,7 @@
 #include <net/netfilter/nf_conntrack_helper.h>
 #include <net/netfilter/nf_conntrack_core.h>
 #include <net/netfilter/nf_conntrack_extend.h>
+#include <net/netfilter/nf_log.h>
 
 static DEFINE_MUTEX(nf_ct_helper_mutex);
 struct hlist_head *nf_ct_helper_hash __read_mostly;
@@ -334,6 +335,24 @@ nf_ct_helper_expectfn_find_by_symbol(const void *symbol)
 }
 EXPORT_SYMBOL_GPL(nf_ct_helper_expectfn_find_by_symbol);
 
+__printf(3, 4)
+void nf_ct_helper_log(struct sk_buff *skb, const struct nf_conn *ct,
+                     const char *fmt, ...)
+{
+       const struct nf_conn_help *help;
+       const struct nf_conntrack_helper *helper;
+
+       /* Called from the helper function, this call never fails */
+       help = nfct_help(ct);
+
+       /* rcu_read_lock()ed by nf_hook_slow */
+       helper = rcu_dereference(help->helper);
+
+       nf_log_packet(nf_ct_l3num(ct), 0, skb, NULL, NULL, NULL,
+                     "nf_ct_%s: dropping packet: %s ", helper->name, fmt);
+}
+EXPORT_SYMBOL_GPL(nf_ct_helper_log);
+
 int nf_conntrack_helper_register(struct nf_conntrack_helper *me)
 {
        int ret = 0;
index 3b20aa7..70985c5 100644 (file)
@@ -194,6 +194,8 @@ static int help(struct sk_buff *skb, unsigned int protoff,
 
                        exp = nf_ct_expect_alloc(ct);
                        if (exp == NULL) {
+                               nf_ct_helper_log(skb, ct,
+                                                "cannot alloc expectation");
                                ret = NF_DROP;
                                goto out;
                        }
@@ -210,8 +212,11 @@ static int help(struct sk_buff *skb, unsigned int protoff,
                                                 addr_beg_p - ib_ptr,
                                                 addr_end_p - addr_beg_p,
                                                 exp);
-                       else if (nf_ct_expect_related(exp) != 0)
+                       else if (nf_ct_expect_related(exp) != 0) {
+                               nf_ct_helper_log(skb, ct,
+                                                "cannot add expectation");
                                ret = NF_DROP;
+                       }
                        nf_ct_expect_put(exp);
                        goto out;
                }
index 79c2d50..5d60e04 100644 (file)
@@ -1256,13 +1256,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
        if (!parse_nat_setup) {
 #ifdef CONFIG_MODULES
                rcu_read_unlock();
-               nfnl_unlock();
+               nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
                if (request_module("nf-nat") < 0) {
-                       nfnl_lock();
+                       nfnl_lock(NFNL_SUBSYS_CTNETLINK);
                        rcu_read_lock();
                        return -EOPNOTSUPP;
                }
-               nfnl_lock();
+               nfnl_lock(NFNL_SUBSYS_CTNETLINK);
                rcu_read_lock();
                if (nfnetlink_parse_nat_setup_hook)
                        return -EAGAIN;
@@ -1274,13 +1274,13 @@ ctnetlink_parse_nat_setup(struct nf_conn *ct,
        if (err == -EAGAIN) {
 #ifdef CONFIG_MODULES
                rcu_read_unlock();
-               nfnl_unlock();
+               nfnl_unlock(NFNL_SUBSYS_CTNETLINK);
                if (request_module("nf-nat-%u", nf_ct_l3num(ct)) < 0) {
-                       nfnl_lock();
+                       nfnl_lock(NFNL_SUBSYS_CTNETLINK);
                        rcu_read_lock();
                        return -EOPNOTSUPP;
                }
-               nfnl_lock();
+               nfnl_lock(NFNL_SUBSYS_CTNETLINK);
                rcu_read_lock();
 #else
                err = -EOPNOTSUPP;
index cc7669e..e6678d2 100644 (file)
@@ -14,7 +14,7 @@
  * Limitations:
  *      - We blindly assume that control connections are always
  *        established in PNS->PAC direction.  This is a violation
- *        of RFFC2673
+ *        of RFC 2637
  *      - We can only support one single call within each session
  * TODO:
  *      - testing of incoming PPTP calls
index 295429f..4a2134f 100644 (file)
@@ -138,6 +138,7 @@ static int help(struct sk_buff *skb,
 
        exp = nf_ct_expect_alloc(ct);
        if (exp == NULL) {
+               nf_ct_helper_log(skb, ct, "cannot alloc expectation");
                ret = NF_DROP;
                goto out;
        }
@@ -151,8 +152,10 @@ static int help(struct sk_buff *skb,
        nf_ct_dump_tuple(&exp->tuple);
 
        /* Can't expect this?  Best to drop packet now. */
-       if (nf_ct_expect_related(exp) != 0)
+       if (nf_ct_expect_related(exp) != 0) {
+               nf_ct_helper_log(skb, ct, "cannot add expectation");
                ret = NF_DROP;
+       }
 
        nf_ct_expect_put(exp);
 
index 72a67bb..069229d 100644 (file)
@@ -1095,8 +1095,10 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
                port = simple_strtoul(*dptr + mediaoff, NULL, 10);
                if (port == 0)
                        continue;
-               if (port < 1024 || port > 65535)
+               if (port < 1024 || port > 65535) {
+                       nf_ct_helper_log(skb, ct, "wrong port %u", port);
                        return NF_DROP;
+               }
 
                /* The media description overrides the session description. */
                maddr_len = 0;
@@ -1107,15 +1109,20 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
                        memcpy(&rtp_addr, &maddr, sizeof(rtp_addr));
                } else if (caddr_len)
                        memcpy(&rtp_addr, &caddr, sizeof(rtp_addr));
-               else
+               else {
+                       nf_ct_helper_log(skb, ct, "cannot parse SDP message");
                        return NF_DROP;
+               }
 
                ret = set_expected_rtp_rtcp(skb, protoff, dataoff,
                                            dptr, datalen,
                                            &rtp_addr, htons(port), t->class,
                                            mediaoff, medialen);
-               if (ret != NF_ACCEPT)
+               if (ret != NF_ACCEPT) {
+                       nf_ct_helper_log(skb, ct,
+                                        "cannot add expectation for voice");
                        return ret;
+               }
 
                /* Update media connection address if present */
                if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) {
@@ -1123,8 +1130,10 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff,
                                              dptr, datalen, mediaoff,
                                              SDP_HDR_CONNECTION, SDP_HDR_MEDIA,
                                              &rtp_addr);
-                       if (ret != NF_ACCEPT)
+                       if (ret != NF_ACCEPT) {
+                               nf_ct_helper_log(skb, ct, "cannot mangle SDP");
                                return ret;
+                       }
                }
                i++;
        }
@@ -1258,9 +1267,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
        ret = ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
                                      SIP_HDR_CONTACT, NULL,
                                      &matchoff, &matchlen, &daddr, &port);
-       if (ret < 0)
+       if (ret < 0) {
+               nf_ct_helper_log(skb, ct, "cannot parse contact");
                return NF_DROP;
-       else if (ret == 0)
+       else if (ret == 0)
                return NF_ACCEPT;
 
        /* We don't support third-party registrations */
@@ -1273,8 +1283,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
 
        if (ct_sip_parse_numerical_param(ct, *dptr,
                                         matchoff + matchlen, *datalen,
-                                        "expires=", NULL, NULL, &expires) < 0)
+                                        "expires=", NULL, NULL, &expires) < 0) {
+               nf_ct_helper_log(skb, ct, "cannot parse expires");
                return NF_DROP;
+       }
 
        if (expires == 0) {
                ret = NF_ACCEPT;
@@ -1282,8 +1294,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
        }
 
        exp = nf_ct_expect_alloc(ct);
-       if (!exp)
+       if (!exp) {
+               nf_ct_helper_log(skb, ct, "cannot alloc expectation");
                return NF_DROP;
+       }
 
        saddr = NULL;
        if (sip_direct_signalling)
@@ -1300,9 +1314,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff,
                ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen,
                                        exp, matchoff, matchlen);
        else {
-               if (nf_ct_expect_related(exp) != 0)
+               if (nf_ct_expect_related(exp) != 0) {
+                       nf_ct_helper_log(skb, ct, "cannot add expectation");
                        ret = NF_DROP;
-               else
+               else
                        ret = NF_ACCEPT;
        }
        nf_ct_expect_put(exp);
@@ -1356,9 +1371,10 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff,
                                              SIP_HDR_CONTACT, &in_contact,
                                              &matchoff, &matchlen,
                                              &addr, &port);
-               if (ret < 0)
+               if (ret < 0) {
+                       nf_ct_helper_log(skb, ct, "cannot parse contact");
                        return NF_DROP;
-               else if (ret == 0)
+               else if (ret == 0)
                        break;
 
                /* We don't support third-party registrations */
@@ -1373,8 +1389,10 @@ static int process_register_response(struct sk_buff *skb, unsigned int protoff,
                                                   matchoff + matchlen,
                                                   *datalen, "expires=",
                                                   NULL, NULL, &c_expires);
-               if (ret < 0)
+               if (ret < 0) {
+                       nf_ct_helper_log(skb, ct, "cannot parse expires");
                        return NF_DROP;
+               }
                if (c_expires == 0)
                        break;
                if (refresh_signalling_expectation(ct, &addr, proto, port,
@@ -1408,15 +1426,21 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff,
        if (*datalen < strlen("SIP/2.0 200"))
                return NF_ACCEPT;
        code = simple_strtoul(*dptr + strlen("SIP/2.0 "), NULL, 10);
-       if (!code)
+       if (!code) {
+               nf_ct_helper_log(skb, ct, "cannot get code");
                return NF_DROP;
+       }
 
        if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
-                             &matchoff, &matchlen) <= 0)
+                             &matchoff, &matchlen) <= 0) {
+               nf_ct_helper_log(skb, ct, "cannot parse cseq");
                return NF_DROP;
+       }
        cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
-       if (!cseq)
+       if (!cseq) {
+               nf_ct_helper_log(skb, ct, "cannot get cseq");
                return NF_DROP;
+       }
        matchend = matchoff + matchlen + 1;
 
        for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
@@ -1471,11 +1495,15 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
                        continue;
 
                if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
-                                     &matchoff, &matchlen) <= 0)
+                                     &matchoff, &matchlen) <= 0) {
+                       nf_ct_helper_log(skb, ct, "cannot parse cseq");
                        return NF_DROP;
+               }
                cseq = simple_strtoul(*dptr + matchoff, NULL, 10);
-               if (!cseq)
+               if (!cseq) {
+                       nf_ct_helper_log(skb, ct, "cannot get cseq");
                        return NF_DROP;
+               }
 
                return handler->request(skb, protoff, dataoff, dptr, datalen,
                                        cseq);
@@ -1498,8 +1526,10 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct,
        if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
                nf_nat_sip = rcu_dereference(nf_nat_sip_hook);
                if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff,
-                                             dptr, datalen))
+                                             dptr, datalen)) {
+                       nf_ct_helper_log(skb, ct, "cannot NAT SIP message");
                        ret = NF_DROP;
+               }
        }
 
        return ret;
@@ -1563,11 +1593,14 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
                end += strlen("\r\n\r\n") + clen;
 
                msglen = origlen = end - dptr;
-               if (msglen > datalen)
+               if (msglen > datalen) {
+                       nf_ct_helper_log(skb, ct, "incomplete/bad SIP message");
                        return NF_DROP;
+               }
 
                ret = process_sip_msg(skb, ct, protoff, dataoff,
                                      &dptr, &msglen);
+               /* process_sip_* functions report why this packet is dropped */
                if (ret != NF_ACCEPT)
                        break;
                diff     = msglen - origlen;
index 81fc61c..e9936c8 100644 (file)
@@ -60,8 +60,10 @@ static int tftp_help(struct sk_buff *skb,
                nf_ct_dump_tuple(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
 
                exp = nf_ct_expect_alloc(ct);
-               if (exp == NULL)
+               if (exp == NULL) {
+                       nf_ct_helper_log(skb, ct, "cannot alloc expectation");
                        return NF_DROP;
+               }
                tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
                nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT,
                                  nf_ct_l3num(ct),
@@ -74,8 +76,10 @@ static int tftp_help(struct sk_buff *skb,
                nf_nat_tftp = rcu_dereference(nf_nat_tftp_hook);
                if (nf_nat_tftp && ct->status & IPS_NAT_MASK)
                        ret = nf_nat_tftp(skb, ctinfo, exp);
-               else if (nf_ct_expect_related(exp) != 0)
+               else if (nf_ct_expect_related(exp) != 0) {
+                       nf_ct_helper_log(skb, ct, "cannot add expectation");
                        ret = NF_DROP;
+               }
                nf_ct_expect_put(exp);
                break;
        case TFTP_OPCODE_DATA:
index 42d3378..3b67c9d 100644 (file)
@@ -56,15 +56,19 @@ static unsigned int help(struct sk_buff *skb,
                }
        }
 
-       if (port == 0)
+       if (port == 0) {
+               nf_ct_helper_log(skb, exp->master, "all ports in use");
                return NF_DROP;
+       }
 
        sprintf(buffer, "%u", port);
        ret = nf_nat_mangle_udp_packet(skb, exp->master, ctinfo,
                                       protoff, matchoff, matchlen,
                                       buffer, strlen(buffer));
-       if (ret != NF_ACCEPT)
+       if (ret != NF_ACCEPT) {
+               nf_ct_helper_log(skb, exp->master, "cannot mangle packet");
                nf_ct_unexpect_related(exp);
+       }
        return ret;
 }
 
index e839b97..e84a578 100644 (file)
@@ -96,8 +96,10 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb,
                }
        }
 
-       if (port == 0)
+       if (port == 0) {
+               nf_ct_helper_log(skb, ct, "all ports in use");
                return NF_DROP;
+       }
 
        buflen = nf_nat_ftp_fmt_cmd(ct, type, buffer, sizeof(buffer),
                                    &newaddr, port);
@@ -113,6 +115,7 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb,
        return NF_ACCEPT;
 
 out:
+       nf_ct_helper_log(skb, ct, "cannot mangle packet");
        nf_ct_unexpect_related(exp);
        return NF_DROP;
 }
index 1fedee6..f02b360 100644 (file)
@@ -56,14 +56,18 @@ static unsigned int help(struct sk_buff *skb,
                }
        }
 
-       if (port == 0)
+       if (port == 0) {
+               nf_ct_helper_log(skb, exp->master, "all ports in use");
                return NF_DROP;
+       }
 
        ret = nf_nat_mangle_tcp_packet(skb, exp->master, ctinfo,
                                       protoff, matchoff, matchlen, buffer,
                                       strlen(buffer));
-       if (ret != NF_ACCEPT)
+       if (ret != NF_ACCEPT) {
+               nf_ct_helper_log(skb, exp->master, "cannot mangle packet");
                nf_ct_unexpect_related(exp);
+       }
        return ret;
 }
 
index 5951146..96ccdf7 100644 (file)
@@ -159,8 +159,10 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
                                         &matchoff, &matchlen,
                                         &addr, &port) > 0 &&
                    !map_addr(skb, protoff, dataoff, dptr, datalen,
-                             matchoff, matchlen, &addr, port))
+                             matchoff, matchlen, &addr, port)) {
+                       nf_ct_helper_log(skb, ct, "cannot mangle SIP message");
                        return NF_DROP;
+               }
                request = 1;
        } else
                request = 0;
@@ -193,8 +195,10 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
 
                olen = *datalen;
                if (!map_addr(skb, protoff, dataoff, dptr, datalen,
-                             matchoff, matchlen, &addr, port))
+                             matchoff, matchlen, &addr, port)) {
+                       nf_ct_helper_log(skb, ct, "cannot mangle Via header");
                        return NF_DROP;
+               }
 
                matchend = matchoff + matchlen + *datalen - olen;
 
@@ -209,8 +213,10 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
                                        &ct->tuplehash[!dir].tuple.dst.u3,
                                        true);
                        if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
-                                          poff, plen, buffer, buflen))
+                                          poff, plen, buffer, buflen)) {
+                               nf_ct_helper_log(skb, ct, "cannot mangle maddr");
                                return NF_DROP;
+                       }
                }
 
                /* The received= parameter (RFC 2361) contains the address
@@ -225,6 +231,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
                                        false);
                        if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
                                           poff, plen, buffer, buflen))
+                               nf_ct_helper_log(skb, ct, "cannot mangle received");
                                return NF_DROP;
                }
 
@@ -238,8 +245,10 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
                        __be16 p = ct->tuplehash[!dir].tuple.src.u.udp.port;
                        buflen = sprintf(buffer, "%u", ntohs(p));
                        if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
-                                          poff, plen, buffer, buflen))
+                                          poff, plen, buffer, buflen)) {
+                               nf_ct_helper_log(skb, ct, "cannot mangle rport");
                                return NF_DROP;
+                       }
                }
        }
 
@@ -253,27 +262,35 @@ next:
                                       &addr, &port) > 0) {
                if (!map_addr(skb, protoff, dataoff, dptr, datalen,
                              matchoff, matchlen,
-                             &addr, port))
+                             &addr, port)) {
+                       nf_ct_helper_log(skb, ct, "cannot mangle contact");
                        return NF_DROP;
+               }
        }
 
        if (!map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_FROM) ||
-           !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO))
+           !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO)) {
+               nf_ct_helper_log(skb, ct, "cannot mangle SIP from/to");
                return NF_DROP;
+       }
 
        /* Mangle destination port for Cisco phones, then fix up checksums */
        if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) {
                struct udphdr *uh;
 
-               if (!skb_make_writable(skb, skb->len))
+               if (!skb_make_writable(skb, skb->len)) {
+                       nf_ct_helper_log(skb, ct, "cannot mangle packet");
                        return NF_DROP;
+               }
 
                uh = (void *)skb->data + protoff;
                uh->dest = ct_sip_info->forced_dport;
 
                if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff,
-                                             0, 0, NULL, 0))
+                                             0, 0, NULL, 0)) {
+                       nf_ct_helper_log(skb, ct, "cannot mangle packet");
                        return NF_DROP;
+               }
        }
 
        return NF_ACCEPT;
@@ -372,15 +389,19 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
                }
        }
 
-       if (port == 0)
+       if (port == 0) {
+               nf_ct_helper_log(skb, ct, "all ports in use for SIP");
                return NF_DROP;
+       }
 
        if (!nf_inet_addr_cmp(&exp->tuple.dst.u3, &exp->saved_addr) ||
            exp->tuple.dst.u.udp.port != exp->saved_proto.udp.port) {
                buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, port);
                if (!mangle_packet(skb, protoff, dataoff, dptr, datalen,
-                                  matchoff, matchlen, buffer, buflen))
+                                  matchoff, matchlen, buffer, buflen)) {
+                       nf_ct_helper_log(skb, ct, "cannot mangle packet");
                        goto err;
+               }
        }
        return NF_ACCEPT;
 
@@ -573,14 +594,18 @@ static unsigned int nf_nat_sdp_media(struct sk_buff *skb, unsigned int protoff,
                }
        }
 
-       if (port == 0)
+       if (port == 0) {
+               nf_ct_helper_log(skb, ct, "all ports in use for SDP media");
                goto err1;
+       }
 
        /* Update media port. */
        if (rtp_exp->tuple.dst.u.udp.port != rtp_exp->saved_proto.udp.port &&
            !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen,
-                            mediaoff, medialen, port))
+                            mediaoff, medialen, port)) {
+               nf_ct_helper_log(skb, ct, "cannot mangle SDP message");
                goto err2;
+       }
 
        return NF_ACCEPT;
 
index ccabbda..7f67e1d 100644 (file)
@@ -28,8 +28,10 @@ static unsigned int help(struct sk_buff *skb,
                = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port;
        exp->dir = IP_CT_DIR_REPLY;
        exp->expectfn = nf_nat_follow_master;
-       if (nf_ct_expect_related(exp) != 0)
+       if (nf_ct_expect_related(exp) != 0) {
+               nf_ct_helper_log(skb, exp->master, "cannot add expectation");
                return NF_DROP;
+       }
        return NF_ACCEPT;
 }
 
index 58a09b7..d578ec2 100644 (file)
@@ -36,8 +36,10 @@ MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_NETFILTER);
 
 static char __initdata nfversion[] = "0.30";
 
-static const struct nfnetlink_subsystem __rcu *subsys_table[NFNL_SUBSYS_COUNT];
-static DEFINE_MUTEX(nfnl_mutex);
+static struct {
+       struct mutex                            mutex;
+       const struct nfnetlink_subsystem __rcu  *subsys;
+} table[NFNL_SUBSYS_COUNT];
 
 static const int nfnl_group2type[NFNLGRP_MAX+1] = {
        [NFNLGRP_CONNTRACK_NEW]         = NFNL_SUBSYS_CTNETLINK,
@@ -48,27 +50,32 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
        [NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
 };
 
-void nfnl_lock(void)
+void nfnl_lock(__u8 subsys_id)
 {
-       mutex_lock(&nfnl_mutex);
+       mutex_lock(&table[subsys_id].mutex);
 }
 EXPORT_SYMBOL_GPL(nfnl_lock);
 
-void nfnl_unlock(void)
+void nfnl_unlock(__u8 subsys_id)
 {
-       mutex_unlock(&nfnl_mutex);
+       mutex_unlock(&table[subsys_id].mutex);
 }
 EXPORT_SYMBOL_GPL(nfnl_unlock);
 
+static struct mutex *nfnl_get_lock(__u8 subsys_id)
+{
+       return &table[subsys_id].mutex;
+}
+
 int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n)
 {
-       nfnl_lock();
-       if (subsys_table[n->subsys_id]) {
-               nfnl_unlock();
+       nfnl_lock(n->subsys_id);
+       if (table[n->subsys_id].subsys) {
+               nfnl_unlock(n->subsys_id);
                return -EBUSY;
        }
-       rcu_assign_pointer(subsys_table[n->subsys_id], n);
-       nfnl_unlock();
+       rcu_assign_pointer(table[n->subsys_id].subsys, n);
+       nfnl_unlock(n->subsys_id);
 
        return 0;
 }
@@ -76,9 +83,9 @@ EXPORT_SYMBOL_GPL(nfnetlink_subsys_register);
 
 int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n)
 {
-       nfnl_lock();
-       subsys_table[n->subsys_id] = NULL;
-       nfnl_unlock();
+       nfnl_lock(n->subsys_id);
+       table[n->subsys_id].subsys = NULL;
+       nfnl_unlock(n->subsys_id);
        synchronize_rcu();
        return 0;
 }
@@ -91,7 +98,7 @@ static inline const struct nfnetlink_subsystem *nfnetlink_get_subsys(u_int16_t t
        if (subsys_id >= NFNL_SUBSYS_COUNT)
                return NULL;
 
-       return rcu_dereference(subsys_table[subsys_id]);
+       return rcu_dereference(table[subsys_id].subsys);
 }
 
 static inline const struct nfnl_callback *
@@ -175,6 +182,7 @@ replay:
                struct nlattr *cda[ss->cb[cb_id].attr_count + 1];
                struct nlattr *attr = (void *)nlh + min_len;
                int attrlen = nlh->nlmsg_len - min_len;
+               __u8 subsys_id = NFNL_SUBSYS_ID(type);
 
                err = nla_parse(cda, ss->cb[cb_id].attr_count,
                                attr, attrlen, ss->cb[cb_id].policy);
@@ -189,10 +197,9 @@ replay:
                        rcu_read_unlock();
                } else {
                        rcu_read_unlock();
-                       nfnl_lock();
-                       if (rcu_dereference_protected(
-                                       subsys_table[NFNL_SUBSYS_ID(type)],
-                                       lockdep_is_held(&nfnl_mutex)) != ss ||
+                       nfnl_lock(subsys_id);
+                       if (rcu_dereference_protected(table[subsys_id].subsys,
+                               lockdep_is_held(nfnl_get_lock(subsys_id))) != ss ||
                            nfnetlink_find_client(type, ss) != nc)
                                err = -EAGAIN;
                        else if (nc->call)
@@ -200,7 +207,7 @@ replay:
                                                   (const struct nlattr **)cda);
                        else
                                err = -EINVAL;
-                       nfnl_unlock();
+                       nfnl_unlock(subsys_id);
                }
                if (err == -EAGAIN)
                        goto replay;
@@ -267,6 +274,11 @@ static struct pernet_operations nfnetlink_net_ops = {
 
 static int __init nfnetlink_init(void)
 {
+       int i;
+
+       for (i=0; i<NFNL_SUBSYS_COUNT; i++)
+               mutex_init(&table[i].mutex);
+
        pr_info("Netfilter messages via NETLINK v%s.\n", nfversion);
        return register_pernet_subsys(&nfnetlink_net_ops);
 }
index bde009e..a60261c 100644 (file)
 #include <net/netfilter/nf_conntrack_timeout.h>
 #include <net/netfilter/nf_conntrack_zones.h>
 
-static unsigned int xt_ct_target_v0(struct sk_buff *skb,
-                                   const struct xt_action_param *par)
+static inline int xt_ct_target(struct sk_buff *skb, struct nf_conn *ct)
 {
-       const struct xt_ct_target_info *info = par->targinfo;
-       struct nf_conn *ct = info->ct;
-
        /* Previously seen (loopback)? Ignore. */
        if (skb->nfct != NULL)
                return XT_CONTINUE;
@@ -37,21 +33,22 @@ static unsigned int xt_ct_target_v0(struct sk_buff *skb,
        return XT_CONTINUE;
 }
 
-static unsigned int xt_ct_target_v1(struct sk_buff *skb,
+static unsigned int xt_ct_target_v0(struct sk_buff *skb,
                                    const struct xt_action_param *par)
 {
-       const struct xt_ct_target_info_v1 *info = par->targinfo;
+       const struct xt_ct_target_info *info = par->targinfo;
        struct nf_conn *ct = info->ct;
 
-       /* Previously seen (loopback)? Ignore. */
-       if (skb->nfct != NULL)
-               return XT_CONTINUE;
+       return xt_ct_target(skb, ct);
+}
 
-       atomic_inc(&ct->ct_general.use);
-       skb->nfct = &ct->ct_general;
-       skb->nfctinfo = IP_CT_NEW;
+static unsigned int xt_ct_target_v1(struct sk_buff *skb,
+                                   const struct xt_action_param *par)
+{
+       const struct xt_ct_target_info_v1 *info = par->targinfo;
+       struct nf_conn *ct = info->ct;
 
-       return XT_CONTINUE;
+       return xt_ct_target(skb, ct);
 }
 
 static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
@@ -104,67 +101,6 @@ xt_ct_set_helper(struct nf_conn *ct, const char *helper_name,
        return 0;
 }
 
-static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
-{
-       struct xt_ct_target_info *info = par->targinfo;
-       struct nf_conntrack_tuple t;
-       struct nf_conn *ct;
-       int ret = -EOPNOTSUPP;
-
-       if (info->flags & ~XT_CT_NOTRACK)
-               return -EINVAL;
-
-       if (info->flags & XT_CT_NOTRACK) {
-               ct = nf_ct_untracked_get();
-               atomic_inc(&ct->ct_general.use);
-               goto out;
-       }
-
-#ifndef CONFIG_NF_CONNTRACK_ZONES
-       if (info->zone)
-               goto err1;
-#endif
-
-       ret = nf_ct_l3proto_try_module_get(par->family);
-       if (ret < 0)
-               goto err1;
-
-       memset(&t, 0, sizeof(t));
-       ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
-       ret = PTR_ERR(ct);
-       if (IS_ERR(ct))
-               goto err2;
-
-       ret = 0;
-       if ((info->ct_events || info->exp_events) &&
-           !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
-                                 GFP_KERNEL))
-               goto err3;
-
-       if (info->helper[0]) {
-               ret = xt_ct_set_helper(ct, info->helper, par);
-               if (ret < 0)
-                       goto err3;
-       }
-
-       __set_bit(IPS_TEMPLATE_BIT, &ct->status);
-       __set_bit(IPS_CONFIRMED_BIT, &ct->status);
-
-       /* Overload tuple linked list to put us in template list. */
-       hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
-                                &par->net->ct.tmpl);
-out:
-       info->ct = ct;
-       return 0;
-
-err3:
-       nf_conntrack_free(ct);
-err2:
-       nf_ct_l3proto_module_put(par->family);
-err1:
-       return ret;
-}
-
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout)
 {
@@ -242,16 +178,13 @@ out:
 #endif
 }
 
-static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
+static int xt_ct_tg_check(const struct xt_tgchk_param *par,
+                         struct xt_ct_target_info_v1 *info)
 {
-       struct xt_ct_target_info_v1 *info = par->targinfo;
        struct nf_conntrack_tuple t;
        struct nf_conn *ct;
        int ret = -EOPNOTSUPP;
 
-       if (info->flags & ~XT_CT_NOTRACK)
-               return -EINVAL;
-
        if (info->flags & XT_CT_NOTRACK) {
                ct = nf_ct_untracked_get();
                atomic_inc(&ct->ct_general.use);
@@ -309,20 +242,49 @@ err1:
        return ret;
 }
 
-static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
+static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
 {
        struct xt_ct_target_info *info = par->targinfo;
-       struct nf_conn *ct = info->ct;
-       struct nf_conn_help *help;
+       struct xt_ct_target_info_v1 info_v1 = {
+               .flags          = info->flags,
+               .zone           = info->zone,
+               .ct_events      = info->ct_events,
+               .exp_events     = info->exp_events,
+       };
+       int ret;
 
-       if (!nf_ct_is_untracked(ct)) {
-               help = nfct_help(ct);
-               if (help)
-                       module_put(help->helper->me);
+       if (info->flags & ~XT_CT_NOTRACK)
+               return -EINVAL;
 
-               nf_ct_l3proto_module_put(par->family);
-       }
-       nf_ct_put(info->ct);
+       memcpy(info_v1.helper, info->helper, sizeof(info->helper));
+
+       ret = xt_ct_tg_check(par, &info_v1);
+       if (ret < 0)
+               return ret;
+
+       info->ct = info_v1.ct;
+
+       return ret;
+}
+
+static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
+{
+       struct xt_ct_target_info_v1 *info = par->targinfo;
+
+       if (info->flags & ~XT_CT_NOTRACK)
+               return -EINVAL;
+
+       return xt_ct_tg_check(par, par->targinfo);
+}
+
+static int xt_ct_tg_check_v2(const struct xt_tgchk_param *par)
+{
+       struct xt_ct_target_info_v1 *info = par->targinfo;
+
+       if (info->flags & ~XT_CT_MASK)
+               return -EINVAL;
+
+       return xt_ct_tg_check(par, par->targinfo);
 }
 
 static void xt_ct_destroy_timeout(struct nf_conn *ct)
@@ -343,9 +305,9 @@ static void xt_ct_destroy_timeout(struct nf_conn *ct)
 #endif
 }
 
-static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
+static void xt_ct_tg_destroy(const struct xt_tgdtor_param *par,
+                            struct xt_ct_target_info_v1 *info)
 {
-       struct xt_ct_target_info_v1 *info = par->targinfo;
        struct nf_conn *ct = info->ct;
        struct nf_conn_help *help;
 
@@ -361,6 +323,26 @@ static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
        nf_ct_put(info->ct);
 }
 
+static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
+{
+       struct xt_ct_target_info *info = par->targinfo;
+       struct xt_ct_target_info_v1 info_v1 = {
+               .flags          = info->flags,
+               .zone           = info->zone,
+               .ct_events      = info->ct_events,
+               .exp_events     = info->exp_events,
+               .ct             = info->ct,
+       };
+       memcpy(info_v1.helper, info->helper, sizeof(info->helper));
+
+       xt_ct_tg_destroy(par, &info_v1);
+}
+
+static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
+{
+       xt_ct_tg_destroy(par, par->targinfo);
+}
+
 static struct xt_target xt_ct_tg_reg[] __read_mostly = {
        {
                .name           = "CT",
@@ -383,6 +365,17 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
                .table          = "raw",
                .me             = THIS_MODULE,
        },
+       {
+               .name           = "CT",
+               .family         = NFPROTO_UNSPEC,
+               .revision       = 2,
+               .targetsize     = sizeof(struct xt_ct_target_info_v1),
+               .checkentry     = xt_ct_tg_check_v2,
+               .destroy        = xt_ct_tg_destroy_v1,
+               .target         = xt_ct_target_v1,
+               .table          = "raw",
+               .me             = THIS_MODULE,
+       },
 };
 
 static unsigned int