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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Filesystem Devel]     [Linux NFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]     [X.Org]

  Powered by Linux