]> Pileus Git - ~andy/linux/blobdiff - net/ipv6/ip6_tunnel.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[~andy/linux] / net / ipv6 / ip6_tunnel.c
index 1e55866cead7425eefdff36c1ddca1aab9504286..61355f7f4da5b4bb7bcc3c12030e09abe9fa9691 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/netfilter_ipv6.h>
 #include <linux/slab.h>
 #include <linux/hash.h>
+#include <linux/etherdevice.h>
 
 #include <asm/uaccess.h>
 #include <linux/atomic.h>
@@ -315,6 +316,7 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
 
        t = netdev_priv(dev);
        t->parms = *p;
+       t->net = dev_net(dev);
        err = ip6_tnl_create2(dev);
        if (err < 0)
                goto failed_free;
@@ -374,7 +376,7 @@ static void
 ip6_tnl_dev_uninit(struct net_device *dev)
 {
        struct ip6_tnl *t = netdev_priv(dev);
-       struct net *net = dev_net(dev);
+       struct net *net = t->net;
        struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
 
        if (dev == ip6n->fb_tnl_dev)
@@ -741,7 +743,7 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t,
 {
        struct __ip6_tnl_parm *p = &t->parms;
        int ret = 0;
-       struct net *net = dev_net(t->dev);
+       struct net *net = t->net;
 
        if ((p->flags & IP6_TNL_F_CAP_RCV) ||
            ((p->flags & IP6_TNL_F_CAP_PER_PACKET) &&
@@ -800,14 +802,12 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
                        rcu_read_unlock();
                        goto discard;
                }
-               secpath_reset(skb);
                skb->mac_header = skb->network_header;
                skb_reset_network_header(skb);
                skb->protocol = htons(protocol);
-               skb->pkt_type = PACKET_HOST;
                memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
 
-               __skb_tunnel_rx(skb, t->dev);
+               __skb_tunnel_rx(skb, t->dev, t->net);
 
                err = dscp_ecn_decapsulate(t, ipv6h, skb);
                if (unlikely(err)) {
@@ -895,7 +895,7 @@ int ip6_tnl_xmit_ctl(struct ip6_tnl *t)
 {
        struct __ip6_tnl_parm *p = &t->parms;
        int ret = 0;
-       struct net *net = dev_net(t->dev);
+       struct net *net = t->net;
 
        if (p->flags & IP6_TNL_F_CAP_XMIT) {
                struct net_device *ldev = NULL;
@@ -945,8 +945,8 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
                         int encap_limit,
                         __u32 *pmtu)
 {
-       struct net *net = dev_net(dev);
        struct ip6_tnl *t = netdev_priv(dev);
+       struct net *net = t->net;
        struct net_device_stats *stats = &t->dev->stats;
        struct ipv6hdr *ipv6h = ipv6_hdr(skb);
        struct ipv6_tel_txoption opt;
@@ -996,6 +996,8 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
                goto tx_err_dst_release;
        }
 
+       skb_scrub_packet(skb, !net_eq(t->net, dev_net(dev)));
+
        /*
         * Okay, now see if we can stuff it in the buffer as-is.
         */
@@ -1013,7 +1015,6 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
                consume_skb(skb);
                skb = new_skb;
        }
-       skb_dst_drop(skb);
        if (fl6->flowi6_mark) {
                skb_dst_set(skb, dst);
                ndst = NULL;
@@ -1027,6 +1028,12 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
                init_tel_txopt(&opt, encap_limit);
                ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL);
        }
+
+       if (likely(!skb->encapsulation)) {
+               skb_reset_inner_headers(skb);
+               skb->encapsulation = 1;
+       }
+
        skb_push(skb, sizeof(struct ipv6hdr));
        skb_reset_network_header(skb);
        ipv6h = ipv6_hdr(skb);
@@ -1202,7 +1209,7 @@ static void ip6_tnl_link_config(struct ip6_tnl *t)
                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);
 
@@ -1251,7 +1258,7 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
 
 static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
 {
-       struct net *net = dev_net(t->dev);
+       struct net *net = t->net;
        struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
        int err;
 
@@ -1463,8 +1470,10 @@ static void ip6_tnl_dev_setup(struct net_device *dev)
                dev->mtu-=8;
        dev->flags |= IFF_NOARP;
        dev->addr_len = sizeof(struct in6_addr);
-       dev->features |= NETIF_F_NETNS_LOCAL;
        dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+       /* This perm addr will be used as interface identifier by IPv6 */
+       dev->addr_assign_type = NET_ADDR_RANDOM;
+       eth_random_addr(dev->perm_addr);
 }
 
 
@@ -1479,6 +1488,7 @@ ip6_tnl_dev_init_gen(struct net_device *dev)
        struct ip6_tnl *t = netdev_priv(dev);
 
        t->dev = dev;
+       t->net = dev_net(dev);
        dev->tstats = alloc_percpu(struct pcpu_tstats);
        if (!dev->tstats)
                return -ENOMEM;
@@ -1596,9 +1606,9 @@ static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev,
 static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
                              struct nlattr *data[])
 {
-       struct ip6_tnl *t;
+       struct ip6_tnl *t = netdev_priv(dev);
        struct __ip6_tnl_parm p;
-       struct net *net = dev_net(dev);
+       struct net *net = t->net;
        struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
 
        if (dev == ip6n->fb_tnl_dev)
@@ -1699,14 +1709,24 @@ static struct xfrm6_tunnel ip6ip6_handler __read_mostly = {
 
 static void __net_exit ip6_tnl_destroy_tunnels(struct ip6_tnl_net *ip6n)
 {
+       struct net *net = dev_net(ip6n->fb_tnl_dev);
+       struct net_device *dev, *aux;
        int h;
        struct ip6_tnl *t;
        LIST_HEAD(list);
 
+       for_each_netdev_safe(net, dev, aux)
+               if (dev->rtnl_link_ops == &ip6_link_ops)
+                       unregister_netdevice_queue(dev, &list);
+
        for (h = 0; h < HASH_SIZE; h++) {
                t = rtnl_dereference(ip6n->tnls_r_l[h]);
                while (t != NULL) {
-                       unregister_netdevice_queue(t->dev, &list);
+                       /* 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, &list);
                        t = rtnl_dereference(t->next);
                }
        }
@@ -1732,6 +1752,10 @@ static int __net_init ip6_tnl_init_net(struct net *net)
        if (!ip6n->fb_tnl_dev)
                goto err_alloc_dev;
        dev_net_set(ip6n->fb_tnl_dev, net);
+       /* FB netdevice is special: we have one, and only one per netns.
+        * Allowing to move it to another netns is clearly unsafe.
+        */
+       ip6n->fb_tnl_dev->features |= NETIF_F_NETNS_LOCAL;
 
        err = ip6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
        if (err < 0)