]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv4/tcp_input.c
tcp: Add tcp_validate_incoming & put duplicated code there
[linux-2.6-omap-h63xx.git] / net / ipv4 / tcp_input.c
index fac49a6e16115d6dde0f23bfbad490b029134dbd..e1b15d4e617162f3754e1aad1002eeff2057987e 100644 (file)
@@ -602,7 +602,7 @@ static u32 tcp_rto_min(struct sock *sk)
        u32 rto_min = TCP_RTO_MIN;
 
        if (dst && dst_metric_locked(dst, RTAX_RTO_MIN))
-               rto_min = dst_metric(dst, RTAX_RTO_MIN);
+               rto_min = dst_metric_rtt(dst, RTAX_RTO_MIN);
        return rto_min;
 }
 
@@ -729,6 +729,7 @@ void tcp_update_metrics(struct sock *sk)
        if (dst && (dst->flags & DST_HOST)) {
                const struct inet_connection_sock *icsk = inet_csk(sk);
                int m;
+               unsigned long rtt;
 
                if (icsk->icsk_backoff || !tp->srtt) {
                        /* This session failed to estimate rtt. Why?
@@ -740,7 +741,8 @@ void tcp_update_metrics(struct sock *sk)
                        return;
                }
 
-               m = dst_metric(dst, RTAX_RTT) - tp->srtt;
+               rtt = dst_metric_rtt(dst, RTAX_RTT);
+               m = rtt - tp->srtt;
 
                /* If newly calculated rtt larger than stored one,
                 * store new one. Otherwise, use EWMA. Remember,
@@ -748,12 +750,13 @@ void tcp_update_metrics(struct sock *sk)
                 */
                if (!(dst_metric_locked(dst, RTAX_RTT))) {
                        if (m <= 0)
-                               dst->metrics[RTAX_RTT - 1] = tp->srtt;
+                               set_dst_metric_rtt(dst, RTAX_RTT, tp->srtt);
                        else
-                               dst->metrics[RTAX_RTT - 1] -= (m >> 3);
+                               set_dst_metric_rtt(dst, RTAX_RTT, rtt - (m >> 3));
                }
 
                if (!(dst_metric_locked(dst, RTAX_RTTVAR))) {
+                       unsigned long var;
                        if (m < 0)
                                m = -m;
 
@@ -762,11 +765,13 @@ void tcp_update_metrics(struct sock *sk)
                        if (m < tp->mdev)
                                m = tp->mdev;
 
-                       if (m >= dst_metric(dst, RTAX_RTTVAR))
-                               dst->metrics[RTAX_RTTVAR - 1] = m;
+                       var = dst_metric_rtt(dst, RTAX_RTTVAR);
+                       if (m >= var)
+                               var = m;
                        else
-                               dst->metrics[RTAX_RTTVAR-1] -=
-                                       (dst_metric(dst, RTAX_RTTVAR) - m)>>2;
+                               var -= (var - m) >> 2;
+
+                       set_dst_metric_rtt(dst, RTAX_RTTVAR, var);
                }
 
                if (tp->snd_ssthresh >= 0xFFFF) {
@@ -897,7 +902,7 @@ static void tcp_init_metrics(struct sock *sk)
        if (dst_metric(dst, RTAX_RTT) == 0)
                goto reset;
 
-       if (!tp->srtt && dst_metric(dst, RTAX_RTT) < (TCP_TIMEOUT_INIT << 3))
+       if (!tp->srtt && dst_metric_rtt(dst, RTAX_RTT) < (TCP_TIMEOUT_INIT << 3))
                goto reset;
 
        /* Initial rtt is determined from SYN,SYN-ACK.
@@ -914,12 +919,12 @@ static void tcp_init_metrics(struct sock *sk)
         * to low value, and then abruptly stops to do it and starts to delay
         * ACKs, wait for troubles.
         */
-       if (dst_metric(dst, RTAX_RTT) > tp->srtt) {
-               tp->srtt = dst_metric(dst, RTAX_RTT);
+       if (dst_metric_rtt(dst, RTAX_RTT) > tp->srtt) {
+               tp->srtt = dst_metric_rtt(dst, RTAX_RTT);
                tp->rtt_seq = tp->snd_nxt;
        }
-       if (dst_metric(dst, RTAX_RTTVAR) > tp->mdev) {
-               tp->mdev = dst_metric(dst, RTAX_RTTVAR);
+       if (dst_metric_rtt(dst, RTAX_RTTVAR) > tp->mdev) {
+               tp->mdev = dst_metric_rtt(dst, RTAX_RTTVAR);
                tp->mdev_max = tp->rttvar = max(tp->mdev, tcp_rto_min(sk));
        }
        tcp_set_rto(sk);
@@ -1418,10 +1423,10 @@ tcp_sacktag_write_queue(struct sock *sk, struct sk_buff *ack_skb,
        unsigned char *ptr = (skb_transport_header(ack_skb) +
                              TCP_SKB_CB(ack_skb)->sacked);
        struct tcp_sack_block_wire *sp_wire = (struct tcp_sack_block_wire *)(ptr+2);
-       struct tcp_sack_block sp[4];
+       struct tcp_sack_block sp[TCP_NUM_SACKS];
        struct tcp_sack_block *cache;
        struct sk_buff *skb;
-       int num_sacks = (ptr[1] - TCPOLEN_SACK_BASE) >> 3;
+       int num_sacks = min(TCP_NUM_SACKS, (ptr[1] - TCPOLEN_SACK_BASE) >> 3);
        int used_sacks;
        int reord = tp->packets_out;
        int flag = 0;
@@ -1624,10 +1629,10 @@ advance_sp:
 out:
 
 #if FASTRETRANS_DEBUG > 0
-       BUG_TRAP((int)tp->sacked_out >= 0);
-       BUG_TRAP((int)tp->lost_out >= 0);
-       BUG_TRAP((int)tp->retrans_out >= 0);
-       BUG_TRAP((int)tcp_packets_in_flight(tp) >= 0);
+       WARN_ON((int)tp->sacked_out < 0);
+       WARN_ON((int)tp->lost_out < 0);
+       WARN_ON((int)tp->retrans_out < 0);
+       WARN_ON((int)tcp_packets_in_flight(tp) < 0);
 #endif
        return flag;
 }
@@ -2176,7 +2181,7 @@ static void tcp_mark_head_lost(struct sock *sk, int packets)
        int err;
        unsigned int mss;
 
-       BUG_TRAP(packets <= tp->packets_out);
+       WARN_ON(packets > tp->packets_out);
        if (tp->lost_skb_hint) {
                skb = tp->lost_skb_hint;
                cnt = tp->lost_cnt_hint;
@@ -2605,7 +2610,7 @@ static void tcp_fastretrans_alert(struct sock *sk, int pkts_acked, int flag)
        /* E. Check state exit conditions. State can be terminated
         *    when high_seq is ACKed. */
        if (icsk->icsk_ca_state == TCP_CA_Open) {
-               BUG_TRAP(tp->retrans_out == 0);
+               WARN_ON(tp->retrans_out != 0);
                tp->retrans_stamp = 0;
        } else if (!before(tp->snd_una, tp->high_seq)) {
                switch (icsk->icsk_ca_state) {
@@ -2967,9 +2972,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets)
        }
 
 #if FASTRETRANS_DEBUG > 0
-       BUG_TRAP((int)tp->sacked_out >= 0);
-       BUG_TRAP((int)tp->lost_out >= 0);
-       BUG_TRAP((int)tp->retrans_out >= 0);
+       WARN_ON((int)tp->sacked_out < 0);
+       WARN_ON((int)tp->lost_out < 0);
+       WARN_ON((int)tp->retrans_out < 0);
        if (!tp->packets_out && tcp_is_sack(tp)) {
                icsk = inet_csk(sk);
                if (tp->lost_out) {
@@ -3287,6 +3292,7 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
         * log. Something worked...
         */
        sk->sk_err_soft = 0;
+       icsk->icsk_probes_out = 0;
        tp->rcv_tstamp = tcp_time_stamp;
        prior_packets = tp->packets_out;
        if (!prior_packets)
@@ -3319,8 +3325,6 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
        return 1;
 
 no_queue:
-       icsk->icsk_probes_out = 0;
-
        /* If this ack opens up a zero window, clear backoff.  It was
         * being used to time the probes, and is probably far higher than
         * it needs to be for normal retransmission.
@@ -3730,8 +3734,7 @@ static void tcp_dsack_set(struct sock *sk, u32 seq, u32 end_seq)
                tp->rx_opt.dsack = 1;
                tp->duplicate_sack[0].start_seq = seq;
                tp->duplicate_sack[0].end_seq = end_seq;
-               tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + 1,
-                                          4 - tp->rx_opt.tstamp_ok);
+               tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + 1;
        }
 }
 
@@ -3786,9 +3789,8 @@ static void tcp_sack_maybe_coalesce(struct tcp_sock *tp)
                         * Decrease num_sacks.
                         */
                        tp->rx_opt.num_sacks--;
-                       tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks +
-                                                  tp->rx_opt.dsack,
-                                                  4 - tp->rx_opt.tstamp_ok);
+                       tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks +
+                                              tp->rx_opt.dsack;
                        for (i = this_sack; i < tp->rx_opt.num_sacks; i++)
                                sp[i] = sp[i + 1];
                        continue;
@@ -3838,7 +3840,7 @@ static void tcp_sack_new_ofo_skb(struct sock *sk, u32 seq, u32 end_seq)
         *
         * If the sack array is full, forget about the last one.
         */
-       if (this_sack >= 4) {
+       if (this_sack >= TCP_NUM_SACKS) {
                this_sack--;
                tp->rx_opt.num_sacks--;
                sp--;
@@ -3851,8 +3853,7 @@ new_sack:
        sp->start_seq = seq;
        sp->end_seq = end_seq;
        tp->rx_opt.num_sacks++;
-       tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks + tp->rx_opt.dsack,
-                                  4 - tp->rx_opt.tstamp_ok);
+       tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks + tp->rx_opt.dsack;
 }
 
 /* RCV.NXT advances, some SACKs should be eaten. */
@@ -3876,7 +3877,7 @@ static void tcp_sack_remove(struct tcp_sock *tp)
                        int i;
 
                        /* RCV.NXT must cover all the block! */
-                       BUG_TRAP(!before(tp->rcv_nxt, sp->end_seq));
+                       WARN_ON(before(tp->rcv_nxt, sp->end_seq));
 
                        /* Zap this SACK, by moving forward any other SACKS. */
                        for (i=this_sack+1; i < num_sacks; i++)
@@ -3889,9 +3890,8 @@ static void tcp_sack_remove(struct tcp_sock *tp)
        }
        if (num_sacks != tp->rx_opt.num_sacks) {
                tp->rx_opt.num_sacks = num_sacks;
-               tp->rx_opt.eff_sacks = min(tp->rx_opt.num_sacks +
-                                          tp->rx_opt.dsack,
-                                          4 - tp->rx_opt.tstamp_ok);
+               tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks +
+                                      tp->rx_opt.dsack;
        }
 }
 
@@ -3970,8 +3970,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
 
        if (tp->rx_opt.dsack) {
                tp->rx_opt.dsack = 0;
-               tp->rx_opt.eff_sacks = min_t(unsigned int, tp->rx_opt.num_sacks,
-                                            4 - tp->rx_opt.tstamp_ok);
+               tp->rx_opt.eff_sacks = tp->rx_opt.num_sacks;
        }
 
        /*  Queue data for delivery to the user.
@@ -4692,6 +4691,67 @@ out:
 }
 #endif /* CONFIG_NET_DMA */
 
+/* Does PAWS and seqno based validation of an incoming segment, flags will
+ * play significant role here.
+ */
+static int tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
+                             struct tcphdr *th, int syn_inerr)
+{
+       struct tcp_sock *tp = tcp_sk(sk);
+
+       /* RFC1323: H1. Apply PAWS check first. */
+       if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
+           tcp_paws_discard(sk, skb)) {
+               if (!th->rst) {
+                       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
+                       tcp_send_dupack(sk, skb);
+                       goto discard;
+               }
+               /* Reset is accepted even if it did not pass PAWS. */
+       }
+
+       /* Step 1: check sequence number */
+       if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
+               /* RFC793, page 37: "In all states except SYN-SENT, all reset
+                * (RST) segments are validated by checking their SEQ-fields."
+                * And page 69: "If an incoming segment is not acceptable,
+                * an acknowledgment should be sent in reply (unless the RST
+                * bit is set, if so drop the segment and return)".
+                */
+               if (!th->rst)
+                       tcp_send_dupack(sk, skb);
+               goto discard;
+       }
+
+       /* Step 2: check RST bit */
+       if (th->rst) {
+               tcp_reset(sk);
+               goto discard;
+       }
+
+       /* ts_recent update must be made after we are sure that the packet
+        * is in window.
+        */
+       tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
+
+       /* step 3: check security and precedence [ignored] */
+
+       /* step 4: Check for a SYN in window. */
+       if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
+               if (syn_inerr)
+                       TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
+               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONSYN);
+               tcp_reset(sk);
+               return -1;
+       }
+
+       return 1;
+
+discard:
+       __kfree_skb(skb);
+       return 0;
+}
+
 /*
  *     TCP receive function for the ESTABLISHED state.
  *
@@ -4719,6 +4779,7 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
                        struct tcphdr *th, unsigned len)
 {
        struct tcp_sock *tp = tcp_sk(sk);
+       int res;
 
        /*
         *      Header prediction.
@@ -4899,52 +4960,13 @@ slow_path:
        if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
                goto csum_error;
 
-       /*
-        * RFC1323: H1. Apply PAWS check first.
-        */
-       if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
-           tcp_paws_discard(sk, skb)) {
-               if (!th->rst) {
-                       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
-                       tcp_send_dupack(sk, skb);
-                       goto discard;
-               }
-               /* Resets are accepted even if PAWS failed.
-
-                  ts_recent update must be made after we are sure
-                  that the packet is in window.
-                */
-       }
-
        /*
         *      Standard slow path.
         */
 
