return tot;
 }
 
+/* Does key in tunnel parameters match packet */
+static bool ipgre_key_match(const struct ip_tunnel_parm *p,
+                           __u32 flags, __be32 key)
+{
+       if (p->i_flags & GRE_KEY) {
+               if (flags & GRE_KEY)
+                       return key == p->i_key;
+               else
+                       return false;   /* key expected, none present */
+       } else
+               return !(flags & GRE_KEY);
+}
+
 /* Given src, dst and key, find appropriate for input tunnel. */
 
 static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
                                             __be32 remote, __be32 local,
-                                            __be32 key, __be16 gre_proto)
+                                            __u32 flags, __be32 key,
+                                            __be16 gre_proto)
 {
        struct net *net = dev_net(dev);
        int link = dev->ifindex;
        for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) {
                if (local != t->parms.iph.saddr ||
                    remote != t->parms.iph.daddr ||
-                   key != t->parms.i_key ||
                    !(t->dev->flags & IFF_UP))
                        continue;
 
+               if (!ipgre_key_match(&t->parms, flags, key))
+                       continue;
+
                if (t->dev->type != ARPHRD_IPGRE &&
                    t->dev->type != dev_type)
                        continue;
 
        for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) {
                if (remote != t->parms.iph.daddr ||
-                   key != t->parms.i_key ||
                    !(t->dev->flags & IFF_UP))
                        continue;
 
+               if (!ipgre_key_match(&t->parms, flags, key))
+                       continue;
+
                if (t->dev->type != ARPHRD_IPGRE &&
                    t->dev->type != dev_type)
                        continue;
                if ((local != t->parms.iph.saddr &&
                     (local != t->parms.iph.daddr ||
                      !ipv4_is_multicast(local))) ||
-                   key != t->parms.i_key ||
                    !(t->dev->flags & IFF_UP))
                        continue;
 
+               if (!ipgre_key_match(&t->parms, flags, key))
+                       continue;
+
                if (t->dev->type != ARPHRD_IPGRE &&
                    t->dev->type != dev_type)
                        continue;
        const int code = icmp_hdr(skb)->code;
        struct ip_tunnel *t;
        __be16 flags;
+       __be32 key = 0;
 
        flags = p[0];
        if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
        if (skb_headlen(skb) < grehlen)
                return;
 
+       if (flags & GRE_KEY)
+               key = *(((__be32 *)p) + (grehlen / 4) - 1);
+
        switch (type) {
        default:
        case ICMP_PARAMETERPROB:
 
        rcu_read_lock();
        t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
-                               flags & GRE_KEY ?
-                               *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
-                               p[1]);
+                               flags, key, p[1]);
+
        if (t == NULL)
                goto out;
 
        gre_proto = *(__be16 *)(h + 2);
 
        rcu_read_lock();
-       if ((tunnel = ipgre_tunnel_lookup(skb->dev,
-                                         iph->saddr, iph->daddr, key,
-                                         gre_proto))) {
+       tunnel = ipgre_tunnel_lookup(skb->dev,
+                                    iph->saddr, iph->daddr, flags, key,
+                                    gre_proto);
+       if (tunnel) {
                struct pcpu_tstats *tstats;
 
                secpath_reset(skb);