Upstream commit:
commit 264619055bd52bc2278af848472176642d759874
Author: Jarno Rajahalme <[email protected]>
Date: Thu Mar 10 10:54:17 2016 -0800
netfilter: Allow calling into nat helper without skb_dst.
NAT checksum recalculation code assumes existence of skb_dst, which
becomes a problem for a later patch in the series ("openvswitch:
Interface with NAT."). Simplify this by removing the check on
skb_dst, as the checksum will be dealt with later in the stack.
Suggested-by: Pravin Shelar <[email protected]>
Signed-off-by: Jarno Rajahalme <[email protected]>
Signed-off-by: Pablo Neira Ayuso <[email protected]>
This patch adds a corresponding backport for Linux 4.5 and older into
datapath/conntrack.c, changing a TCP or UDP packet to CHECKSUM_PARTIAL
to avoid triggering the skb_dst dependency that otherwise crashes the
kernel when checksums are recalculated after NAT helper has mangled
TCP or UDP packet contents.
Signed-off-by: Jarno Rajahalme <[email protected]>
---
datapath/conntrack.c | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/datapath/conntrack.c b/datapath/conntrack.c
index 4b3b78e..40e9843 100644
--- a/datapath/conntrack.c
+++ b/datapath/conntrack.c
@@ -300,6 +300,7 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
enum ip_conntrack_info ctinfo;
unsigned int protoff;
struct nf_conn *ct;
+ u8 nexthdr;
int err;
ct = nf_ct_get(skb, &ctinfo);
@@ -319,10 +320,10 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
protoff = ip_hdrlen(skb);
break;
case NFPROTO_IPV6: {
- u8 nexthdr = ipv6_hdr(skb)->nexthdr;
__be16 frag_off;
int ofs;
+ nexthdr = ipv6_hdr(skb)->nexthdr;
ofs = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr,
&frag_off);
if (ofs < 0 || (frag_off & htons(~0x7)) != 0) {
@@ -337,6 +338,38 @@ static int ovs_ct_helper(struct sk_buff *skb, u16 proto)
return NF_DROP;
}
+#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 contents.
+ * This dependency is avoided when skb is CHECKSUM_PARTIAL or when UDP
+ * has no checksum.
+ */
+ 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:
+ case IPPROTO_UDPLITE:
+ /* Skip if no csum. */
+ if (udp_hdr(skb)->check)
+ offset = offsetof(struct udphdr, check);
+ break;
+ }
+ if (offset) {
+ if (skb->len < protoff + offset + 2)
+ return NF_DROP;
+
+ skb->csum_start = skb_headroom(skb) + protoff;
+ skb->csum_offset = offset;
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ }
+ }
+#endif
err = helper->help(skb, protoff, ct, ctinfo);
if (err != NF_ACCEPT)
return err;
--
2.1.4
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev