*/
 static struct nf_conn *
 ovs_ct_find_existing(struct net *net, const struct nf_conntrack_zone *zone,
-                    u8 l3num, struct sk_buff *skb)
+                    u8 l3num, struct sk_buff *skb, bool natted)
 {
        struct nf_conntrack_l3proto *l3proto;
        struct nf_conntrack_l4proto *l4proto;
                return NULL;
        }
 
+       /* Must invert the tuple if skb has been transformed by NAT. */
+       if (natted) {
+               struct nf_conntrack_tuple inverse;
+
+               if (!nf_ct_invert_tuple(&inverse, &tuple, l3proto, l4proto)) {
+                       pr_debug("ovs_ct_find_existing: Inversion failed!\n");
+                       return NULL;
+               }
+               tuple = inverse;
+       }
+
        /* look for tuple match */
        h = nf_conntrack_find_get(net, zone, &tuple);
        if (!h)
 
        ct = nf_ct_tuplehash_to_ctrack(h);
 
+       /* Inverted packet tuple matches the reverse direction conntrack tuple,
+        * select the other tuplehash to get the right 'ctinfo' bits for this
+        * packet.
+        */
+       if (natted)
+               h = &ct->tuplehash[!h->tuple.dst.dir];
+
        nf_ct_set(skb, ct, ovs_ct_get_info(h));
        return ct;
 }
        if (!ct && key->ct.state & OVS_CS_F_TRACKED &&
            !(key->ct.state & OVS_CS_F_INVALID) &&
            key->ct.zone == info->zone.id)
-               ct = ovs_ct_find_existing(net, &info->zone, info->family, skb);
+               ct = ovs_ct_find_existing(net, &info->zone, info->family, skb,
+                                         !!(key->ct.state
+                                            & OVS_CS_F_NAT_MASK));
        if (!ct)
                return false;
        if (!net_eq(net, read_pnet(&ct->ct_net)))