LVS
lvs-devel
Google
 
Web LinuxVirtualServer.org

[PATCH ipvs 1/2] ipvs: Do tcp/udp checksumming prior to tunnel xmit

To: <horms@xxxxxxxxxxxx>
Subject: [PATCH ipvs 1/2] ipvs: Do tcp/udp checksumming prior to tunnel xmit
Cc: <ja@xxxxxx>, <lvs-devel@xxxxxxxxxxxxxxx>, <agartrell@xxxxxx>, <kernel-team@xxxxxx>
From: Alex Gartrell <agartrell@xxxxxx>
Date: Tue, 22 Jul 2014 23:37:44 -0700
The combination of NF_INET_LOCAL_OUT and hardware checksum offload results
in picking the packet out of the send path before it gets proper checksums.
Our NICs (most NICs?) cannot do the checksumming in ipip packets, so these
faulty tcp/udp headers arrive at their destination and are discarded.

Instead, checksum the tcp/udp packets in the tunnel transmit path.  Right
now we do this for all packets, but a subsequent patch will limit this to
the NF_INET_LOCAL_OUT hook.

Signed-off-by: Alex Gartrell <agartrell@xxxxxx>
---
 net/netfilter/ipvs/ip_vs_xmit.c | 49 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 6f70bdd..e9b5e6e 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -37,6 +37,7 @@
 #include <net/icmp.h>                   /* for icmp_send */
 #include <net/route.h>                  /* for ip_route_output */
 #include <net/ipv6.h>
+#include <net/ip6_checksum.h>
 #include <net/ip6_route.h>
 #include <net/addrconf.h>
 #include <linux/icmpv6.h>
@@ -824,6 +825,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn 
*cp,
        struct iphdr  *iph;                     /* Our new IP header */
        unsigned int max_headroom;              /* The extra header space 
needed */
        int ret, local;
+       unsigned int l4_len = 0;
 
        EnterFunction(10);
 
@@ -862,6 +864,29 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn 
*cp,
                old_iph = ip_hdr(skb);
        }
 
+       {
+               /* ipip breaks layer 4 checksumming on many (all?) NICs, so
+                * we must do it ourselves instead of relying upon checksum
+                * offload */
+               l4_len = ntohs(old_iph->tot_len) - (old_iph->ihl * 4);
+               switch (old_iph->protocol) {
+               case IPPROTO_TCP:
+                       tcp_hdr(skb)->check = 0;
+                       tcp_hdr(skb)->check = tcp_v4_check(
+                               l4_len, old_iph->saddr, old_iph->daddr,
+                               csum_partial(tcp_hdr(skb), l4_len, 0));
+                       break;
+               case IPPROTO_UDP:
+                       udp_hdr(skb)->check = 0;
+                       udp_hdr(skb)->check = udp_v4_check(
+                               l4_len, old_iph->saddr, old_iph->daddr,
+                               csum_partial(udp_hdr(skb), l4_len, 0));
+                       break;
+               default:
+                       break;
+               }
+       }
+
        skb->transport_header = skb->network_header;
 
        /* fix old IP header checksum */
@@ -918,6 +943,7 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn 
*cp,
        struct ipv6hdr  *iph;           /* Our new IP header */
        unsigned int max_headroom;      /* The extra header space needed */
        int ret, local;
+       unsigned int l4_len = 0;
 
        EnterFunction(10);
 
@@ -953,6 +979,29 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct 
ip_vs_conn *cp,
                old_iph = ipv6_hdr(skb);
        }
 
+       {
+               /* ipip breaks layer 4 checksumming on many (all?) NICs, so
+                * we must do it ourselves instead of relying upon checksum
+                * offload */
+               l4_len = ntohs(old_iph->payload_len);
+               switch (old_iph->nexthdr) {
+               case IPPROTO_TCP:
+                       tcp_hdr(skb)->check = 0;
+                       tcp_hdr(skb)->check = tcp_v6_check(
+                               l4_len, &old_iph->saddr, &old_iph->daddr,
+                               csum_partial(tcp_hdr(skb), l4_len, 0));
+                       break;
+               case IPPROTO_UDP:
+                       udp_hdr(skb)->check = 0;
+                       udp_hdr(skb)->check = udp_v6_check(
+                               l4_len, &old_iph->saddr, &old_iph->daddr,
+                               csum_partial(udp_hdr(skb), l4_len, 0));
+                       break;
+               default:
+                       break;
+               }
+       }
+
        skb->transport_header = skb->network_header;
 
        skb_push(skb, sizeof(struct ipv6hdr));
-- 
1.8.1

--
To unsubscribe from this list: send the line "unsubscribe lvs-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

<Prev in Thread] Current Thread [Next in Thread>