Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/davem/net...
authorPatrick McHardy <kaber@trash.net>
Thu, 11 Jun 2009 14:00:49 +0000 (16:00 +0200)
committerPatrick McHardy <kaber@trash.net>
Thu, 11 Jun 2009 14:00:49 +0000 (16:00 +0200)
1  2 
include/linux/netfilter/nf_conntrack_tcp.h
net/ipv4/netfilter/ipt_MASQUERADE.c
net/netfilter/nf_conntrack_proto_dccp.c
net/netfilter/nf_conntrack_proto_gre.c
net/netfilter/nf_conntrack_proto_tcp.c

@@@ -15,8 -15,7 +15,8 @@@ enum tcp_conntrack 
        TCP_CONNTRACK_LAST_ACK,
        TCP_CONNTRACK_TIME_WAIT,
        TCP_CONNTRACK_CLOSE,
 -      TCP_CONNTRACK_LISTEN,
 +      TCP_CONNTRACK_LISTEN,   /* obsolete */
 +#define TCP_CONNTRACK_SYN_SENT2       TCP_CONNTRACK_LISTEN
        TCP_CONNTRACK_MAX,
        TCP_CONNTRACK_IGNORE
  };
@@@ -36,6 -35,9 +36,9 @@@
  /* Has unacknowledged data */
  #define IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED    0x10
  
+ /* The field td_maxack has been set */
+ #define IP_CT_TCP_FLAG_MAXACK_SET             0x20
  struct nf_ct_tcp_flags {
        __u8 flags;
        __u8 mask;
@@@ -47,6 -49,7 +50,7 @@@ struct ip_ct_tcp_state 
        u_int32_t       td_end;         /* max of seq + len */
        u_int32_t       td_maxend;      /* max of ack + max(win, 1) */
        u_int32_t       td_maxwin;      /* max(win) */
+       u_int32_t       td_maxack;      /* max of ack */
        u_int8_t        td_scale;       /* window scale factor */
        u_int8_t        flags;          /* per direction options */
  };
