[NETFILTER]: Introduce NF_INET_ hook values
[safe/jmp/linux-2.6] / net / netfilter / nf_conntrack_proto_tcp.c
index 3b9ad7f..d96f188 100644 (file)
@@ -4,29 +4,10 @@
  * 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.
- *
- * Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>:
- *     - Real stateful connection tracking
- *     - Modified state transitions table
- *     - Window scaling support added
- *     - SACK support added
- *
- * Willy Tarreau:
- *     - State table bugfixes
- *     - More robust state changes
- *     - Tuning timer parameters
- *
- * 27 Oct 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
- *     - genelized Layer 3 protocol part.
- *
- * Derived from net/ipv4/netfilter/ip_conntrack_proto_tcp.c
- *
- * version 2.2
  */
 
 #include <linux/types.h>
 #include <linux/timer.h>
-#include <linux/netfilter.h>
 #include <linux/module.h>
 #include <linux/in.h>
 #include <linux/tcp.h>
 #include <net/netfilter/nf_conntrack_l4proto.h>
 #include <net/netfilter/nf_conntrack_ecache.h>
 
-#if 0
-#define DEBUGP printk
-#define DEBUGP_VARS
-#else
-#define DEBUGP(format, args...)
-#endif
-
 /* Protects conntrack->proto.tcp */
 static DEFINE_RWLOCK(tcp_lock);
 
@@ -470,11 +444,10 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff,
 
        /* Fast path for timestamp-only option */
        if (length == TCPOLEN_TSTAMP_ALIGNED*4
-           && *(__be32 *)ptr ==
-               __constant_htonl((TCPOPT_NOP << 24)
-                                | (TCPOPT_NOP << 16)
-                                | (TCPOPT_TIMESTAMP << 8)
-                                | TCPOLEN_TIMESTAMP))
+           && *(__be32 *)ptr == htonl((TCPOPT_NOP << 24)
+                                      | (TCPOPT_NOP << 16)
+                                      | (TCPOPT_TIMESTAMP << 8)
+                                      | TCPOLEN_TIMESTAMP))
                return;
 
        while (length > 0) {
@@ -515,7 +488,8 @@ static void tcp_sack(const struct sk_buff *skb, unsigned int dataoff,
        }
 }
 
