/* 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;
/* 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;
}
__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);
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;
}
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]);
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;
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);
#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;
return -EINVAL;
dev->flags = IFF_BROADCAST;
dev->header_ops = &ipgre_header_ops;
- dev->open = ipgre_open;
- dev->stop = ipgre_close;
}
#endif
} else
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;
dev_hold(dev);
ign->tunnels_wc[0] = tunnel;
- return 0;
}
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)))
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;