Upstream commit: commit 264619055bd52bc2278af848472176642d759874 Author: Jarno Rajahalme <ja...@ovn.org> 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 <pshe...@nicira.com> Signed-off-by: Jarno Rajahalme <ja...@ovn.org> Signed-off-by: Pablo Neira Ayuso <pa...@netfilter.org> 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 <ja...@ovn.org> --- 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 dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev