|
@@ -332,12 +332,15 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
|
|
|
{
|
|
|
struct iphdr *iph = (struct iphdr *)icmp_skb->data;
|
|
|
struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2));
|
|
|
+ struct inet_connection_sock *icsk;
|
|
|
struct tcp_sock *tp;
|
|
|
struct inet_sock *inet;
|
|
|
const int type = icmp_hdr(icmp_skb)->type;
|
|
|
const int code = icmp_hdr(icmp_skb)->code;
|
|
|
struct sock *sk;
|
|
|
+ struct sk_buff *skb;
|
|
|
__u32 seq;
|
|
|
+ __u32 remaining;
|
|
|
int err;
|
|
|
struct net *net = dev_net(icmp_skb->dev);
|
|
|
|
|
@@ -367,6 +370,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
|
|
|
if (sk->sk_state == TCP_CLOSE)
|
|
|
goto out;
|
|
|
|
|
|
+ icsk = inet_csk(sk);
|
|
|
tp = tcp_sk(sk);
|
|
|
seq = ntohl(th->seq);
|
|
|
if (sk->sk_state != TCP_LISTEN &&
|
|
@@ -393,6 +397,39 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
|
|
|
}
|
|
|
|
|
|
err = icmp_err_convert[code].errno;
|
|
|
+ /* check if icmp_skb allows revert of backoff
|
|
|
+ * (see draft-zimmermann-tcp-lcd) */
|
|
|
+ if (code != ICMP_NET_UNREACH && code != ICMP_HOST_UNREACH)
|
|
|
+ break;
|
|
|
+ if (seq != tp->snd_una || !icsk->icsk_retransmits ||
|
|
|
+ !icsk->icsk_backoff)
|
|
|
+ break;
|
|
|
+
|
|
|
+ icsk->icsk_backoff--;
|
|
|
+ inet_csk(sk)->icsk_rto = __tcp_set_rto(tp) <<
|
|
|
+ icsk->icsk_backoff;
|
|
|
+ tcp_bound_rto(sk);
|
|
|
+
|
|
|
+ skb = tcp_write_queue_head(sk);
|
|
|
+ BUG_ON(!skb);
|
|
|
+
|
|
|
+ remaining = icsk->icsk_rto - min(icsk->icsk_rto,
|
|
|
+ tcp_time_stamp - TCP_SKB_CB(skb)->when);
|
|
|
+
|
|
|
+ if (remaining) {
|
|
|
+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
|
|
|
+ remaining, TCP_RTO_MAX);
|
|
|
+ } else if (sock_owned_by_user(sk)) {
|
|
|
+ /* RTO revert clocked out retransmission,
|
|
|
+ * but socket is locked. Will defer. */
|
|
|
+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
|
|
|
+ HZ/20, TCP_RTO_MAX);
|
|
|
+ } else {
|
|
|
+ /* RTO revert clocked out retransmission.
|
|
|
+ * Will retransmit now */
|
|
|
+ tcp_retransmit_timer(sk);
|
|
|
+ }
|
|
|
+
|
|
|
break;
|
|
|
case ICMP_TIME_EXCEEDED:
|
|
|
err = EHOSTUNREACH;
|