Merge remote-tracking branches 'asoc/fix/wm8737', 'asoc/fix/wm8903', 'asoc/fix/wm8955...
[deliverable/linux.git] / net / netfilter / nf_conntrack_proto_tcp.c
index 5caa0c41bf26c3e6a2542f0dd50ac6f029ed8a84..70383de7205460a8ebdadd7fd1ba615fc7681296 100644 (file)
@@ -202,7 +202,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
  *     sES -> sES      :-)
  *     sFW -> sCW      Normal close request answered by ACK.
  *     sCW -> sCW
- *     sLA -> sTW      Last ACK detected.
+ *     sLA -> sTW      Last ACK detected (RFC5961 challenged)
  *     sTW -> sTW      Retransmitted last ACK. Remain in the same state.
  *     sCL -> sCL
  */
@@ -261,7 +261,7 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
  *     sES -> sES      :-)
  *     sFW -> sCW      Normal close request answered by ACK.
  *     sCW -> sCW
- *     sLA -> sTW      Last ACK detected.
+ *     sLA -> sTW      Last ACK detected (RFC5961 challenged)
  *     sTW -> sTW      Retransmitted last ACK.
  *     sCL -> sCL
  */
@@ -906,6 +906,7 @@ static int tcp_packet(struct nf_conn *ct,
                                        1 : ct->proto.tcp.last_win;
                        ct->proto.tcp.seen[ct->proto.tcp.last_dir].td_scale =
                                ct->proto.tcp.last_wscale;
+                       ct->proto.tcp.last_flags &= ~IP_CT_EXP_CHALLENGE_ACK;
                        ct->proto.tcp.seen[ct->proto.tcp.last_dir].flags =
                                ct->proto.tcp.last_flags;
                        memset(&ct->proto.tcp.seen[dir], 0,
@@ -923,7 +924,9 @@ static int tcp_packet(struct nf_conn *ct,
                 * may be in sync but we are not. In that case, we annotate
                 * the TCP options and let the packet go through. If it is a
                 * valid SYN packet, the server will reply with a SYN/ACK, and
-                * then we'll get in sync. Otherwise, the server ignores it. */
+                * then we'll get in sync. Otherwise, the server potentially
+                * responds with a challenge ACK if implementing RFC5961.
+                */
                if (index == TCP_SYN_SET && dir == IP_CT_DIR_ORIGINAL) {
                        struct ip_ct_tcp_state seen = {};
 
@@ -939,6 +942,13 @@ static int tcp_packet(struct nf_conn *ct,
                                ct->proto.tcp.last_flags |=
                                        IP_CT_TCP_FLAG_SACK_PERM;
                        }
+                       /* Mark the potential for RFC5961 challenge ACK,
+                        * this pose a special problem for LAST_ACK state
+                        * as ACK is intrepretated as ACKing last FIN.
+                        */
+                       if (old_state == TCP_CONNTRACK_LAST_ACK)
+                               ct->proto.tcp.last_flags |=
+                                       IP_CT_EXP_CHALLENGE_ACK;
                }
                spin_unlock_bh(&ct->lock);
                if (LOG_INVALID(net, IPPROTO_TCP))
@@ -970,6 +980,25 @@ static int tcp_packet(struct nf_conn *ct,
                        nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
                                  "nf_ct_tcp: invalid state ");
                return -NF_ACCEPT;
+       case TCP_CONNTRACK_TIME_WAIT:
+               /* RFC5961 compliance cause stack to send "challenge-ACK"
+                * e.g. in response to spurious SYNs.  Conntrack MUST
+                * not believe this ACK is acking last FIN.
+                */
+               if (old_state == TCP_CONNTRACK_LAST_ACK &&
+                   index == TCP_ACK_SET &&
+                   ct->proto.tcp.last_dir != dir &&
+                   ct->proto.tcp.last_index == TCP_SYN_SET &&
+                   (ct->proto.tcp.last_flags & IP_CT_EXP_CHALLENGE_ACK)) {
+                       /* Detected RFC5961 challenge ACK */
+                       ct->proto.tcp.last_flags &= ~IP_CT_EXP_CHALLENGE_ACK;
+                       spin_unlock_bh(&ct->lock);
+                       if (LOG_INVALID(net, IPPROTO_TCP))
+                               nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL,
+                                     "nf_ct_tcp: challenge-ACK ignored ");
+                       return NF_ACCEPT; /* Don't change state */
+               }
+               break;
        case TCP_CONNTRACK_CLOSE:
                if (index == TCP_RST_SET
                    && (ct->proto.tcp.seen[!dir].flags & IP_CT_TCP_FLAG_MAXACK_SET)
This page took 0.036589 seconds and 5 git commands to generate.