|
@@ -72,6 +72,7 @@ struct ip6gre_net {
|
|
|
};
|
|
|
|
|
|
static struct rtnl_link_ops ip6gre_link_ops __read_mostly;
|
|
|
+static struct rtnl_link_ops ip6gre_tap_ops __read_mostly;
|
|
|
static int ip6gre_tunnel_init(struct net_device *dev);
|
|
|
static void ip6gre_tunnel_setup(struct net_device *dev);
|
|
|
static void ip6gre_tunnel_link(struct ip6gre_net *ign, struct ip6_tnl *t);
|
|
@@ -353,10 +354,10 @@ failed_free:
|
|
|
|
|
|
static void ip6gre_tunnel_uninit(struct net_device *dev)
|
|
|
{
|
|
|
- struct net *net = dev_net(dev);
|
|
|
- struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
|
|
|
+ struct ip6_tnl *t = netdev_priv(dev);
|
|
|
+ struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id);
|
|
|
|
|
|
- ip6gre_tunnel_unlink(ign, netdev_priv(dev));
|
|
|
+ ip6gre_tunnel_unlink(ign, t);
|
|
|
dev_put(dev);
|
|
|
}
|
|
|
|
|
@@ -611,8 +612,8 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
|
|
|
int encap_limit,
|
|
|
__u32 *pmtu)
|
|
|
{
|
|
|
- struct net *net = dev_net(dev);
|
|
|
struct ip6_tnl *tunnel = netdev_priv(dev);
|
|
|
+ struct net *net = tunnel->net;
|
|
|
struct net_device *tdev; /* Device to other host */
|
|
|
struct ipv6hdr *ipv6h; /* Our new IP header */
|
|
|
unsigned int max_headroom = 0; /* The extra header space needed */
|
|
@@ -979,7 +980,7 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
|
|
|
int strict = (ipv6_addr_type(&p->raddr) &
|
|
|
(IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL));
|
|
|
|
|
|
- struct rt6_info *rt = rt6_lookup(dev_net(dev),
|
|
|
+ struct rt6_info *rt = rt6_lookup(t->net,
|
|
|
&p->raddr, &p->laddr,
|
|
|
p->link, strict);
|
|
|
|
|
@@ -1063,13 +1064,12 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,
|
|
|
int err = 0;
|
|
|
struct ip6_tnl_parm2 p;
|
|
|
struct __ip6_tnl_parm p1;
|
|
|
- struct ip6_tnl *t;
|
|
|
- struct net *net = dev_net(dev);
|
|
|
+ struct ip6_tnl *t = netdev_priv(dev);
|
|
|
+ struct net *net = t->net;
|
|
|
struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
|
|
|
|
|
|
switch (cmd) {
|
|
|
case SIOCGETTUNNEL:
|
|
|
- t = NULL;
|
|
|
if (dev == ign->fb_tunnel_dev) {
|
|
|
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
|
|
|
err = -EFAULT;
|
|
@@ -1077,9 +1077,9 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,
|
|
|
}
|
|
|
ip6gre_tnl_parm_from_user(&p1, &p);
|
|
|
t = ip6gre_tunnel_locate(net, &p1, 0);
|
|
|
+ if (t == NULL)
|
|
|
+ t = netdev_priv(dev);
|
|
|
}
|
|
|
- if (t == NULL)
|
|
|
- t = netdev_priv(dev);
|
|
|
memset(&p, 0, sizeof(p));
|
|
|
ip6gre_tnl_parm_to_user(&p, &t->parms);
|
|
|
if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p)))
|
|
@@ -1242,7 +1242,6 @@ static void ip6gre_tunnel_setup(struct net_device *dev)
|
|
|
dev->flags |= IFF_NOARP;
|
|
|
dev->iflink = 0;
|
|
|
dev->addr_len = sizeof(struct in6_addr);
|
|
|
- dev->features |= NETIF_F_NETNS_LOCAL;
|
|
|
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
|
|
|
}
|
|
|
|
|
@@ -1297,11 +1296,17 @@ static struct inet6_protocol ip6gre_protocol __read_mostly = {
|
|
|
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
|
|
|
};
|
|
|
|
|
|
-static void ip6gre_destroy_tunnels(struct ip6gre_net *ign,
|
|
|
- struct list_head *head)
|
|
|
+static void ip6gre_destroy_tunnels(struct net *net, struct list_head *head)
|
|
|
{
|
|
|
+ struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
|
|
|
+ struct net_device *dev, *aux;
|
|
|
int prio;
|
|
|
|
|
|
+ for_each_netdev_safe(net, dev, aux)
|
|
|
+ if (dev->rtnl_link_ops == &ip6gre_link_ops ||
|
|
|
+ dev->rtnl_link_ops == &ip6gre_tap_ops)
|
|
|
+ unregister_netdevice_queue(dev, head);
|
|
|
+
|
|
|
for (prio = 0; prio < 4; prio++) {
|
|
|
int h;
|
|
|
for (h = 0; h < HASH_SIZE; h++) {
|
|
@@ -1310,7 +1315,12 @@ static void ip6gre_destroy_tunnels(struct ip6gre_net *ign,
|
|
|
t = rtnl_dereference(ign->tunnels[prio][h]);
|
|
|
|
|
|
while (t != NULL) {
|
|
|
- unregister_netdevice_queue(t->dev, head);
|
|
|
+ /* If dev is in the same netns, it has already
|
|
|
+ * been added to the list by the previous loop.
|
|
|
+ */
|
|
|
+ if (!net_eq(dev_net(t->dev), net))
|
|
|
+ unregister_netdevice_queue(t->dev,
|
|
|
+ head);
|
|
|
t = rtnl_dereference(t->next);
|
|
|
}
|
|
|
}
|
|
@@ -1329,6 +1339,11 @@ static int __net_init ip6gre_init_net(struct net *net)
|
|
|
goto err_alloc_dev;
|
|
|
}
|
|
|
dev_net_set(ign->fb_tunnel_dev, net);
|
|
|
+ /* FB netdevice is special: we have one, and only one per netns.
|
|
|
+ * Allowing to move it to another netns is clearly unsafe.
|
|
|
+ */
|
|
|
+ ign->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
|
|
|
+
|
|
|
|
|
|
ip6gre_fb_tunnel_init(ign->fb_tunnel_dev);
|
|
|
ign->fb_tunnel_dev->rtnl_link_ops = &ip6gre_link_ops;
|
|
@@ -1349,12 +1364,10 @@ err_alloc_dev:
|
|
|
|
|
|
static void __net_exit ip6gre_exit_net(struct net *net)
|
|
|
{
|
|
|
- struct ip6gre_net *ign;
|
|
|
LIST_HEAD(list);
|
|
|
|
|
|
- ign = net_generic(net, ip6gre_net_id);
|
|
|
rtnl_lock();
|
|
|
- ip6gre_destroy_tunnels(ign, &list);
|
|
|
+ ip6gre_destroy_tunnels(net, &list);
|
|
|
unregister_netdevice_many(&list);
|
|
|
rtnl_unlock();
|
|
|
}
|
|
@@ -1531,15 +1544,14 @@ out:
|
|
|
static int ip6gre_changelink(struct net_device *dev, struct nlattr *tb[],
|
|
|
struct nlattr *data[])
|
|
|
{
|
|
|
- struct ip6_tnl *t, *nt;
|
|
|
- struct net *net = dev_net(dev);
|
|
|
+ struct ip6_tnl *t, *nt = netdev_priv(dev);
|
|
|
+ struct net *net = nt->net;
|
|
|
struct ip6gre_net *ign = net_generic(net, ip6gre_net_id);
|
|
|
struct __ip6_tnl_parm p;
|
|
|
|
|
|
if (dev == ign->fb_tunnel_dev)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- nt = netdev_priv(dev);
|
|
|
ip6gre_netlink_parms(data, &p);
|
|
|
|
|
|
t = ip6gre_tunnel_locate(net, &p, 0);
|