Commit d2083287 authored by stephen hemminger's avatar stephen hemminger Committed by David S. Miller

gre: fix handling of key 0

GRE driver incorrectly uses zero as a flag value. Zero is a perfectly
valid value for key, and the tunnel should match packets with no key only
with tunnels created without key, and vice versa.

This is a slightly visible  change since previously it might be possible to
construct a working tunnel that sent key 0 and received only because
of the key wildcard of zero.  I.e the sender sent key of zero, but tunnel
was defined without key.

Note: using gre key 0 requires iproute2 utilities v3.2 or later.
The original utility code was broken as well.
Signed-off-by: default avatarStephen Hemminger <shemminger@vyatta.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent aa99521e
...@@ -214,11 +214,25 @@ static struct rtnl_link_stats64 *ipgre_get_stats64(struct net_device *dev, ...@@ -214,11 +214,25 @@ static struct rtnl_link_stats64 *ipgre_get_stats64(struct net_device *dev,
return tot; 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. */ /* Given src, dst and key, find appropriate for input tunnel. */
static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev, static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
__be32 remote, __be32 local, __be32 remote, __be32 local,
__be32 key, __be16 gre_proto) __u32 flags, __be32 key,
__be16 gre_proto)
{ {
struct net *net = dev_net(dev); struct net *net = dev_net(dev);
int link = dev->ifindex; int link = dev->ifindex;
...@@ -233,10 +247,12 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev, ...@@ -233,10 +247,12 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) { for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) {
if (local != t->parms.iph.saddr || if (local != t->parms.iph.saddr ||
remote != t->parms.iph.daddr || remote != t->parms.iph.daddr ||
key != t->parms.i_key ||
!(t->dev->flags & IFF_UP)) !(t->dev->flags & IFF_UP))
continue; continue;
if (!ipgre_key_match(&t->parms, flags, key))
continue;
if (t->dev->type != ARPHRD_IPGRE && if (t->dev->type != ARPHRD_IPGRE &&
t->dev->type != dev_type) t->dev->type != dev_type)
continue; continue;
...@@ -257,10 +273,12 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev, ...@@ -257,10 +273,12 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) { for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) {
if (remote != t->parms.iph.daddr || if (remote != t->parms.iph.daddr ||
key != t->parms.i_key ||
!(t->dev->flags & IFF_UP)) !(t->dev->flags & IFF_UP))
continue; continue;
if (!ipgre_key_match(&t->parms, flags, key))
continue;
if (t->dev->type != ARPHRD_IPGRE && if (t->dev->type != ARPHRD_IPGRE &&
t->dev->type != dev_type) t->dev->type != dev_type)
continue; continue;
...@@ -283,10 +301,12 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev, ...@@ -283,10 +301,12 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
if ((local != t->parms.iph.saddr && if ((local != t->parms.iph.saddr &&
(local != t->parms.iph.daddr || (local != t->parms.iph.daddr ||
!ipv4_is_multicast(local))) || !ipv4_is_multicast(local))) ||
key != t->parms.i_key ||
!(t->dev->flags & IFF_UP)) !(t->dev->flags & IFF_UP))
continue; continue;
if (!ipgre_key_match(&t->parms, flags, key))
continue;
if (t->dev->type != ARPHRD_IPGRE && if (t->dev->type != ARPHRD_IPGRE &&
t->dev->type != dev_type) t->dev->type != dev_type)
continue; continue;
...@@ -489,6 +509,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info) ...@@ -489,6 +509,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
const int code = icmp_hdr(skb)->code; const int code = icmp_hdr(skb)->code;
struct ip_tunnel *t; struct ip_tunnel *t;
__be16 flags; __be16 flags;
__be32 key = 0;
flags = p[0]; flags = p[0];
if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) { if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
...@@ -505,6 +526,9 @@ static void ipgre_err(struct sk_buff *skb, u32 info) ...@@ -505,6 +526,9 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
if (skb_headlen(skb) < grehlen) if (skb_headlen(skb) < grehlen)
return; return;
if (flags & GRE_KEY)
key = *(((__be32 *)p) + (grehlen / 4) - 1);
switch (type) { switch (type) {
default: default:
case ICMP_PARAMETERPROB: case ICMP_PARAMETERPROB:
...@@ -535,9 +559,8 @@ static void ipgre_err(struct sk_buff *skb, u32 info) ...@@ -535,9 +559,8 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
rcu_read_lock(); rcu_read_lock();
t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr, t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
flags & GRE_KEY ? flags, key, p[1]);
*(((__be32 *)p) + (grehlen / 4) - 1) : 0,
p[1]);
if (t == NULL) if (t == NULL)
goto out; goto out;
...@@ -642,9 +665,10 @@ static int ipgre_rcv(struct sk_buff *skb) ...@@ -642,9 +665,10 @@ static int ipgre_rcv(struct sk_buff *skb)
gre_proto = *(__be16 *)(h + 2); gre_proto = *(__be16 *)(h + 2);
rcu_read_lock(); rcu_read_lock();
if ((tunnel = ipgre_tunnel_lookup(skb->dev, tunnel = ipgre_tunnel_lookup(skb->dev,
iph->saddr, iph->daddr, key, iph->saddr, iph->daddr, flags, key,
gre_proto))) { gre_proto);
if (tunnel) {
struct pcpu_tstats *tstats; struct pcpu_tstats *tstats;
secpath_reset(skb); secpath_reset(skb);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment