netdev_dbg(dev, "no tunnel metadata\n");
                        goto tx_error;
                }
+               if (info && ip_tunnel_info_af(info) != AF_INET)
+                       goto tx_error;
        }
 
        rt = geneve_get_rt(skb, dev, &fl4, info);
 
                                  dev->name);
                        goto drop;
                }
+               if (family != ip_tunnel_info_af(info))
+                       goto drop;
 
                dst_port = info->key.tp_dst ? : vxlan->cfg.dst_port;
                vni = be64_to_cpu(info->key.tun_id);
 
        info->key.u.ipv6.dst = ip6h->daddr;
        info->key.tos = ipv6_get_dsfield(ip6h);
        info->key.ttl = ip6h->hop_limit;
+       info->mode = IP_TUNNEL_INFO_IPV6;
        return tun_dst;
 }
 
 
 #include <linux/if_tunnel.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/socket.h>
 #include <linux/types.h>
 #include <linux/u64_stats_sync.h>
 #include <net/dsfield.h>
 
 /* Flags for ip_tunnel_info mode. */
 #define IP_TUNNEL_INFO_TX      0x01    /* represents tx tunnel parameters */
+#define IP_TUNNEL_INFO_IPV6    0x02    /* key contains IPv6 addresses */
 
 struct ip_tunnel_info {
        struct ip_tunnel_key    key;
 
        tun_info->options = opts;
        tun_info->options_len = opts_len;
+
+       tun_info->mode = 0;
 }
 
 static inline void ip_tunnel_info_init(struct ip_tunnel_info *tun_info,
                              tun_id, tun_flags, opts, opts_len);
 }
 
+static inline unsigned short ip_tunnel_info_af(const struct ip_tunnel_info
+                                              *tun_info)
+{
+       return tun_info->mode & IP_TUNNEL_INFO_IPV6 ? AF_INET6 : AF_INET;
+}
+
 #ifdef CONFIG_INET
 
 int ip_tunnel_init(struct net_device *dev);
 
 
        if (unlikely(size != sizeof(struct bpf_tunnel_key) || flags || !info))
                return -EINVAL;
+       if (ip_tunnel_info_af(info) != AF_INET)
+               return -EINVAL;
 
        to->tunnel_id = be64_to_cpu(info->key.tun_id);
        to->remote_ipv4 = be32_to_cpu(info->key.u.ipv4.src);
 
        int err;
 
        tun_info = skb_tunnel_info(skb);
-       if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX)))
+       if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) ||
+                    ip_tunnel_info_af(tun_info) != AF_INET))
                goto err_free_skb;
 
        key = &tun_info->key;
 
        if (tb[LWTUNNEL_IP6_FLAGS])
                tun_info->key.tun_flags = nla_get_u16(tb[LWTUNNEL_IP6_FLAGS]);
 
-       tun_info->mode = IP_TUNNEL_INFO_TX;
+       tun_info->mode = IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_IPV6;
        tun_info->options = NULL;
        tun_info->options_len = 0;
 
 
 {
        /* Extract metadata from packet. */
        if (tun_info) {
+               if (ip_tunnel_info_af(tun_info) != AF_INET)
+                       return -EINVAL;
                memcpy(&key->tun_key, &tun_info->key, sizeof(key->tun_key));
 
                if (tun_info->options) {
 
 
        if (unlikely(!tun_info))
                return -EINVAL;
+       if (ip_tunnel_info_af(tun_info) != AF_INET)
+               return -EINVAL;
 
        tun_key = &tun_info->key;