[ovs-dev] [PATCH] Conntrack: Fix L4 Checksums in kernel <4.6 when using NAT and helpers
John Hurley
john.hurley at netronome.com
Wed Dec 28 21:37:30 UTC 2016
Fix for a bug when sending a NATed packet to helper function in kernels
<4.6.
Setting CHECKSUM_PARTIAL flag means packets could have L4 checksum
corrupted in
datapath.c/queue_userspace_packet().
Giving the packet an skb_dst allows the kernel to correct the checksum if
packet
mangling happens in Conntrack/NAT helpers.
Signed-off-by: John Hurley <john.hurley at netronome.com>
---
diff --git a/datapath/conntrack.c b/datapath/conntrack.c
index d942884..18db41b 100644
--- a/datapath/conntrack.c
+++ b/datapath/conntrack.c
@@ -314,6 +314,10 @@ static int ovs_ct_helper(struct sk_buff *skb, u16
proto)
u8 nexthdr;
int err;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
+ struct rtable *rt = NULL;
+#endif
+
ct = nf_ct_get(skb, &ctinfo);
if (!ct || ctinfo == IP_CT_RELATED_REPLY)
return NF_ACCEPT;
@@ -352,43 +356,29 @@ static int ovs_ct_helper(struct sk_buff *skb, u16
proto)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
/* Linux 4.5 and older depend on skb_dst being set when recalculating
* checksums after NAT helper has mangled TCP or UDP packet payload.
- * This dependency is avoided when skb is CHECKSUM_PARTIAL or when UDP
- * has no checksum.
*
- * The dependency is not triggered when the main NAT code updates
- * checksums after translating the IP header (address, port), so this
- * fix only needs to be executed on packets that are both being NATted
- * and that have a helper assigned.
+ * skb_dst is cast to a rtable struct and the flags examined.
+ * Forcing these flags to have RTCF_LOCAL set allows checksum
calculations
+ * to be carried out in the same way as kernel versions > 4.5
*/
if (ct->status & IPS_NAT_MASK && skb->ip_summed != CHECKSUM_PARTIAL) {
- u8 ipproto = (proto == NFPROTO_IPV4)
- ? ip_hdr(skb)->protocol : nexthdr;
- u16 offset = 0;
-
- switch (ipproto) {
- case IPPROTO_TCP:
- offset = offsetof(struct tcphdr, check);
- break;
- case IPPROTO_UDP:
- /* Skip if no csum. */
- if (udp_hdr(skb)->check)
- offset = offsetof(struct udphdr, check);
- break;
- }
- if (offset) {
- if (unlikely(!pskb_may_pull(skb, protoff + offset + 2)))
- return NF_DROP;
-
- skb->csum_start = skb_headroom(skb) + protoff;
- skb->csum_offset = offset;
- skb->ip_summed = CHECKSUM_PARTIAL;
- }
+ rt = kmalloc(sizeof(struct rtable), GFP_KERNEL);
+ rt->rt_flags = RTCF_LOCAL;
+ skb_dst_set(skb, (struct dst_entry *)rt);
}
#endif
+
err = helper->help(skb, protoff, ct, ctinfo);
if (err != NF_ACCEPT)
return err;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0)
+ if (rt) {
+ skb_dst_set(skb, NULL);
+ kfree(rt);
+ }
+#endif
+
/* Adjust seqs after helper. This is needed due to some helpers (e.g.,
* FTP with NAT) adusting the TCP payload size when mangling IP
* addresses and/or port numbers in the text-based control connection.
--
More information about the dev
mailing list