-       if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
-               /* RFC793, page 37: "In all states except SYN-SENT, all reset
-                * (RST) segments are validated by checking their SEQ-fields."
-                * And page 69: "If an incoming segment is not acceptable,
-                * an acknowledgment should be sent in reply (unless the RST bit
-                * is set, if so drop the segment and return)".
-                */
-               if (!th->rst)
-                       tcp_send_dupack(sk, skb);
-               goto discard;
-       }
-
-       if (th->rst) {
-               tcp_reset(sk);
-               goto discard;
-       }
-
-       tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
-
-       if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
-               TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);
-               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONSYN);
-               tcp_reset(sk);
-               return 1;
-       }
+       res = tcp_validate_incoming(sk, skb, th, 1);
+       if (res <= 0)
+               return -res;
 
 step5:
        if (th->ack)
@@ -5226,6 +5248,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
        struct tcp_sock *tp = tcp_sk(sk);
        struct inet_connection_sock *icsk = inet_csk(sk);
        int queued = 0;
+       int res;
 
        tp->rx_opt.saw_tstamp = 0;
 
@@ -5278,42 +5301,9 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
                return 0;
        }
 
-       if (tcp_fast_parse_options(skb, th, tp) && tp->rx_opt.saw_tstamp &&
-           tcp_paws_discard(sk, skb)) {
-               if (!th->rst) {
-                       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSESTABREJECTED);
-                       tcp_send_dupack(sk, skb);
-                       goto discard;
-               }
-               /* Reset is accepted even if it did not pass PAWS. */
-       }
-
-       /* step 1: check sequence number */
-       if (!tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq)) {
-               if (!th->rst)
-                       tcp_send_dupack(sk, skb);
-               goto discard;
-       }
-
-       /* step 2: check RST bit */
-       if (th->rst) {
-               tcp_reset(sk);
-               goto discard;
-       }
-
-       tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
-
-       /* step 3: check security and precedence [ignored] */
-
-       /*      step 4:
-        *
-        *      Check for a SYN in window.
-        */
-       if (th->syn && !before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
-               NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPABORTONSYN);
-               tcp_reset(sk);
-               return 1;
-       }
+       res = tcp_validate_incoming(sk, skb, th, 0);
+       if (res <= 0)
+               return -res;
 
        /* step 5: check the ACK field */
        if (th->ack) {