aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-05-16 03:36:33 -0400
committerDavid S. Miller <davem@davemloft.net>2010-05-16 03:36:33 -0400
commita465419b1febb603821f924805529cff89cafeed (patch)
tree5131fa2dbf624ebeb6cf61bf4dc1bc9464fe0bbd
parent3b098e2d7c693796cc4dffb07caa249fc0f70771 (diff)
net: Introduce sk_route_nocaps
TCP-MD5 sessions have intermittent failures, when route cache is invalidated. ip_queue_xmit() has to find a new route, calls sk_setup_caps(sk, &rt->u.dst), destroying the sk->sk_route_caps &= ~NETIF_F_GSO_MASK that MD5 desperately try to make all over its way (from tcp_transmit_skb() for example) So we send few bad packets, and everything is fine when tcp_transmit_skb() is called again for this socket. Since ip_queue_xmit() is at a lower level than TCP-MD5, I chose to use a socket field, sk_route_nocaps, containing bits to mask on sk_route_caps. Reported-by: Bhaskar Dutta <bhaskie@gmail.com> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/sock.h8
-rw-r--r--net/core/sock.c1
-rw-r--r--net/ipv4/tcp_ipv4.c6
-rw-r--r--net/ipv4/tcp_output.c2
-rw-r--r--net/ipv6/tcp_ipv6.c4
5 files changed, 15 insertions, 6 deletions
diff --git a/include/net/sock.h b/include/net/sock.h
index 328e03f47dd1..aed16eb9db4b 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -177,6 +177,7 @@ struct sock_common {
177 * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings 177 * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings
178 * @sk_no_check: %SO_NO_CHECK setting, wether or not checkup packets 178 * @sk_no_check: %SO_NO_CHECK setting, wether or not checkup packets
179 * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO) 179 * @sk_route_caps: route capabilities (e.g. %NETIF_F_TSO)
180 * @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK)
180 * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) 181 * @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
181 * @sk_gso_max_size: Maximum GSO segment size to build 182 * @sk_gso_max_size: Maximum GSO segment size to build
182 * @sk_lingertime: %SO_LINGER l_linger setting 183 * @sk_lingertime: %SO_LINGER l_linger setting
@@ -276,6 +277,7 @@ struct sock {
276 int sk_forward_alloc; 277 int sk_forward_alloc;
277 gfp_t sk_allocation; 278 gfp_t sk_allocation;
278 int sk_route_caps; 279 int sk_route_caps;
280 int sk_route_nocaps;
279 int sk_gso_type; 281 int sk_gso_type;
280 unsigned int sk_gso_max_size; 282 unsigned int sk_gso_max_size;
281 int sk_rcvlowat; 283 int sk_rcvlowat;
@@ -1335,6 +1337,12 @@ static inline int sk_can_gso(const struct sock *sk)
1335 1337
1336extern void sk_setup_caps(struct sock *sk, struct dst_entry *dst); 1338extern void sk_setup_caps(struct sock *sk, struct dst_entry *dst);
1337 1339
1340static inline void sk_nocaps_add(struct sock *sk, int flags)
1341{
1342 sk->sk_route_nocaps |= flags;
1343 sk->sk_route_caps &= ~flags;
1344}
1345
1338static inline int skb_copy_to_page(struct sock *sk, char __user *from, 1346static inline int skb_copy_to_page(struct sock *sk, char __user *from,
1339 struct sk_buff *skb, struct page *page, 1347 struct sk_buff *skb, struct page *page,
1340 int off, int copy) 1348 int off, int copy)
diff --git a/net/core/sock.c b/net/core/sock.c
index 94c4affdda9b..63530a03b8c2 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1231,6 +1231,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
1231 sk->sk_route_caps = dst->dev->features; 1231 sk->sk_route_caps = dst->dev->features;
1232 if (sk->sk_route_caps & NETIF_F_GSO) 1232 if (sk->sk_route_caps & NETIF_F_GSO)
1233 sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE; 1233 sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE;
1234 sk->sk_route_caps &= ~sk->sk_route_nocaps;
1234 if (sk_can_gso(sk)) { 1235 if (sk_can_gso(sk)) {
1235 if (dst->header_len) { 1236 if (dst->header_len) {
1236 sk->sk_route_caps &= ~NETIF_F_GSO_MASK; 1237 sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 771f8146a2e5..202cf09c4cd4 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -891,7 +891,7 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr,
891 kfree(newkey); 891 kfree(newkey);
892 return -ENOMEM; 892 return -ENOMEM;
893 } 893 }
894 sk->sk_route_caps &= ~NETIF_F_GSO_MASK; 894 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
895 } 895 }
896 if (tcp_alloc_md5sig_pool(sk) == NULL) { 896 if (tcp_alloc_md5sig_pool(sk) == NULL) {
897 kfree(newkey); 897 kfree(newkey);
@@ -1021,7 +1021,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
1021 return -EINVAL; 1021 return -EINVAL;
1022 1022
1023 tp->md5sig_info = p; 1023 tp->md5sig_info = p;
1024 sk->sk_route_caps &= ~NETIF_F_GSO_MASK; 1024 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
1025 } 1025 }
1026 1026
1027 newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation); 1027 newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation);
@@ -1462,7 +1462,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
1462 if (newkey != NULL) 1462 if (newkey != NULL)
1463 tcp_v4_md5_do_add(newsk, newinet->inet_daddr, 1463 tcp_v4_md5_do_add(newsk, newinet->inet_daddr,
1464 newkey, key->keylen); 1464 newkey, key->keylen);
1465 newsk->sk_route_caps &= ~NETIF_F_GSO_MASK; 1465 sk_nocaps_add(newsk, NETIF_F_GSO_MASK);
1466 } 1466 }
1467#endif 1467#endif
1468 1468
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 5db3a2c6cb33..18a3302480cb 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -873,7 +873,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
873#ifdef CONFIG_TCP_MD5SIG 873#ifdef CONFIG_TCP_MD5SIG
874 /* Calculate the MD5 hash, as we have all we need now */ 874 /* Calculate the MD5 hash, as we have all we need now */
875 if (md5) { 875 if (md5) {
876 sk->sk_route_caps &= ~NETIF_F_GSO_MASK; 876 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
877 tp->af_specific->calc_md5_hash(opts.hash_location, 877 tp->af_specific->calc_md5_hash(opts.hash_location,
878 md5, sk, NULL, skb); 878 md5, sk, NULL, skb);
879 } 879 }
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 6603511e3673..2b7c3a100e2c 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -604,7 +604,7 @@ static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer,
604 kfree(newkey); 604 kfree(newkey);
605 return -ENOMEM; 605 return -ENOMEM;
606 } 606 }
607 sk->sk_route_caps &= ~NETIF_F_GSO_MASK; 607 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
608 } 608 }
609 if (tcp_alloc_md5sig_pool(sk) == NULL) { 609 if (tcp_alloc_md5sig_pool(sk) == NULL) {
610 kfree(newkey); 610 kfree(newkey);
@@ -741,7 +741,7 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
741 return -ENOMEM; 741 return -ENOMEM;
742 742
743 tp->md5sig_info = p; 743 tp->md5sig_info = p;
744 sk->sk_route_caps &= ~NETIF_F_GSO_MASK; 744 sk_nocaps_add(sk, NETIF_F_GSO_MASK);
745 } 745 }
746 746
747 newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL); 747 newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);