gre: strict physical device binding
[safe/jmp/linux-2.6] / net / ipv4 / ip_gre.c
index 191ef75..4a43739 100644 (file)
@@ -126,8 +126,6 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev);
 
 /* Fallback tunnel: no source, no destination, no key, no options */
 
-static int ipgre_fb_tunnel_init(struct net_device *dev);
-
 #define HASH_SIZE  16
 
 static int ipgre_net_id;
@@ -166,67 +164,113 @@ static DEFINE_RWLOCK(ipgre_lock);
 
 /* Given src, dst and key, find appropriate for input tunnel. */
 
-static struct ip_tunnel * ipgre_tunnel_lookup(struct net *net,
+static struct ip_tunnel * ipgre_tunnel_lookup(struct net_device *dev,
                                              __be32 remote, __be32 local,
                                              __be32 key, __be16 gre_proto)
 {
+       struct net *net = dev_net(dev);
+       int link = dev->ifindex;
        unsigned h0 = HASH(remote);
        unsigned h1 = HASH(key);
-       struct ip_tunnel *t;
-       struct ip_tunnel *t2 = NULL;
+       struct ip_tunnel *t, *sel[4] = { NULL, NULL, NULL, NULL };
        struct ipgre_net *ign = net_generic(net, ipgre_net_id);
        int dev_type = (gre_proto == htons(ETH_P_TEB)) ?
                       ARPHRD_ETHER : ARPHRD_IPGRE;
+       int idx;
 
        for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) {
-               if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) {
-                       if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
-                               if (t->dev->type == dev_type)
-                                       return t;
-                               if (t->dev->type == ARPHRD_IPGRE && !t2)
-                                       t2 = t;
-                       }
-               }
+               if (local != t->parms.iph.saddr ||
+                   remote != t->parms.iph.daddr ||
+                   key != t->parms.i_key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IPGRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               idx = 0;
+               if (t->parms.link != link)
+                       idx |= 1;
+               if (t->dev->type != dev_type)
+                       idx |= 2;
+               if (idx == 0)
+                       return t;
+               if (sel[idx] == NULL)
+                       sel[idx] = t;
        }
 
        for (t = ign->tunnels_r[h0^h1]; t; t = t->next) {
-               if (remote == t->parms.iph.daddr) {
-                       if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
-                               if (t->dev->type == dev_type)
-                                       return t;
-                               if (t->dev->type == ARPHRD_IPGRE && !t2)
-                                       t2 = t;
-                       }
-               }
+               if (remote != t->parms.iph.daddr ||
+                   key != t->parms.i_key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IPGRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               idx = 0;
+               if (t->parms.link != link)
+                       idx |= 1;
+               if (t->dev->type != dev_type)
+                       idx |= 2;
+               if (idx == 0)
+                       return t;
+               if (sel[idx] == NULL)
+                       sel[idx] = t;
        }
 
        for (t = ign->tunnels_l[h1]; t; t = t->next) {
-               if (local == t->parms.iph.saddr ||
-                    (local == t->parms.iph.daddr &&
-                     ipv4_is_multicast(local))) {
-                       if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
-                               if (t->dev->type == dev_type)
-                                       return t;
-                               if (t->dev->type == ARPHRD_IPGRE && !t2)
-                                       t2 = t;
-                       }
-               }
+               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 (t->dev->type != ARPHRD_IPGRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               idx = 0;
+               if (t->parms.link != link)
+                       idx |= 1;
+               if (t->dev->type != dev_type)
+                       idx |= 2;
+               if (idx == 0)
+                       return t;
+               if (sel[idx] == NULL)
+                       sel[idx] = t;
        }
 
        for (t = ign->tunnels_wc[h1]; t; t = t->next) {
-               if (t->parms.i_key == key && t->dev->flags & IFF_UP) {
-                       if (t->dev->type == dev_type)
-                               return t;
-                       if (t->dev->type == ARPHRD_IPGRE && !t2)
-                               t2 = t;
-               }
+               if (t->parms.i_key != key ||
+                   !(t->dev->flags & IFF_UP))
+                       continue;
+
+               if (t->dev->type != ARPHRD_IPGRE &&
+                   t->dev->type != dev_type)
+                       continue;
+
+               idx = 0;
+               if (t->parms.link != link)
+                       idx |= 1;
+               if (t->dev->type != dev_type)
+                       idx |= 2;
+               if (idx == 0)
+                       return t;
+               if (sel[idx] == NULL)
+                       sel[idx] = t;
        }
 
-       if (t2)
-               return t2;
+       for (idx = 1; idx < ARRAY_SIZE(sel); idx++)
+               if (sel[idx] != NULL)
+                       return sel[idx];
 
-       if (ign->fb_tunnel_dev->flags&IFF_UP)
+       if (ign->fb_tunnel_dev->flags & IFF_UP)
                return netdev_priv(ign->fb_tunnel_dev);
+
        return NULL;
 }
 
@@ -286,6 +330,7 @@ static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
        __be32 remote = parms->iph.daddr;
        __be32 local = parms->iph.saddr;
        __be32 key = parms->i_key;
+       int link = parms->link;
        struct ip_tunnel *t, **tp;
        struct ipgre_net *ign = net_generic(net, ipgre_net_id);
 
