All pastes #2066431 Raw Edit

Unnamed

public text v1 · immutable
#2066431 ·published 2011-05-22 01:53 UTC
rendered paste body
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index e64f4c6..4191164 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -106,6 +106,7 @@ enum {
 #define TCP_THIN_LINEAR_TIMEOUTS 16      /* Use linear timeouts for thin streams*/
 #define TCP_THIN_DUPACK         17      /* Fast retrans. after 1 dupack */
 #define TCP_USER_TIMEOUT       18      /* How long for loss retry before timeout */
+#define TCP_CONNECT_TIMEOUT    19      /* How long for to wait for connect */
 
 /* for TCP_INFO socket option */
 #define TCPI_OPT_TIMESTAMPS    1
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index 6ac4e3b..e4d3f97 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -82,6 +82,8 @@ struct inet_connection_sock_af_ops {
  * @icsk_ext_hdr_len:     Network protocol overhead (IP/IPv6 options)
  * @icsk_ack:             Delayed ACK control data
  * @icsk_mtup;            MTU probing control data
+ * @icsk_connect_timeout   Maximum time we want to wait before giving up on connecting 
+ * @icsk_connect_start     When the connection was started
  */
 struct inet_connection_sock {
        /* inet_sock has to be the first member! */
@@ -126,6 +128,8 @@ struct inet_connection_sock {
        } icsk_mtup;
        u32                       icsk_ca_priv[16];
        u32                       icsk_user_timeout;
+       u32                       icsk_connect_timeout;
+       u32                       icsk_connect_start;
 #define ICSK_CA_PRIV_SIZE      (16 * sizeof(u32))
 };
 
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 6c11eec..8d87dd4 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2396,6 +2396,10 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
                 */
                icsk->icsk_user_timeout = msecs_to_jiffies(val);
                break;
+       case TCP_CONNECT_TIMEOUT:
+               /* Set the connect timeout on the socket */
+               icsk->icsk_connect_timeout = msecs_to_jiffies(val);
+               break;
        default:
                err = -ENOPROTOOPT;
                break;
@@ -2618,6 +2622,9 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
        case TCP_USER_TIMEOUT:
                val = jiffies_to_msecs(icsk->icsk_user_timeout);
                break;
+       case TCP_CONNECT_TIMEOUT:
+               val = jiffies_to_msecs(icsk->icsk_connect_timeout);
+               break;
        default:
                return -ENOPROTOOPT;
        }
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 65f6c04..d7a8e7b 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5437,6 +5437,7 @@ discard:
        return 0;
 }
 EXPORT_SYMBOL(tcp_rcv_established);
+extern void tcp_v6_update_label_rtt(struct sock *sk);
 
 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                                         struct tcphdr *th, unsigned len)
@@ -5502,7 +5503,9 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                 */
 
                TCP_ECN_rcv_synack(tp, th);
-
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                tcp_v6_update_label_rtt(sk);
+#endif
                tp->snd_wl1 = TCP_SKB_CB(skb)->seq;
                tcp_ack(sk, skb, FLAG_SLOWPATH);
 
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index dfa5beb..44fed04 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2609,6 +2609,7 @@ int tcp_connect(struct sock *sk)
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *buff;
        int err;
+       u32 retrans_timeout;
 
        tcp_connect_init(sk);
 
@@ -2642,9 +2643,19 @@ int tcp_connect(struct sock *sk)
        tp->pushed_seq = tp->write_seq;
        TCP_INC_STATS(sock_net(sk), TCP_MIB_ACTIVEOPENS);
 
-       /* Timer for repeating the SYN until an answer. */
+        if (likely(inet_csk(sk)->icsk_connect_timeout == 0)) {
+               /* Here we should populate it from the label's table ? */
+               inet_csk(sk)->icsk_connect_timeout = msecs_to_jiffies(1000*20);
+       }
+       retrans_timeout = inet_csk(sk)->icsk_connect_timeout;
+       if (retrans_timeout > inet_csk(sk)->icsk_rto) {
+               retrans_timeout = inet_csk(sk)->icsk_rto;
+       }
+       printk("Init retrans timeout: %d (rto: %d, connect_to: %d\n", retrans_timeout, inet_csk(sk)->icsk_rto, inet_csk(sk)->icsk_connect_timeout);
+       // inet_csk(sk)->icsk_rto = retrans_timeout;
        inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
-                                 inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
+                                 retrans_timeout, TCP_RTO_MAX);
+       inet_csk(sk)->icsk_connect_start = jiffies;
        return 0;
 }
 EXPORT_SYMBOL(tcp_connect);
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 74a6aa0..f883db4 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -166,6 +166,9 @@ static bool retransmits_timed_out(struct sock *sk,
        return (tcp_time_stamp - start_ts) >= timeout;
 }
 
+extern void tcp_v6_update_label_rtt(struct sock *sk);
+
+
 /* A write timeout has occurred. Process the after effects. */
 static int tcp_write_timeout(struct sock *sk)
 {
@@ -177,6 +180,17 @@ static int tcp_write_timeout(struct sock *sk)
                if (icsk->icsk_retransmits)
                        dst_negative_advice(sk);
                retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;
+               /* bail if we are beyond the connect timeout */
+               if(time_after(jiffies, icsk->icsk_connect_start + icsk->icsk_connect_timeout-1)) {
+                       printk("Timeout: %x > %x + %x\n", jiffies, icsk->icsk_connect_start, icsk->icsk_connect_timeout);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                       tcp_v6_update_label_rtt(sk);
+#endif
+                       tcp_write_err(sk);
+                       return 1;
+               } else {
+                       printk("Not Timeout: %x > %x + %x\n", jiffies, icsk->icsk_connect_start, icsk->icsk_connect_timeout);
+               }
                syn_set = 1;
        } else {
                if (retransmits_timed_out(sk, sysctl_tcp_retries1, 0, 0)) {
diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c
index c8993e5..bcf86c8 100644
--- a/net/ipv6/addrlabel.c
+++ b/net/ipv6/addrlabel.c
@@ -25,6 +25,9 @@
 #define ADDRLABEL(x...) do { ; } while(0)
 #endif
 
+#define LABEL_TIMEOUT_FACTOR   100
+#define LABEL_MAX_TIMEOUT      20000
+
 /*
  * Policy Table
  */
@@ -38,6 +41,9 @@ struct ip6addrlbl_entry
        int ifindex;
        int addrtype;
        u32 label;
+       u32 rtt_average;
+       u32 rtt_variance;
+       u32 when_added;
        struct hlist_node list;
        atomic_t refcnt;
        struct rcu_head rcu;
@@ -193,7 +199,7 @@ u32 ipv6_addr_label(struct net *net,
 static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net,
                                                 const struct in6_addr *prefix,
                                                 int prefixlen, int ifindex,
-                                                u32 label)
+                                                u32 label, u32 rtt)
 {
        struct ip6addrlbl_entry *newp;
        int addrtype;
@@ -229,6 +235,9 @@ static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net,
        newp->ifindex = ifindex;
        newp->addrtype = addrtype;
        newp->label = label;
+       newp->rtt_average = rtt;
+       newp->rtt_variance = rtt/2; /* SWAG */
+       newp->when_added = jiffies;
        INIT_HLIST_NODE(&newp->list);
 #ifdef CONFIG_NET_NS
        newp->lbl_net = hold_net(net);
@@ -281,7 +290,7 @@ out:
 /* add a label */
 static int ip6addrlbl_add(struct net *net,
                          const struct in6_addr *prefix, int prefixlen,
-                         int ifindex, u32 label, int replace)
+                         int ifindex, u32 label, int replace, u32 rtt)
 {
        struct ip6addrlbl_entry *newp;
        int ret = 0;
@@ -290,7 +299,7 @@ static int ip6addrlbl_add(struct net *net,
                  __func__, prefix, prefixlen, ifindex, (unsigned int)label,
                  replace);
 
-       newp = ip6addrlbl_alloc(net, prefix, prefixlen, ifindex, label);
+       newp = ip6addrlbl_alloc(net, prefix, prefixlen, ifindex, label, rtt);
        if (IS_ERR(newp))
                return PTR_ERR(newp);
        spin_lock(&ip6addrlbl_table.lock);
@@ -344,6 +353,102 @@ static int ip6addrlbl_del(struct net *net,
        return ret;
 }
 
+u32 ipv6_addr_label_get_timeout(struct net *net,
+                   const struct in6_addr *addr, int type, int ifindex)
+{
+       u32 timeout;
+        struct ip6addrlbl_entry *p;
+
+        type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK;
+
+        rcu_read_lock();
+        p = __ipv6_addr_label(net, addr, type, ifindex);
+        timeout = p ? p->rtt_average + 3*p->rtt_variance : LABEL_MAX_TIMEOUT;
+        rcu_read_unlock();
+       if(timeout > LABEL_MAX_TIMEOUT) {
+               timeout = LABEL_MAX_TIMEOUT;
+       }
+       printk("Got label timeout: %d\n", timeout);
+        return timeout;
+}
+
+#define MAX_DYNAMIC_LABELS     3
+#define DYNAMIC_LABEL_VAL      9999
+
+u32 ipv6_addr_label_update_timeout(struct net *net,
+                   const struct in6_addr *addr, int type, int ifindex, u32 rtt)
+{
+       static added_labels = 0;
+       u32 timeout = 0;
+        struct ip6addrlbl_entry *p;
+       int need_new = 0;
+       int ret;
+       u32 big_rtt_average = 0;
+       u32 big_rtt_variance = 0;
+
+        type &= IPV6_ADDR_MAPPED | IPV6_ADDR_COMPATv4 | IPV6_ADDR_LOOPBACK;
+       printk("Setting label timeout: %d\n", rtt);
+
+        rcu_read_lock();
+        p = __ipv6_addr_label(net, addr, type, ifindex);
+        if(p) {
+               /* Need somewhat sensible math here.
+                * The logic: 
+                 *    if the RTT is slightly different, update the average/deviation.
+                *    if the RTT is "outrageously long" - then create a new entry.
+                */
+               if( (type == 0) && (rtt > p->rtt_average + 2*p->rtt_variance) ) {
+                       need_new = 1;
+               } else {
+                       p->rtt_average = (49*p->rtt_average + rtt)/50;
+               }
+        }
+        rcu_read_unlock();
+       if(need_new) {
+               printk("Adding new label /64\n");
+               ret = ip6addrlbl_add(net, addr, 64, ifindex, DYNAMIC_LABEL_VAL, 0, rtt);
+               if(ret == 0) {
+                       added_labels++;
+                       printk("Total added labels: %d\n", added_labels);
+                       if (added_labels > MAX_DYNAMIC_LABELS) {
+                               struct hlist_node *pos, *n;
+                               struct ip6addrlbl_entry *p = NULL;
+                               u32 when_added = 0xffffffff;
+                               hlist_for_each_entry_safe(p, pos, n,
+                                          &ip6addrlbl_table.head, list) {
+                                       if((p->label == DYNAMIC_LABEL_VAL) && (p->when_added < when_added)) {
+                                               when_added = p->when_added;
+                                       }
+                               };
+                               p = NULL;
+                               hlist_for_each_entry_safe(p, pos, n,
+                                          &ip6addrlbl_table.head, list) {
+                                       if((p->label == DYNAMIC_LABEL_VAL) && (p->when_added == when_added)) {
+                                               big_rtt_average = p->rtt_average;
+                                               big_rtt_variance = p->rtt_variance;
+                                               hlist_del_rcu(&p->list);
+                                               added_labels--;
+                                               ip6addrlbl_put(p);
+                                               goto out;
+                                       }
+
+                               };
+                               out:
+                               printk("Updating the parent label with rtt: %d\n", big_rtt_average);
+                               rcu_read_lock();
+                               p = __ipv6_addr_label(net, addr, type, ifindex);
+                               if(p) {
+                                       p->rtt_average = (4*p->rtt_average + big_rtt_average)/5;
+                               }
+                               rcu_read_unlock();
+                       }
+               }
+       }
+        return timeout;
+}
+
+
+
 /* add default label */
 static int __net_init ip6addrlbl_net_init(struct net *net)
 {
@@ -357,7 +462,7 @@ static int __net_init ip6addrlbl_net_init(struct net *net)
                                         ip6addrlbl_init_table[i].prefix,
                                         ip6addrlbl_init_table[i].prefixlen,
                                         0,
-                                        ip6addrlbl_init_table[i].label, 0);
+                                        ip6addrlbl_init_table[i].label, 0, ip6addrlbl_init_table[i].label*LABEL_TIMEOUT_FACTOR);
                /* XXX: should we free all rules when we catch an error? */
                if (ret && (!err || err != -ENOMEM))
                        err = ret;
@@ -444,7 +549,7 @@ static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh,
 
                err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen,
                                     ifal->ifal_index, label,
-                                    nlh->nlmsg_flags & NLM_F_REPLACE);
+                                    nlh->nlmsg_flags & NLM_F_REPLACE, label*LABEL_TIMEOUT_FACTOR);
                break;
        case RTM_DELADDRLABEL:
                err = ip6addrlbl_del(net, pfx, ifal->ifal_prefixlen,
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 20aa95e..76811bc 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -121,6 +121,33 @@ static __u32 tcp_v6_init_sequence(struct sk_buff *skb)
                                            tcp_hdr(skb)->source);
 }
 