@@@ -27,6 -27,9 +27,6 @@@ MODULE_LICENSE("GPL")
  MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
  MODULE_DESCRIPTION("Xtables: automatic-address SNAT");
  
 -/* Lock protects masq region inside conntrack */
 -static DEFINE_RWLOCK(masq_lock);
 -
  /* FIXME: Multiple targets. --RR */
  static bool masquerade_tg_check(const struct xt_tgchk_param *par)
  {
@@@ -69,14 -72,16 +69,14 @@@ masquerade_tg(struct sk_buff *skb, cons
                return NF_ACCEPT;
  
        mr = par->targinfo;
-       rt = skb->rtable;
+       rt = skb_rtable(skb);
        newsrc = inet_select_addr(par->out, rt->rt_gateway, RT_SCOPE_UNIVERSE);
        if (!newsrc) {
                printk("MASQUERADE: %s ate my IP address\n", par->out->name);
                return NF_DROP;
        }
  
 -      write_lock_bh(&masq_lock);
        nat->masq_index = par->out->ifindex;
 -      write_unlock_bh(&masq_lock);
  
        /* Transfer from original range. */
        newrange = ((struct nf_nat_range)
@@@ -92,11 -97,16 +92,11 @@@ static in
  device_cmp(struct nf_conn *i, void *ifindex)
  {
        const struct nf_conn_nat *nat = nfct_nat(i);
 -      int ret;
  
        if (!nat)
                return 0;
  
 -      read_lock_bh(&masq_lock);
 -      ret = (nat->masq_index == (int)(long)ifindex);
 -      read_unlock_bh(&masq_lock);
 -
 -      return ret;
 +      return nat->masq_index == (int)(long)ifindex;
  }
  
  static int masq_device_event(struct notifier_block *this,
  #include <linux/netfilter/nfnetlink_conntrack.h>
  #include <net/netfilter/nf_conntrack.h>
  #include <net/netfilter/nf_conntrack_l4proto.h>
+ #include <net/netfilter/nf_conntrack_ecache.h>
  #include <net/netfilter/nf_log.h>
  
 -static DEFINE_RWLOCK(dccp_lock);
 -
  /* Timeouts are based on values from RFC4340:
   *
   * - REQUEST:
@@@ -489,7 -492,7 +490,7 @@@ static int dccp_packet(struct nf_conn *
                return NF_ACCEPT;
        }
  
 -      write_lock_bh(&dccp_lock);
 +      spin_lock_bh(&ct->lock);
  
        role = ct->proto.dccp.role[dir];
        old_state = ct->proto.dccp.state;
                ct->proto.dccp.last_dir = dir;
                ct->proto.dccp.last_pkt = type;
  
 -              write_unlock_bh(&dccp_lock);
 +              spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_DCCP))
                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_dccp: invalid packet ignored ");
                return NF_ACCEPT;
        case CT_DCCP_INVALID:
 -              write_unlock_bh(&dccp_lock);
 +              spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_DCCP))
                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
                                      "nf_ct_dccp: invalid state transition ");
        ct->proto.dccp.last_dir = dir;
        ct->proto.dccp.last_pkt = type;
        ct->proto.dccp.state = new_state;
 -      write_unlock_bh(&dccp_lock);
 +      spin_unlock_bh(&ct->lock);
  
+       if (new_state != old_state)
+               nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
        dn = dccp_pernet(net);
        nf_ct_refresh_acct(ct, ctinfo, skb, dn->dccp_timeout[new_state]);
  
@@@ -615,39 -621,36 +619,39 @@@ static int dccp_print_tuple(struct seq_
                          ntohs(tuple->dst.u.dccp.port));
  }
  
 -static int dccp_print_conntrack(struct seq_file *s, const struct nf_conn *ct)
 +static int dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
  {
        return seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]);
  }
  
  #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
  static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
 -                        const struct nf_conn *ct)
 +                        struct nf_conn *ct)
  {
        struct nlattr *nest_parms;
  
 -      read_lock_bh(&dccp_lock);
 +      spin_lock_bh(&ct->lock);
        nest_parms = nla_nest_start(skb, CTA_PROTOINFO_DCCP | NLA_F_NESTED);
        if (!nest_parms)
                goto nla_put_failure;
        NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_STATE, ct->proto.dccp.state);
        NLA_PUT_U8(skb, CTA_PROTOINFO_DCCP_ROLE,
                   ct->proto.dccp.role[IP_CT_DIR_ORIGINAL]);
 +      NLA_PUT_BE64(skb, CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
 +                   cpu_to_be64(ct->proto.dccp.handshake_seq));
        nla_nest_end(skb, nest_parms);
 -      read_unlock_bh(&dccp_lock);
 +      spin_unlock_bh(&ct->lock);
        return 0;
  
  nla_put_failure:
 -      read_unlock_bh(&dccp_lock);
 +      spin_unlock_bh(&ct->lock);
        return -1;
  }
  
  static const struct nla_policy dccp_nla_policy[CTA_PROTOINFO_DCCP_MAX + 1] = {
        [CTA_PROTOINFO_DCCP_STATE]      = { .type = NLA_U8 },
        [CTA_PROTOINFO_DCCP_ROLE]       = { .type = NLA_U8 },
 +      [CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ] = { .type = NLA_U64 },
  };
  
  static int nlattr_to_dccp(struct nlattr *cda[], struct nf_conn *ct)
                return -EINVAL;
        }
  
 -      write_lock_bh(&dccp_lock);
 +      spin_lock_bh(&ct->lock);
        ct->proto.dccp.state = nla_get_u8(tb[CTA_PROTOINFO_DCCP_STATE]);
        if (nla_get_u8(tb[CTA_PROTOINFO_DCCP_ROLE]) == CT_DCCP_ROLE_CLIENT) {
                ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_CLIENT;
                ct->proto.dccp.role[IP_CT_DIR_ORIGINAL] = CT_DCCP_ROLE_SERVER;
                ct->proto.dccp.role[IP_CT_DIR_REPLY] = CT_DCCP_ROLE_CLIENT;
        }
 -      write_unlock_bh(&dccp_lock);
 +      if (tb[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ]) {
 +              ct->proto.dccp.handshake_seq =
 +              be64_to_cpu(nla_get_be64(tb[CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ]));
 +      }
 +      spin_unlock_bh(&ct->lock);
        return 0;
  }
  
@@@ -176,7 -176,7 +176,7 @@@ static bool gre_invert_tuple(struct nf_
  static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
                             struct nf_conntrack_tuple *tuple)
  {
-       struct net *net = dev_net(skb->dev ? skb->dev : skb->dst->dev);
+       struct net *net = dev_net(skb->dev ? skb->dev : skb_dst(skb)->dev);
        const struct gre_hdr_pptp *pgrehdr;
        struct gre_hdr_pptp _pgrehdr;
        __be16 srckey;
@@@ -219,7 -219,8 +219,7 @@@ static int gre_print_tuple(struct seq_f
  }
  
  /* print private data for conntrack */
 -static int gre_print_conntrack(struct seq_file *s,
 -                             const struct nf_conn *ct)
 +static int gre_print_conntrack(struct seq_file *s, struct nf_conn *ct)
  {
        return seq_printf(s, "timeout=%u, stream_timeout=%u ",
                          (ct->proto.gre.timeout / HZ),
@@@ -29,6 -29,9 +29,6 @@@
  #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
  #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
  
 -/* Protects ct->proto.tcp */
 -static DEFINE_RWLOCK(tcp_lock);
 -
  /* "Be conservative in what you do,
      be liberal in what you accept from others."
      If it's non-zero, we mark only out of window RST segments as INVALID. */
@@@ -56,7 -59,7 +56,7 @@@ static const char *const tcp_conntrack_
        "LAST_ACK",
        "TIME_WAIT",
        "CLOSE",
 -      "LISTEN"
 +      "SYN_SENT2",
  };
  
  #define SECS * HZ
@@@ -79,7 -82,6 +79,7 @@@ static unsigned int tcp_timeouts[TCP_CO
        [TCP_CONNTRACK_LAST_ACK]        = 30 SECS,
        [TCP_CONNTRACK_TIME_WAIT]       = 2 MINS,
        [TCP_CONNTRACK_CLOSE]           = 10 SECS,
 +      [TCP_CONNTRACK_SYN_SENT2]       = 2 MINS,
  };
  
  #define sNO TCP_CONNTRACK_NONE
@@@ -91,7 -93,7 +91,7 @@@
  #define sLA TCP_CONNTRACK_LAST_ACK
  #define sTW TCP_CONNTRACK_TIME_WAIT
  #define sCL TCP_CONNTRACK_CLOSE
 -#define sLI TCP_CONNTRACK_LISTEN
 +#define sS2 TCP_CONNTRACK_SYN_SENT2
  #define sIV TCP_CONNTRACK_MAX
  #define sIG TCP_CONNTRACK_IGNORE
  
@@@ -121,7 -123,6 +121,7 @@@ enum tcp_bit_set 
   *
   * NONE:      initial state
   * SYN_SENT:  SYN-only packet seen
 + * SYN_SENT2: SYN-only packet seen from reply dir, simultaneous open
   * SYN_RECV:  SYN-ACK packet seen
   * ESTABLISHED:       ACK packet seen
   * FIN_WAIT:  FIN packet seen
   * TIME_WAIT: last ACK seen
   * CLOSE:     closed connection (RST)
   *
 - * LISTEN state is not used.
 - *
   * Packets marked as IGNORED (sIG):
   *    if they may be either invalid or valid
   *    and the receiver may send back a connection
   *    closing RST or a SYN/ACK.
   *
   * Packets marked as INVALID (sIV):
 - *    if they are invalid
 - *    or we do not support the request (simultaneous open)
 + *    if we regard them as truly invalid packets
   */
  static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
        {
  /* ORIGINAL */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 -/*syn*/          { sSS, sSS, sIG, sIG, sIG, sIG, sIG, sSS, sSS, sIV },
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
 +/*syn*/          { sSS, sSS, sIG, sIG, sIG, sIG, sIG, sSS, sSS, sS2 },
  /*
   *    sNO -> sSS      Initialize a new connection
   *    sSS -> sSS      Retransmitted SYN
 - *    sSR -> sIG      Late retransmitted SYN?
 + *    sS2 -> sS2      Late retransmitted SYN
 + *    sSR -> sIG
   *    sES -> sIG      Error: SYNs in window outside the SYN_SENT state
   *                    are errors. Receiver will reply with RST
   *                    and close the connection.
   *    sTW -> sSS      Reopened connection (RFC 1122).
   *    sCL -> sSS
   */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 -/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
 +/*synack*/ { sIV, sIV, sIG, sIG, sIG, sIG, sIG, sIG, sIG, sSR },
  /*
 - * A SYN/ACK from the client is always invalid:
 - *    - either it tries to set up a simultaneous open, which is
 - *      not supported;
 - *    - or the firewall has just been inserted between the two hosts
 - *      during the session set-up. The SYN will be retransmitted
 - *      by the true client (or it'll time out).
 + *    sNO -> sIV      Too late and no reason to do anything
 + *    sSS -> sIV      Client can't send SYN and then SYN/ACK
 + *    sS2 -> sSR      SYN/ACK sent to SYN2 in simultaneous open
 + *    sSR -> sIG
 + *    sES -> sIG      Error: SYNs in window outside the SYN_SENT state
 + *                    are errors. Receiver will reply with RST
 + *                    and close the connection.
 + *                    Or we are not in sync and hold a dead connection.
 + *    sFW -> sIG
 + *    sCW -> sIG
 + *    sLA -> sIG
 + *    sTW -> sIG
 + *    sCL -> sIG
   */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
  /*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
  /*
   *    sNO -> sIV      Too late and no reason to do anything...
   *    sSS -> sIV      Client migth not send FIN in this state:
   *                    we enforce waiting for a SYN/ACK reply first.
 + *    sS2 -> sIV
   *    sSR -> sFW      Close started.
   *    sES -> sFW
   *    sFW -> sLA      FIN seen in both directions, waiting for
   *    sTW -> sTW
   *    sCL -> sCL
   */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
  /*ack*/          { sES, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV },
  /*
   *    sNO -> sES      Assumed.
   *    sSS -> sIV      ACK is invalid: we haven't seen a SYN/ACK yet.
 + *    sS2 -> sIV
   *    sSR -> sES      Established state is reached.
   *    sES -> sES      :-)
   *    sFW -> sCW      Normal close request answered by ACK.
   *    sTW -> sTW      Retransmitted last ACK. Remain in the same state.
   *    sCL -> sCL
   */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 -/*rst*/    { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
 +/*rst*/    { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL },
  /*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
        },
        {
  /* REPLY */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 -/*syn*/          { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
 +/*syn*/          { sIV, sS2, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sS2 },
  /*
   *    sNO -> sIV      Never reached.
 - *    sSS -> sIV      Simultaneous open, not supported
 - *    sSR -> sIV      Simultaneous open, not supported.
 - *    sES -> sIV      Server may not initiate a connection.
 + *    sSS -> sS2      Simultaneous open
 + *    sS2 -> sS2      Retransmitted simultaneous SYN
 + *    sSR -> sIV      Invalid SYN packets sent by the server
 + *    sES -> sIV
   *    sFW -> sIV
   *    sCW -> sIV
   *    sLA -> sIV
   *    sTW -> sIV      Reopened connection, but server may not do it.
   *    sCL -> sIV
   */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 -/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sIV },
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
 +/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sSR },
  /*
   *    sSS -> sSR      Standard open.
 + *    sS2 -> sSR      Simultaneous open
   *    sSR -> sSR      Retransmitted SYN/ACK.
   *    sES -> sIG      Late retransmitted SYN/ACK?
   *    sFW -> sIG      Might be SYN/ACK answering ignored SYN
   *    sTW -> sIG
   *    sCL -> sIG
   */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
  /*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
  /*
   *    sSS -> sIV      Server might not send FIN in this state.
 + *    sS2 -> sIV
   *    sSR -> sFW      Close started.
   *    sES -> sFW
   *    sFW -> sLA      FIN seen in both directions.
   *    sTW -> sTW
   *    sCL -> sCL
   */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 -/*ack*/          { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV },
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
 +/*ack*/          { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIG },
  /*
   *    sSS -> sIG      Might be a half-open connection.
 + *    sS2 -> sIG
   *    sSR -> sSR      Might answer late resent SYN.
   *    sES -> sES      :-)
   *    sFW -> sCW      Normal close request answered by ACK.
   *    sTW -> sTW      Retransmitted last ACK.
   *    sCL -> sCL
   */
 -/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI   */
 -/*rst*/    { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
 +/*         sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2   */
 +/*rst*/    { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL },
  /*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
        }
  };
@@@ -306,13 -296,13 +306,13 @@@ static int tcp_print_tuple(struct seq_f
  }
  
  /* Print out the private part of the conntrack. */
 -static int tcp_print_conntrack(struct seq_file *s, const struct nf_conn *ct)
 +static int tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
  {
        enum tcp_conntrack state;
  
 -      read_lock_bh(&tcp_lock);
 +      spin_lock_bh(&ct->lock);
        state = ct->proto.tcp.state;
 -      read_unlock_bh(&tcp_lock);
 +      spin_unlock_bh(&ct->lock);
  
        return seq_printf(s, "%s ", tcp_conntrack_names[state]);
  }
@@@ -531,14 -521,13 +531,14 @@@ static bool tcp_in_window(const struct 
                 receiver->td_end, receiver->td_maxend, receiver->td_maxwin,
                 receiver->td_scale);
  
 -      if (sender->td_end == 0) {
 +      if (sender->td_maxwin == 0) {
                /*
                 * Initialize sender data.
                 */
 -              if (tcph->syn && tcph->ack) {
 +              if (tcph->syn) {
                        /*
 -                       * Outgoing SYN-ACK in reply to a SYN.
 +                       * SYN-ACK in reply to a SYN
 +                       * or SYN from reply direction in simultaneous open.
                         */
                        sender->td_end =
                        sender->td_maxend = end;
                              && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE))
                                sender->td_scale =
                                receiver->td_scale = 0;
 +                      if (!tcph->ack)
 +                              /* Simultaneous open */
 +                              return true;
                } else {
                        /*
                         * We are in the middle of a connection,
                        sender->td_end = end;
                        sender->flags |= IP_CT_TCP_FLAG_DATA_UNACKNOWLEDGED;
                }
+               if (tcph->ack) {
+                       if (!(sender->flags & IP_CT_TCP_FLAG_MAXACK_SET)) {
+                               sender->td_maxack = ack;
+                               sender->flags |= IP_CT_TCP_FLAG_MAXACK_SET;
+                       } else if (after(ack, sender->td_maxack))
+                               sender->td_maxack = ack;
+               }
                /*
                 * Update receiver data.
                 */
@@@ -722,14 -716,14 +730,14 @@@ void nf_conntrack_tcp_update(const stru
  
        end = segment_seq_plus_len(ntohl(tcph->seq), skb->len, dataoff, tcph);
  
 -      write_lock_bh(&tcp_lock);
 +      spin_lock_bh(&ct->lock);
        /*
         * We have to worry for the ack in the reply packet only...
         */
        if (after(end, ct->proto.tcp.seen[dir].td_end))
                ct->proto.tcp.seen[dir].td_end = end;
        ct->proto.tcp.last_end = end;
 -      write_unlock_bh(&tcp_lock);
 +      spin_unlock_bh(&ct->lock);
        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,
@@@ -838,7 -832,7 +846,7 @@@ static int tcp_packet(struct nf_conn *c
        th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph);
        BUG_ON(th == NULL);
  
 -      write_lock_bh(&tcp_lock);
 +      spin_lock_bh(&ct->lock);
        old_state = ct->proto.tcp.state;
        dir = CTINFO2DIR(ctinfo);
        index = get_conntrack_index(th);
                        && ct->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);
 +                      spin_unlock_bh(&ct->lock);
  
                        /* Only repeat if we can actually remove the timer.
                         * Destruction may already be in progress in process
                         * that the client cannot but retransmit its SYN and
                         * thus initiate a clean new session.
                         */
 -                      write_unlock_bh(&tcp_lock);
 +                      spin_unlock_bh(&ct->lock);
                        if (LOG_INVALID(net, IPPROTO_TCP))
                                nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
                                          "nf_ct_tcp: killing out of sync session ");
                ct->proto.tcp.last_end =
                    segment_seq_plus_len(ntohl(th->seq), skb->len, dataoff, th);
  
 -              write_unlock_bh(&tcp_lock);
 +              spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_TCP))
                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
                                  "nf_ct_tcp: invalid packet ignored ");
                /* Invalid packet */
                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);
 +              spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_TCP))
                        nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
                                  "nf_ct_tcp: invalid state ");
                return -NF_ACCEPT;
        case TCP_CONNTRACK_CLOSE:
                if (index == TCP_RST_SET
+                   && (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET)
+                   && before(ntohl(th->seq), ct->proto.tcp.seen[!dir].td_maxack)) {
+                       /* Invalid RST  */
+                       write_unlock_bh(&tcp_lock);
+                       if (LOG_INVALID(net, IPPROTO_TCP))
+                               nf_log_packet(pf, 0, skb, NULL, NULL, NULL,
+                                         "nf_ct_tcp: invalid RST ");
+                       return -NF_ACCEPT;
+               }
+               if (index == TCP_RST_SET
                    && ((test_bit(IPS_SEEN_REPLY_BIT, &ct->status)
                         && ct->proto.tcp.last_index == TCP_SYN_SET)
                        || (!test_bit(IPS_ASSURED_BIT, &ct->status)
  
        if (!tcp_in_window(ct, &ct->proto.tcp, dir, index,
                           skb, dataoff, th, pf)) {
 -              write_unlock_bh(&tcp_lock);
 +              spin_unlock_bh(&ct->lock);
                return -NF_ACCEPT;
        }
       in_window:
                timeout = nf_ct_tcp_timeout_unacknowledged;
        else
                timeout = tcp_timeouts[new_state];
 -      write_unlock_bh(&tcp_lock);
 +      spin_unlock_bh(&ct->lock);
  
 -      nf_conntrack_event_cache(IPCT_PROTOINFO_VOLATILE, ct);
        if (new_state != old_state)
                nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
  
@@@ -1081,7 -1086,7 +1099,7 @@@ static bool tcp_new(struct nf_conn *ct
  
        ct->proto.tcp.seen[1].td_end = 0;
        ct->proto.tcp.seen[1].td_maxend = 0;
 -      ct->proto.tcp.seen[1].td_maxwin = 1;
 +      ct->proto.tcp.seen[1].td_maxwin = 0;
        ct->proto.tcp.seen[1].td_scale = 0;
  
        /* tcp_packet will set them */
  #include <linux/netfilter/nfnetlink_conntrack.h>
  
  static int tcp_to_nlattr(struct sk_buff *skb, struct nlattr *nla,
 -                       const struct nf_conn *ct)
 +                       struct nf_conn *ct)
  {
        struct nlattr *nest_parms;
        struct nf_ct_tcp_flags tmp = {};
  
 -      read_lock_bh(&tcp_lock);
 +      spin_lock_bh(&ct->lock);
        nest_parms = nla_nest_start(skb, CTA_PROTOINFO_TCP | NLA_F_NESTED);
        if (!nest_parms)
                goto nla_put_failure;
        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);
 +      spin_unlock_bh(&ct->lock);
  
        nla_nest_end(skb, nest_parms);
  
        return 0;
  
  nla_put_failure:
 -      read_unlock_bh(&tcp_lock);
 +      spin_unlock_bh(&ct->lock);
        return -1;
  }
  
@@@ -1166,7 -1171,7 +1184,7 @@@ static int nlattr_to_tcp(struct nlattr 
            nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]) >= TCP_CONNTRACK_MAX)
                return -EINVAL;
  
 -      write_lock_bh(&tcp_lock);
 +      spin_lock_bh(&ct->lock);
        if (tb[CTA_PROTOINFO_TCP_STATE])
                ct->proto.tcp.state = nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE]);
  
                ct->proto.tcp.seen[1].td_scale =
                        nla_get_u8(tb[CTA_PROTOINFO_TCP_WSCALE_REPLY]);
        }
 -      write_unlock_bh(&tcp_lock);
 +      spin_unlock_bh(&ct->lock);
  
        return 0;
  }
@@@ -1323,13 -1328,6 +1341,13 @@@ static struct ctl_table tcp_compat_sysc
                .proc_handler   = proc_dointvec_jiffies,
        },
        {
 +              .procname       = "ip_conntrack_tcp_timeout_syn_sent2",
 +              .data           = &tcp_timeouts[TCP_CONNTRACK_SYN_SENT2],
 +              .maxlen         = sizeof(unsigned int),
 +              .mode           = 0644,
 +              .proc_handler   = proc_dointvec_jiffies,
 +      },
 +      {
                .procname       = "ip_conntrack_tcp_timeout_syn_recv",
                .data           = &tcp_timeouts[TCP_CONNTRACK_SYN_RECV],
                .maxlen         = sizeof(unsigned int),