@@ -293,6 +338,7 @@ static struct ip_tunnel *ipgre_tunnel_find(struct net *net,
                if (local == t->parms.iph.saddr &&
                    remote == t->parms.iph.daddr &&
                    key == t->parms.i_key &&
+                   link == t->parms.link &&
                    type == t->dev->type)
                        break;
 
@@ -423,7 +469,7 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
        }
 
        read_lock(&ipgre_lock);
-       t = ipgre_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr,
+       t = ipgre_tunnel_lookup(skb->dev, iph->daddr, iph->saddr,
                                flags & GRE_KEY ?
                                *(((__be32 *)p) + (grehlen / 4) - 1) : 0,
                                p[1]);
@@ -520,7 +566,7 @@ static int ipgre_rcv(struct sk_buff *skb)
        gre_proto = *(__be16 *)(h + 2);
 
        read_lock(&ipgre_lock);
-       if ((tunnel = ipgre_tunnel_lookup(dev_net(skb->dev),
+       if ((tunnel = ipgre_tunnel_lookup(skb->dev,
                                          iph->saddr, iph->daddr, key,
                                          gre_proto))) {
                struct net_device_stats *stats = &tunnel->dev->stats;
@@ -1142,6 +1188,7 @@ static int ipgre_open(struct net_device *dev)
 static int ipgre_close(struct net_device *dev)
 {
        struct ip_tunnel *t = netdev_priv(dev);
+
        if (ipv4_is_multicast(t->parms.iph.daddr) && t->mlink) {
                struct in_device *in_dev;
                in_dev = inetdev_by_index(dev_net(dev), t->mlink);
@@ -1155,14 +1202,22 @@ static int ipgre_close(struct net_device *dev)
 
 #endif
 
+static const struct net_device_ops ipgre_netdev_ops = {
+       .ndo_init               = ipgre_tunnel_init,
+       .ndo_uninit             = ipgre_tunnel_uninit,
+#ifdef CONFIG_NET_IPGRE_BROADCAST
+       .ndo_open               = ipgre_open,
+       .ndo_stop               = ipgre_close,
+#endif
+       .ndo_start_xmit         = ipgre_tunnel_xmit,
+       .ndo_do_ioctl           = ipgre_tunnel_ioctl,
+       .ndo_change_mtu         = ipgre_tunnel_change_mtu,
+};
+
 static void ipgre_tunnel_setup(struct net_device *dev)
 {
-       dev->init               = ipgre_tunnel_init;
-       dev->uninit             = ipgre_tunnel_uninit;
+       dev->netdev_ops         = &ipgre_netdev_ops;
        dev->destructor         = free_netdev;
-       dev->hard_start_xmit    = ipgre_tunnel_xmit;
-       dev->do_ioctl           = ipgre_tunnel_ioctl;
-       dev->change_mtu         = ipgre_tunnel_change_mtu;
 
        dev->type               = ARPHRD_IPGRE;
        dev->needed_headroom    = LL_MAX_HEADER + sizeof(struct iphdr) + 4;
@@ -1194,8 +1249,6 @@ static int ipgre_tunnel_init(struct net_device *dev)
                                return -EINVAL;
                        dev->flags = IFF_BROADCAST;
                        dev->header_ops = &ipgre_header_ops;
-                       dev->open = ipgre_open;
-                       dev->stop = ipgre_close;
                }
 #endif
        } else
@@ -1204,7 +1257,7 @@ static int ipgre_tunnel_init(struct net_device *dev)
        return 0;
 }
 
-static int ipgre_fb_tunnel_init(struct net_device *dev)
+static void ipgre_fb_tunnel_init(struct net_device *dev)
 {
        struct ip_tunnel *tunnel = netdev_priv(dev);
        struct iphdr *iph = &tunnel->parms.iph;
@@ -1220,7 +1273,6 @@ static int ipgre_fb_tunnel_init(struct net_device *dev)
 
        dev_hold(dev);
        ign->tunnels_wc[0]      = tunnel;
-       return 0;
 }
 
 
@@ -1264,9 +1316,9 @@ static int ipgre_init_net(struct net *net)
                err = -ENOMEM;
                goto err_alloc_dev;
        }
-
-       ign->fb_tunnel_dev->init = ipgre_fb_tunnel_init;
        dev_net_set(ign->fb_tunnel_dev, net);
+
+       ipgre_fb_tunnel_init(ign->fb_tunnel_dev);
        ign->fb_tunnel_dev->rtnl_link_ops = &ipgre_link_ops;
 
        if ((err = register_netdev(ign->fb_tunnel_dev)))
@@ -1397,16 +1449,22 @@ static int ipgre_tap_init(struct net_device *dev)
        return 0;
 }
 
+static const struct net_device_ops ipgre_tap_netdev_ops = {
+       .ndo_init               = ipgre_tap_init,
+       .ndo_uninit             = ipgre_tunnel_uninit,
+       .ndo_start_xmit         = ipgre_tunnel_xmit,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_change_mtu         = ipgre_tunnel_change_mtu,
+};
+
 static void ipgre_tap_setup(struct net_device *dev)
 {
 
        ether_setup(dev);
 
-       dev->init               = ipgre_tap_init;
-       dev->uninit             = ipgre_tunnel_uninit;
+       dev->netdev_ops         = &ipgre_netdev_ops;
        dev->destructor         = free_netdev;
-       dev->hard_start_xmit    = ipgre_tunnel_xmit;
-       dev->change_mtu         = ipgre_tunnel_change_mtu;
 
        dev->iflink             = 0;
        dev->features           |= NETIF_F_NETNS_LOCAL;