-static int tcp_in_window(struct ip_ct_tcp *state,
+static int tcp_in_window(struct nf_conn *ct,
+                        struct ip_ct_tcp *state,
                         enum ip_conntrack_dir dir,
                         unsigned int index,
                         const struct sk_buff *skb,
@@ -525,6 +499,7 @@ static int tcp_in_window(struct ip_ct_tcp *state,
 {
        struct ip_ct_tcp_state *sender = &state->seen[dir];
        struct ip_ct_tcp_state *receiver = &state->seen[!dir];
+       struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple;
        __u32 seq, ack, sack, end, win, swin;
        int res;
 
@@ -539,18 +514,17 @@ static int tcp_in_window(struct ip_ct_tcp *state,
        if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM)
                tcp_sack(skb, dataoff, tcph, &sack);
 
-       DEBUGP("tcp_in_window: START\n");
-       DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
-              "seq=%u ack=%u sack=%u win=%u end=%u\n",
-               NIPQUAD(iph->saddr), ntohs(tcph->source),
-               NIPQUAD(iph->daddr), ntohs(tcph->dest),
-               seq, ack, sack, win, end);
-       DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
-              "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
-               sender->td_end, sender->td_maxend, sender->td_maxwin,
-               sender->td_scale,
-               receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
-               receiver->td_scale);
+       pr_debug("tcp_in_window: START\n");
+       pr_debug("tcp_in_window: ");
+       NF_CT_DUMP_TUPLE(tuple);
+       pr_debug("seq=%u ack=%u sack=%u win=%u end=%u\n",
+                seq, ack, sack, win, end);
+       pr_debug("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
+                "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
+                sender->td_end, sender->td_maxend, sender->td_maxwin,
+                sender->td_scale,
+                receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
+                receiver->td_scale);
 
        if (sender->td_end == 0) {
                /*
@@ -628,23 +602,22 @@ static int tcp_in_window(struct ip_ct_tcp *state,
                 */
                seq = end = sender->td_end;
 
-       DEBUGP("tcp_in_window: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
-              "seq=%u ack=%u sack =%u win=%u end=%u\n",
-               NIPQUAD(iph->saddr), ntohs(tcph->source),
-               NIPQUAD(iph->daddr), ntohs(tcph->dest),
-               seq, ack, sack, win, end);
-       DEBUGP("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
-              "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
-               sender->td_end, sender->td_maxend, sender->td_maxwin,
-               sender->td_scale,
-               receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
-               receiver->td_scale);
-
-       DEBUGP("tcp_in_window: I=%i II=%i III=%i IV=%i\n",
-               before(seq, sender->td_maxend + 1),
-               after(end, sender->td_end - receiver->td_maxwin - 1),
-               before(sack, receiver->td_end + 1),
-               after(ack, receiver->td_end - MAXACKWINDOW(sender)));
+       pr_debug("tcp_in_window: ");
+       NF_CT_DUMP_TUPLE(tuple);
+       pr_debug("seq=%u ack=%u sack =%u win=%u end=%u\n",
+                seq, ack, sack, win, end);
+       pr_debug("tcp_in_window: sender end=%u maxend=%u maxwin=%u scale=%i "
+                "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
+                sender->td_end, sender->td_maxend, sender->td_maxwin,
+                sender->td_scale,
+                receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
+                receiver->td_scale);
+
+       pr_debug("tcp_in_window: I=%i II=%i III=%i IV=%i\n",
+                before(seq, sender->td_maxend + 1),
+                after(end, sender->td_end - receiver->td_maxwin - 1),
+                before(sack, receiver->td_end + 1),
+                after(ack, receiver->td_end - MAXACKWINDOW(sender)));
 
        if (before(seq, sender->td_maxend + 1) &&
            after(end, sender->td_end - receiver->td_maxwin - 1) &&
@@ -713,10 +686,10 @@ static int tcp_in_window(struct ip_ct_tcp *state,
                        : "SEQ is over the upper bound (over the window of the receiver)");
        }
 
-       DEBUGP("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u "
-              "receiver end=%u maxend=%u maxwin=%u\n",
-               res, sender->td_end, sender->td_maxend, sender->td_maxwin,
-               receiver->td_end, receiver->td_maxend, receiver->td_maxwin);
+       pr_debug("tcp_in_window: res=%i sender end=%u maxend=%u maxwin=%u "
+                "receiver end=%u maxend=%u maxwin=%u\n",
+                res, sender->td_end, sender->td_maxend, sender->td_maxwin,
+                receiver->td_end, receiver->td_maxend, receiver->td_maxwin);
 
        return res;
 }
@@ -730,11 +703,9 @@ void nf_conntrack_tcp_update(struct sk_buff *skb,
                             int dir)
 {
        struct tcphdr *tcph = (void *)skb->data + dataoff;
-       __u32 end;
-#ifdef DEBUGP_VARS
        struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[dir];
        struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[!dir];
-#endif
+       __u32 end;
 
        end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, dataoff, tcph);
 
@@ -746,12 +717,12 @@ void nf_conntrack_tcp_update(struct sk_buff *skb,
                conntrack->proto.tcp.seen[dir].td_end = end;
        conntrack->proto.tcp.last_end = end;
        write_unlock_bh(&tcp_lock);
-       DEBUGP("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i "
-              "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
-               sender->td_end, sender->td_maxend, sender->td_maxwin,
-               sender->td_scale,
-               receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
-               receiver->td_scale);
+       pr_debug("tcp_update: sender end=%u maxend=%u maxwin=%u scale=%i "
+                "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
+                sender->td_end, sender->td_maxend, sender->td_maxwin,
+                sender->td_scale,
+                receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
+                receiver->td_scale);
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_tcp_update);
 #endif
@@ -765,24 +736,18 @@ EXPORT_SYMBOL_GPL(nf_conntrack_tcp_update);
 #define        TH_ECE  0x40
 #define        TH_CWR  0x80
 
-/* table of valid flag combinations - ECE and CWR are always valid */
-static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] =
+/* table of valid flag combinations - PUSH, ECE and CWR are always valid */
+static u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG) + 1] =
 {
        [TH_SYN]                        = 1,
+       [TH_SYN|TH_URG]                 = 1,
        [TH_SYN|TH_ACK]                 = 1,
-       [TH_SYN|TH_PUSH]                = 1,
-       [TH_SYN|TH_ACK|TH_PUSH]         = 1,
        [TH_RST]                        = 1,
        [TH_RST|TH_ACK]                 = 1,
-       [TH_RST|TH_ACK|TH_PUSH]         = 1,
        [TH_FIN|TH_ACK]                 = 1,
+       [TH_FIN|TH_ACK|TH_URG]          = 1,
        [TH_ACK]                        = 1,
-       [TH_ACK|TH_PUSH]                = 1,
        [TH_ACK|TH_URG]                 = 1,
-       [TH_ACK|TH_URG|TH_PUSH]         = 1,
-       [TH_FIN|TH_ACK|TH_PUSH]         = 1,
-       [TH_FIN|TH_ACK|TH_URG]          = 1,
-       [TH_FIN|TH_ACK|TH_URG|TH_PUSH]  = 1,
 };
 
 /* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c.  */
@@ -818,9 +783,7 @@ static int tcp_error(struct sk_buff *skb,
         * because the checksum is assumed to be correct.
         */
        /* FIXME: Source route IP option packets --RR */
-       if (nf_conntrack_checksum &&
-           ((pf == PF_INET && hooknum == NF_IP_PRE_ROUTING) ||
-            (pf == PF_INET6 && hooknum == NF_IP6_PRE_ROUTING)) &&
+       if (nf_conntrack_checksum && hooknum == NF_INET_PRE_ROUTING &&
            nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) {
                if (LOG_INVALID(IPPROTO_TCP))
                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
@@ -829,7 +792,7 @@ static int tcp_error(struct sk_buff *skb,
        }
 
        /* Check TCP flags. */
-       tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR));
+       tcpflags = (((u_int8_t *)th)[13] & ~(TH_ECE|TH_CWR|TH_PUSH));
        if (!tcp_valid_flags[tcpflags]) {
                if (LOG_INVALID(IPPROTO_TCP))
                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
@@ -848,6 +811,7 @@ static int tcp_packet(struct nf_conn *conntrack,
                      int pf,
                      unsigned int hooknum)
 {
+       struct nf_conntrack_tuple *tuple;
        enum tcp_conntrack new_state, old_state;
        enum ip_conntrack_dir dir;
        struct tcphdr *th, _tcph;
@@ -862,8 +826,25 @@ static int tcp_packet(struct nf_conn *conntrack,
        dir = CTINFO2DIR(ctinfo);
        index = get_conntrack_index(th);
        new_state = tcp_conntracks[dir][index][old_state];
+       tuple = &conntrack->tuplehash[dir].tuple;
 
        switch (new_state) {
+       case TCP_CONNTRACK_SYN_SENT:
+               if (old_state < TCP_CONNTRACK_TIME_WAIT)
+                       break;
+               if ((conntrack->proto.tcp.seen[!dir].flags &
+                       IP_CT_TCP_FLAG_CLOSE_INIT)
+                   || (conntrack->proto.tcp.last_dir == dir
+                       && conntrack->proto.tcp.last_index == TCP_RST_SET)) {
+                       /* Attempt to reopen a closed/aborted connection.
+                        * Delete this connection and look up again. */
+                       write_unlock_bh(&tcp_lock);
+                       if (del_timer(&conntrack->timeout))
+                               conntrack->timeout.function((unsigned long)
+                                                           conntrack);
+                       return -NF_REPEAT;
+               }
+               /* Fall through */
        case TCP_CONNTRACK_IGNORE:
                /* Ignored packets:
                 *
@@ -905,35 +886,13 @@ static int tcp_packet(struct nf_conn *conntrack,
                return NF_ACCEPT;
        case TCP_CONNTRACK_MAX:
                /* Invalid packet */
-               DEBUGP("nf_ct_tcp: Invalid dir=%i index=%u ostate=%u\n",
-                      dir, get_conntrack_index(th),
-                      old_state);
+               pr_debug("nf_ct_tcp: Invalid dir=%i index=%u ostate=%u\n",
+                        dir, get_conntrack_index(th), old_state);
                write_unlock_bh(&tcp_lock);
                if (LOG_INVALID(IPPROTO_TCP))
                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
                                  "nf_ct_tcp: invalid state ");
                return -NF_ACCEPT;
-       case TCP_CONNTRACK_SYN_SENT:
-               if (old_state < TCP_CONNTRACK_TIME_WAIT)
-                       break;
-               if ((conntrack->proto.tcp.seen[dir].flags &
-                       IP_CT_TCP_FLAG_CLOSE_INIT)
-                   || after(ntohl(th->seq),
-                            conntrack->proto.tcp.seen[dir].td_end)) {
-                       /* Attempt to reopen a closed connection.
-                       * Delete this connection and look up again. */
-                       write_unlock_bh(&tcp_lock);
-                       if (del_timer(&conntrack->timeout))
-                               conntrack->timeout.function((unsigned long)
-                                                           conntrack);
-                       return -NF_REPEAT;
-               } else {
-                       write_unlock_bh(&tcp_lock);
-                       if (LOG_INVALID(IPPROTO_TCP))
-                               nf_log_packet(pf, 0, skb, NULL, NULL,
-                                             NULL, "nf_ct_tcp: invalid SYN");
-                       return -NF_ACCEPT;
-               }
        case TCP_CONNTRACK_CLOSE:
                if (index == TCP_RST_SET
                    && ((test_bit(IPS_SEEN_REPLY_BIT, &conntrack->status)
@@ -958,7 +917,7 @@ static int tcp_packet(struct nf_conn *conntrack,
                break;
        }
 
-       if (!tcp_in_window(&conntrack->proto.tcp, dir, index,
+       if (!tcp_in_window(conntrack, &conntrack->proto.tcp, dir, index,
                           skb, dataoff, th, pf)) {
                write_unlock_bh(&tcp_lock);
                return -NF_ACCEPT;
@@ -966,14 +925,14 @@ static int tcp_packet(struct nf_conn *conntrack,
      in_window:
        /* From now on we have got in-window packets */
        conntrack->proto.tcp.last_index = index;
+       conntrack->proto.tcp.last_dir = dir;
 
-       DEBUGP("tcp_conntracks: src=%u.%u.%u.%u:%hu dst=%u.%u.%u.%u:%hu "
-              "syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n",
-               NIPQUAD(iph->saddr), ntohs(th->source),
-               NIPQUAD(iph->daddr), ntohs(th->dest),
-               (th->syn ? 1 : 0), (th->ack ? 1 : 0),
-               (th->fin ? 1 : 0), (th->rst ? 1 : 0),
-               old_state, new_state);
+       pr_debug("tcp_conntracks: ");
+       NF_CT_DUMP_TUPLE(tuple);
+       pr_debug("syn=%i ack=%i fin=%i rst=%i old=%i new=%i\n",
+                (th->syn ? 1 : 0), (th->ack ? 1 : 0),
+                (th->fin ? 1 : 0), (th->rst ? 1 : 0),
+                old_state, new_state);
 
        conntrack->proto.tcp.state = new_state;
        if (old_state != new_state
@@ -1022,10 +981,8 @@ static int tcp_new(struct nf_conn *conntrack,
 {
        enum tcp_conntrack new_state;
        struct tcphdr *th, _tcph;
-#ifdef DEBUGP_VARS
        struct ip_ct_tcp_state *sender = &conntrack->proto.tcp.seen[0];
        struct ip_ct_tcp_state *receiver = &conntrack->proto.tcp.seen[1];
-#endif
 
        th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
        BUG_ON(th == NULL);
@@ -1037,7 +994,7 @@ static int tcp_new(struct nf_conn *conntrack,
 
        /* Invalid: delete conntrack */
        if (new_state >= TCP_CONNTRACK_MAX) {
-               DEBUGP("nf_ct_tcp: invalid new deleting.\n");
+               pr_debug("nf_ct_tcp: invalid new deleting.\n");
                return 0;
        }
 
@@ -1090,12 +1047,12 @@ static int tcp_new(struct nf_conn *conntrack,
        conntrack->proto.tcp.state = TCP_CONNTRACK_NONE;
        conntrack->proto.tcp.last_index = TCP_NONE_SET;
 
-       DEBUGP("tcp_new: sender end=%u maxend=%u maxwin=%u scale=%i "
-              "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
-               sender->td_end, sender->td_maxend, sender->td_maxwin,
-               sender->td_scale,
-               receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
-               receiver->td_scale);
+       pr_debug("tcp_new: sender end=%u maxend=%u maxwin=%u scale=%i "
+                "receiver end=%u maxend=%u maxwin=%u scale=%i\n",
+                sender->td_end, sender->td_maxend, sender->td_maxwin,
+                sender->td_scale,
+                receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
+                receiver->td_scale);
        return 1;
 }
 
@@ -1104,51 +1061,97 @@ static int tcp_new(struct nf_conn *conntrack,
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nfnetlink_conntrack.h>
 
-static int tcp_to_nfattr(struct sk_buff *skb, struct nfattr *nfa,
+static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
                         const struct nf_conn *ct)
 {
-       struct nfattr *nest_parms;
+       struct nlattr *nest_parms;
+       struct nf_ct_tcp_flags tmp = {};
 
        read_lock_bh(&tcp_lock);
-       nest_parms = NFA_NEST(skb, CTA_PROTOINFO_TCP);
-       NFA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
+       nest_parms = nla_nest_start(skb, CTA_PROTOINFO_TCP | NLA_F_NESTED);
+       if (!nest_parms)
+               goto nla_put_failure;
+
+       NLA_PUT(skb, CTA_PROTOINFO_TCP_STATE, sizeof(u_int8_t),
                &ct->proto.tcp.state);
+
+       NLA_PUT(skb, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, sizeof(u_int8_t),
+               &ct->proto.tcp.seen[0].td_scale);
+
+       NLA_PUT(skb, CTA_PROTOINFO_TCP_WSCALE_REPLY, sizeof(u_int8_t),
+               &ct->proto.tcp.seen[1].td_scale);
+
+       tmp.flags = ct->proto.tcp.seen[0].flags;
+       NLA_PUT(skb, CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
+               sizeof(struct nf_ct_tcp_flags), &tmp);
+
+       tmp.flags = ct->proto.tcp.seen[1].flags;
+       NLA_PUT(skb, CTA_PROTOINFO_TCP_FLAGS_REPLY,
+               sizeof(struct nf_ct_tcp_flags), &tmp);
        read_unlock_bh(&tcp_lock);
 
-       NFA_NEST_END(skb, nest_parms);
+       nla_nest_end(skb, nest_parms);
 
        return 0;
 
-nfattr_failure:
+nla_put_failure:
        read_unlock_bh(&tcp_lock);
        return -1;
 }
 
-static const size_t cta_min_tcp[CTA_PROTOINFO_TCP_MAX] = {
-       [CTA_PROTOINFO_TCP_STATE-1]     = sizeof(u_int8_t),
+static const struct nla_policy tcp_nla_policy[CTA_PROTOINFO_TCP_MAX+1] = {
+       [CTA_PROTOINFO_TCP_STATE]           = { .type = NLA_U8 },
+       [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NLA_U8 },
+       [CTA_PROTOINFO_TCP_WSCALE_REPLY]    = { .type = NLA_U8 },
+       [CTA_PROTOINFO_TCP_FLAGS_ORIGINAL]  = { .len = sizeof(struct nf_ct_tcp_flags) },
+       [CTA_PROTOINFO_TCP_FLAGS_REPLY]     = { .len =  sizeof(struct nf_ct_tcp_flags) },
 };
 
-static int nfattr_to_tcp(struct nfattr *cda[], struct nf_conn *ct)
+static int nlattr_to_tcp(struct nlattr *cda[], struct nf_conn *ct)
 {
-       struct nfattr *attr = cda[CTA_PROTOINFO_TCP-1];
-       struct nfattr *tb[CTA_PROTOINFO_TCP_MAX];
+       struct nlattr *attr = cda[CTA_PROTOINFO_TCP];
+       struct nlattr *tb[CTA_PROTOINFO_TCP_MAX+1];
+       int err;
 
        /* updates could not contain anything about the private
         * protocol info, in that case skip the parsing */
        if (!attr)
                return 0;
 
-       nfattr_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr);
-
-       if (nfattr_bad_size(tb, CTA_PROTOINFO_TCP_MAX, cta_min_tcp))
-               return -EINVAL;
+       err = nla_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr, tcp_nla_policy);
+       if (err < 0)
+               return err;
 
-       if (!tb[CTA_PROTOINFO_TCP_STATE-1])
+       if (!tb[CTA_PROTOINFO_TCP_STATE])
                return -EINVAL;
 
        write_lock_bh(&tcp_lock);
        ct->proto.tcp.state =
-               *(u_int8_t *)NFA_DATA(tb[CTA_PROTOINFO_TCP_STATE-1]);
+               *(u_int8_t *)nla_data(tb[CTA_PROTOINFO_TCP_STATE]);
+
+       if (tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL]) {
+               struct nf_ct_tcp_flags *attr =
+                       nla_data(tb[CTA_PROTOINFO_TCP_FLAGS_ORIGINAL]);
+               ct->proto.tcp.seen[0].flags &= ~attr->mask;
+               ct->proto.tcp.seen[0].flags |= attr->flags & attr->mask;
+       }
+
+       if (tb[CTA_PROTOINFO_TCP_FLAGS_REPLY]) {
+               struct nf_ct_tcp_flags *attr =
+                       nla_data(tb[CTA_PROTOINFO_TCP_FLAGS_REPLY]);
+               ct->proto.tcp.seen[1].flags &= ~attr->mask;
+               ct->proto.tcp.seen[1].flags |= attr->flags & attr->mask;
+       }
+
+       if (tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] &&
+           tb[CTA_PROTOINFO_TCP_WSCALE_REPLY] &&
+           ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_WINDOW_SCALE &&
+           ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_WINDOW_SCALE) {
+               ct->proto.tcp.seen[0].td_scale = *(u_int8_t *)
+                       nla_data(tb[CTA_PROTOINFO_TCP_WSCALE_ORIGINAL]);
+               ct->proto.tcp.seen[1].td_scale = *(u_int8_t *)
+                       nla_data(tb[CTA_PROTOINFO_TCP_WSCALE_REPLY]);
+       }
        write_unlock_bh(&tcp_lock);
 
        return 0;
@@ -1160,7 +1163,6 @@ static unsigned int tcp_sysctl_table_users;
 static struct ctl_table_header *tcp_sysctl_header;
 static struct ctl_table tcp_sysctl_table[] = {
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT,
                .procname       = "nf_conntrack_tcp_timeout_syn_sent",
                .data           = &nf_ct_tcp_timeout_syn_sent,
                .maxlen         = sizeof(unsigned int),
@@ -1168,7 +1170,6 @@ static struct ctl_table tcp_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV,
                .procname       = "nf_conntrack_tcp_timeout_syn_recv",
                .data           = &nf_ct_tcp_timeout_syn_recv,
                .maxlen         = sizeof(unsigned int),
@@ -1176,7 +1177,6 @@ static struct ctl_table tcp_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED,
                .procname       = "nf_conntrack_tcp_timeout_established",
                .data           = &nf_ct_tcp_timeout_established,
                .maxlen         = sizeof(unsigned int),
@@ -1184,7 +1184,6 @@ static struct ctl_table tcp_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT,
                .procname       = "nf_conntrack_tcp_timeout_fin_wait",
                .data           = &nf_ct_tcp_timeout_fin_wait,
                .maxlen         = sizeof(unsigned int),
@@ -1192,7 +1191,6 @@ static struct ctl_table tcp_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT,
                .procname       = "nf_conntrack_tcp_timeout_close_wait",
                .data           = &nf_ct_tcp_timeout_close_wait,
                .maxlen         = sizeof(unsigned int),
@@ -1200,7 +1198,6 @@ static struct ctl_table tcp_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK,
                .procname       = "nf_conntrack_tcp_timeout_last_ack",
                .data           = &nf_ct_tcp_timeout_last_ack,
                .maxlen         = sizeof(unsigned int),
@@ -1208,7 +1205,6 @@ static struct ctl_table tcp_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT,
                .procname       = "nf_conntrack_tcp_timeout_time_wait",
                .data           = &nf_ct_tcp_timeout_time_wait,
                .maxlen         = sizeof(unsigned int),
@@ -1216,7 +1212,6 @@ static struct ctl_table tcp_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_CLOSE,
                .procname       = "nf_conntrack_tcp_timeout_close",
                .data           = &nf_ct_tcp_timeout_close,
                .maxlen         = sizeof(unsigned int),
@@ -1224,7 +1219,6 @@ static struct ctl_table tcp_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS,
                .procname       = "nf_conntrack_tcp_timeout_max_retrans",
                .data           = &nf_ct_tcp_timeout_max_retrans,
                .maxlen         = sizeof(unsigned int),
@@ -1263,7 +1257,6 @@ static struct ctl_table tcp_sysctl_table[] = {
 #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
 static struct ctl_table tcp_compat_sysctl_table[] = {
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT,
                .procname       = "ip_conntrack_tcp_timeout_syn_sent",
                .data           = &nf_ct_tcp_timeout_syn_sent,
                .maxlen         = sizeof(unsigned int),
@@ -1271,7 +1264,6 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_RECV,
                .procname       = "ip_conntrack_tcp_timeout_syn_recv",
                .data           = &nf_ct_tcp_timeout_syn_recv,
                .maxlen         = sizeof(unsigned int),
@@ -1279,7 +1271,6 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED,
                .procname       = "ip_conntrack_tcp_timeout_established",
                .data           = &nf_ct_tcp_timeout_established,
                .maxlen         = sizeof(unsigned int),
@@ -1287,7 +1278,6 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_FIN_WAIT,
                .procname       = "ip_conntrack_tcp_timeout_fin_wait",
                .data           = &nf_ct_tcp_timeout_fin_wait,
                .maxlen         = sizeof(unsigned int),
@@ -1295,7 +1285,6 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE_WAIT,
                .procname       = "ip_conntrack_tcp_timeout_close_wait",
                .data           = &nf_ct_tcp_timeout_close_wait,
                .maxlen         = sizeof(unsigned int),
@@ -1303,7 +1292,6 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_LAST_ACK,
                .procname       = "ip_conntrack_tcp_timeout_last_ack",
                .data           = &nf_ct_tcp_timeout_last_ack,
                .maxlen         = sizeof(unsigned int),
@@ -1311,7 +1299,6 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_TIME_WAIT,
                .procname       = "ip_conntrack_tcp_timeout_time_wait",
                .data           = &nf_ct_tcp_timeout_time_wait,
                .maxlen         = sizeof(unsigned int),
@@ -1319,7 +1306,6 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_CLOSE,
                .procname       = "ip_conntrack_tcp_timeout_close",
                .data           = &nf_ct_tcp_timeout_close,
                .maxlen         = sizeof(unsigned int),
@@ -1327,7 +1313,6 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
                .proc_handler   = &proc_dointvec_jiffies,
        },
        {
-               .ctl_name       = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_MAX_RETRANS,
                .procname       = "ip_conntrack_tcp_timeout_max_retrans",
                .data           = &nf_ct_tcp_timeout_max_retrans,
                .maxlen         = sizeof(unsigned int),
@@ -1365,7 +1350,7 @@ static struct ctl_table tcp_compat_sysctl_table[] = {
 #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
 #endif /* CONFIG_SYSCTL */
 
-struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 =
+struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly =
 {
        .l3proto                = PF_INET,
        .l4proto                = IPPROTO_TCP,
@@ -1378,10 +1363,11 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 =
        .new                    = tcp_new,
        .error                  = tcp_error,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
-       .to_nfattr              = tcp_to_nfattr,
-       .from_nfattr            = nfattr_to_tcp,
-       .tuple_to_nfattr        = nf_ct_port_tuple_to_nfattr,
-       .nfattr_to_tuple        = nf_ct_port_nfattr_to_tuple,
+       .to_nlattr              = tcp_to_nlattr,
+       .from_nlattr            = nlattr_to_tcp,
+       .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
+       .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
+       .nla_policy             = nf_ct_port_nla_policy,
 #endif
 #ifdef CONFIG_SYSCTL
        .ctl_table_users        = &tcp_sysctl_table_users,
@@ -1394,7 +1380,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 =
 };
 EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_tcp4);
 
-struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 =
+struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly =
 {
        .l3proto                = PF_INET6,
        .l4proto                = IPPROTO_TCP,
@@ -1407,10 +1393,11 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 =
        .new                    = tcp_new,
        .error                  = tcp_error,
 #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
-       .to_nfattr              = tcp_to_nfattr,
-       .from_nfattr            = nfattr_to_tcp,
-       .tuple_to_nfattr        = nf_ct_port_tuple_to_nfattr,
-       .nfattr_to_tuple        = nf_ct_port_nfattr_to_tuple,
+       .to_nlattr              = tcp_to_nlattr,
+       .from_nlattr            = nlattr_to_tcp,
+       .tuple_to_nlattr        = nf_ct_port_tuple_to_nlattr,
+       .nlattr_to_tuple        = nf_ct_port_nlattr_to_tuple,
+       .nla_policy             = nf_ct_port_nla_policy,
 #endif
 #ifdef CONFIG_SYSCTL
        .ctl_table_users        = &tcp_sysctl_table_users,