#include <linux/ipv6.h>
 #include <linux/if_vlan.h>
 #include <net/dsa.h>
+#include <net/dst_metadata.h>
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <net/gre.h>
 }
 EXPORT_SYMBOL(__skb_flow_get_ports);
 
+static void
+skb_flow_dissect_set_enc_addr_type(enum flow_dissector_key_id type,
+                                  struct flow_dissector *flow_dissector,
+                                  void *target_container)
+{
+       struct flow_dissector_key_control *ctrl;
+
+       if (!dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_ENC_CONTROL))
+               return;
+
+       ctrl = skb_flow_dissector_target(flow_dissector,
+                                        FLOW_DISSECTOR_KEY_ENC_CONTROL,
+                                        target_container);
+       ctrl->addr_type = type;
+}
+
+static void
+__skb_flow_dissect_tunnel_info(const struct sk_buff *skb,
+                              struct flow_dissector *flow_dissector,
+                              void *target_container)
+{
+       struct ip_tunnel_info *info;
+       struct ip_tunnel_key *key;
+
+       /* A quick check to see if there might be something to do. */
+       if (!dissector_uses_key(flow_dissector,
+                               FLOW_DISSECTOR_KEY_ENC_KEYID) &&
+           !dissector_uses_key(flow_dissector,
+                               FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS) &&
+           !dissector_uses_key(flow_dissector,
+                               FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS) &&
+           !dissector_uses_key(flow_dissector,
+                               FLOW_DISSECTOR_KEY_ENC_CONTROL) &&
+           !dissector_uses_key(flow_dissector,
+                               FLOW_DISSECTOR_KEY_ENC_PORTS))
+               return;
+
+       info = skb_tunnel_info(skb);
+       if (!info)
+               return;
+
+       key = &info->key;
+
+       switch (ip_tunnel_info_af(info)) {
+       case AF_INET:
+               skb_flow_dissect_set_enc_addr_type(FLOW_DISSECTOR_KEY_IPV4_ADDRS,
+                                                  flow_dissector,
+                                                  target_container);
+               if (dissector_uses_key(flow_dissector,
+                                      FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) {
+                       struct flow_dissector_key_ipv4_addrs *ipv4;
+
+                       ipv4 = skb_flow_dissector_target(flow_dissector,
+                                                        FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS,
+                                                        target_container);
+                       ipv4->src = key->u.ipv4.src;
+                       ipv4->dst = key->u.ipv4.dst;
+               }
+               break;
+       case AF_INET6:
+               skb_flow_dissect_set_enc_addr_type(FLOW_DISSECTOR_KEY_IPV6_ADDRS,
+                                                  flow_dissector,
+                                                  target_container);
+               if (dissector_uses_key(flow_dissector,
+                                      FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) {
+                       struct flow_dissector_key_ipv6_addrs *ipv6;
+
+                       ipv6 = skb_flow_dissector_target(flow_dissector,
+                                                        FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS,
+                                                        target_container);
+                       ipv6->src = key->u.ipv6.src;
+                       ipv6->dst = key->u.ipv6.dst;
+               }
+               break;
+       }
+
+       if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) {
+               struct flow_dissector_key_keyid *keyid;
+
+               keyid = skb_flow_dissector_target(flow_dissector,
+                                                 FLOW_DISSECTOR_KEY_ENC_KEYID,
+                                                 target_container);
+               keyid->keyid = tunnel_id_to_key32(key->tun_id);
+       }
+
+       if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) {
+               struct flow_dissector_key_ports *tp;
+
+               tp = skb_flow_dissector_target(flow_dissector,
+                                              FLOW_DISSECTOR_KEY_ENC_PORTS,
+                                              target_container);
+               tp->src = key->tp_src;
+               tp->dst = key->tp_dst;
+       }
+}
+
 static enum flow_dissect_ret
 __skb_flow_dissect_mpls(const struct sk_buff *skb,
                        struct flow_dissector *flow_dissector,
                                              FLOW_DISSECTOR_KEY_BASIC,
                                              target_container);
 
+       __skb_flow_dissect_tunnel_info(skb, flow_dissector,
+                                      target_container);
+
        if (dissector_uses_key(flow_dissector,
                               FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
                struct ethhdr *eth = eth_hdr(skb);
 
        struct cls_fl_filter *f;
        struct fl_flow_key skb_key;
        struct fl_flow_key skb_mkey;
-       struct ip_tunnel_info *info;
 
        if (!atomic_read(&head->ht.nelems))
                return -1;
 
        fl_clear_masked_range(&skb_key, &head->mask);
 
-       info = skb_tunnel_info(skb);
-       if (info) {
-               struct ip_tunnel_key *key = &info->key;
-
-               switch (ip_tunnel_info_af(info)) {
-               case AF_INET:
-                       skb_key.enc_control.addr_type =
-                               FLOW_DISSECTOR_KEY_IPV4_ADDRS;
-                       skb_key.enc_ipv4.src = key->u.ipv4.src;
-                       skb_key.enc_ipv4.dst = key->u.ipv4.dst;
-                       break;
-               case AF_INET6:
-                       skb_key.enc_control.addr_type =
-                               FLOW_DISSECTOR_KEY_IPV6_ADDRS;
-                       skb_key.enc_ipv6.src = key->u.ipv6.src;
-                       skb_key.enc_ipv6.dst = key->u.ipv6.dst;
-                       break;
-               }
-
-               skb_key.enc_key_id.keyid = tunnel_id_to_key32(key->tun_id);
-               skb_key.enc_tp.src = key->tp_src;
-               skb_key.enc_tp.dst = key->tp_dst;
-       }
-
        skb_key.indev_ifindex = skb->skb_iif;
        /* skb_flow_dissect() does not set n_proto in case an unknown protocol,
         * so do it rather here.