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

Reply via email to