+extern u32 ipv6_addr_label_get_timeout(struct net *net,
+                    const struct in6_addr *addr, int type, int ifindex);
+
+extern u32 ipv6_addr_label_update_timeout(struct net *net,
+                    const struct in6_addr *addr, int type, int ifindex, u32 rtt);
+
+void tcp_v6_update_label_rtt(struct sock *sk)
+{
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+                struct inet_sock *inet = inet_sk(sk);
+               struct inet_connection_sock *icsk = inet_csk(sk);
+                u32 rtt = jiffies_to_msecs(jiffies - icsk->icsk_connect_start) ;
+                if (sk->sk_family == AF_INET) {
+                        struct in6_addr daddr;
+                        daddr.s6_addr32[0] = daddr.s6_addr32[1] = 0;
+                        daddr.s6_addr32[2] = 0xffff;
+                        daddr.s6_addr32[3] = inet->inet_daddr;
+                        ipv6_addr_label_update_timeout(sock_net(sk), &daddr, ipv6_addr_type(&daddr), 0, rtt);
+                }              
+                else if (sk->sk_family == AF_INET6) {
+                        struct ipv6_pinfo *np = inet6_sk(sk);
+                        ipv6_addr_label_update_timeout(sock_net(sk), &np->daddr, ipv6_addr_type(&np->daddr), 0, rtt);
+                }
+#endif
+}
+
+
 static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
                          int addr_len)
 {
@@ -197,6 +224,9 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        ipv6_addr_copy(&np->daddr, &usin->sin6_addr);
        np->flow_label = fl.fl6_flowlabel;
 
+       icsk->icsk_connect_timeout = msecs_to_jiffies(ipv6_addr_label_get_timeout(sock_net(sk), &np->daddr, addr_type, 0));
+       printk("Set timeout to: %d\n", icsk->icsk_connect_timeout);
+
        /*
         *      TCP over IPv4
         */