rendered paste bodydiff --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
*/