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 */