Commit 6c742e71 authored by Nicolas Dichtel's avatar Nicolas Dichtel Committed by David S. Miller

ipip: add x-netns support

This patch allows to switch the netns when packet is encapsulated or
decapsulated. In other word, the encapsulated packet is received in a netns,
where the lookup is done to find the tunnel. Once the tunnel is found, the
packet is decapsulated and injecting into the corresponding interface which
stands to another netns.

When one of the two netns is removed, the tunnel is destroyed.
Signed-off-by: default avatarNicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent fc8f999d
...@@ -102,7 +102,7 @@ void ip_tunnel_dellink(struct net_device *dev, struct list_head *head); ...@@ -102,7 +102,7 @@ void ip_tunnel_dellink(struct net_device *dev, struct list_head *head);
int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
struct rtnl_link_ops *ops, char *devname); struct rtnl_link_ops *ops, char *devname);
void ip_tunnel_delete_net(struct ip_tunnel_net *itn); void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops);
void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
const struct iphdr *tnl_params, const u8 protocol); const struct iphdr *tnl_params, const u8 protocol);
......
...@@ -534,7 +534,7 @@ static int __net_init ipgre_init_net(struct net *net) ...@@ -534,7 +534,7 @@ static int __net_init ipgre_init_net(struct net *net)
static void __net_exit ipgre_exit_net(struct net *net) static void __net_exit ipgre_exit_net(struct net *net)
{ {
struct ip_tunnel_net *itn = net_generic(net, ipgre_net_id); struct ip_tunnel_net *itn = net_generic(net, ipgre_net_id);
ip_tunnel_delete_net(itn); ip_tunnel_delete_net(itn, &ipgre_link_ops);
} }
static struct pernet_operations ipgre_net_ops = { static struct pernet_operations ipgre_net_ops = {
...@@ -767,7 +767,7 @@ static int __net_init ipgre_tap_init_net(struct net *net) ...@@ -767,7 +767,7 @@ static int __net_init ipgre_tap_init_net(struct net *net)
static void __net_exit ipgre_tap_exit_net(struct net *net) static void __net_exit ipgre_tap_exit_net(struct net *net)
{ {
struct ip_tunnel_net *itn = net_generic(net, gre_tap_net_id); struct ip_tunnel_net *itn = net_generic(net, gre_tap_net_id);
ip_tunnel_delete_net(itn); ip_tunnel_delete_net(itn, &ipgre_tap_ops);
} }
static struct pernet_operations ipgre_tap_net_ops = { static struct pernet_operations ipgre_tap_net_ops = {
......
...@@ -350,7 +350,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) ...@@ -350,7 +350,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev)
struct flowi4 fl4; struct flowi4 fl4;
struct rtable *rt; struct rtable *rt;
rt = ip_route_output_tunnel(dev_net(dev), &fl4, rt = ip_route_output_tunnel(tunnel->net, &fl4,
tunnel->parms.iph.protocol, tunnel->parms.iph.protocol,
iph->daddr, iph->saddr, iph->daddr, iph->saddr,
tunnel->parms.o_key, tunnel->parms.o_key,
...@@ -365,7 +365,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev) ...@@ -365,7 +365,7 @@ static int ip_tunnel_bind_dev(struct net_device *dev)
} }
if (!tdev && tunnel->parms.link) if (!tdev && tunnel->parms.link)
tdev = __dev_get_by_index(dev_net(dev), tunnel->parms.link); tdev = __dev_get_by_index(tunnel->net, tunnel->parms.link);
if (tdev) { if (tdev) {
hlen = tdev->hard_header_len + tdev->needed_headroom; hlen = tdev->hard_header_len + tdev->needed_headroom;
...@@ -654,7 +654,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, ...@@ -654,7 +654,7 @@ void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev,
} }
} }
err = iptunnel_xmit(dev_net(dev), rt, skb, err = iptunnel_xmit(tunnel->net, rt, skb,
fl4.saddr, fl4.daddr, protocol, fl4.saddr, fl4.daddr, protocol,
ip_tunnel_ecn_encap(tos, inner_iph, skb), ttl, df); ip_tunnel_ecn_encap(tos, inner_iph, skb), ttl, df);
iptunnel_xmit_stats(err, &dev->stats, dev->tstats); iptunnel_xmit_stats(err, &dev->stats, dev->tstats);
...@@ -821,11 +821,10 @@ static void ip_tunnel_dev_free(struct net_device *dev) ...@@ -821,11 +821,10 @@ static void ip_tunnel_dev_free(struct net_device *dev)
void ip_tunnel_dellink(struct net_device *dev, struct list_head *head) void ip_tunnel_dellink(struct net_device *dev, struct list_head *head)
{ {
struct net *net = dev_net(dev);
struct ip_tunnel *tunnel = netdev_priv(dev); struct ip_tunnel *tunnel = netdev_priv(dev);
struct ip_tunnel_net *itn; struct ip_tunnel_net *itn;
itn = net_generic(net, tunnel->ip_tnl_net_id); itn = net_generic(tunnel->net, tunnel->ip_tnl_net_id);
if (itn->fb_tunnel_dev != dev) { if (itn->fb_tunnel_dev != dev) {
ip_tunnel_del(netdev_priv(dev)); ip_tunnel_del(netdev_priv(dev));
...@@ -855,6 +854,10 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, ...@@ -855,6 +854,10 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
rtnl_lock(); rtnl_lock();
itn->fb_tunnel_dev = __ip_tunnel_create(net, ops, &parms); itn->fb_tunnel_dev = __ip_tunnel_create(net, ops, &parms);
/* FB netdevice is special: we have one, and only one per netns.
* Allowing to move it to another netns is clearly unsafe.
*/
itn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
rtnl_unlock(); rtnl_unlock();
if (IS_ERR(itn->fb_tunnel_dev)) if (IS_ERR(itn->fb_tunnel_dev))
...@@ -864,28 +867,39 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id, ...@@ -864,28 +867,39 @@ int ip_tunnel_init_net(struct net *net, int ip_tnl_net_id,
} }
EXPORT_SYMBOL_GPL(ip_tunnel_init_net); EXPORT_SYMBOL_GPL(ip_tunnel_init_net);
static void ip_tunnel_destroy(struct ip_tunnel_net *itn, struct list_head *head) static void ip_tunnel_destroy(struct ip_tunnel_net *itn, struct list_head *head,
struct rtnl_link_ops *ops)
{ {
struct net *net = dev_net(itn->fb_tunnel_dev);
struct net_device *dev, *aux;
int h; int h;
for_each_netdev_safe(net, dev, aux)
if (dev->rtnl_link_ops == ops)
unregister_netdevice_queue(dev, head);
for (h = 0; h < IP_TNL_HASH_SIZE; h++) { for (h = 0; h < IP_TNL_HASH_SIZE; h++) {
struct ip_tunnel *t; struct ip_tunnel *t;
struct hlist_node *n; struct hlist_node *n;
struct hlist_head *thead = &itn->tunnels[h]; struct hlist_head *thead = &itn->tunnels[h];
hlist_for_each_entry_safe(t, n, thead, hash_node) hlist_for_each_entry_safe(t, n, thead, hash_node)
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);
} }
if (itn->fb_tunnel_dev) if (itn->fb_tunnel_dev)
unregister_netdevice_queue(itn->fb_tunnel_dev, head); unregister_netdevice_queue(itn->fb_tunnel_dev, head);
} }
void ip_tunnel_delete_net(struct ip_tunnel_net *itn) void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops)
{ {
LIST_HEAD(list); LIST_HEAD(list);
rtnl_lock(); rtnl_lock();
ip_tunnel_destroy(itn, &list); ip_tunnel_destroy(itn, &list, ops);
unregister_netdevice_many(&list); unregister_netdevice_many(&list);
rtnl_unlock(); rtnl_unlock();
} }
...@@ -929,23 +943,21 @@ EXPORT_SYMBOL_GPL(ip_tunnel_newlink); ...@@ -929,23 +943,21 @@ EXPORT_SYMBOL_GPL(ip_tunnel_newlink);
int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[], int ip_tunnel_changelink(struct net_device *dev, struct nlattr *tb[],
struct ip_tunnel_parm *p) struct ip_tunnel_parm *p)
{ {
struct ip_tunnel *t, *nt; struct ip_tunnel *t;
struct net *net = dev_net(dev);
struct ip_tunnel *tunnel = netdev_priv(dev); struct ip_tunnel *tunnel = netdev_priv(dev);
struct net *net = tunnel->net;
struct ip_tunnel_net *itn = net_generic(net, tunnel->ip_tnl_net_id); struct ip_tunnel_net *itn = net_generic(net, tunnel->ip_tnl_net_id);
if (dev == itn->fb_tunnel_dev) if (dev == itn->fb_tunnel_dev)
return -EINVAL; return -EINVAL;
nt = netdev_priv(dev);
t = ip_tunnel_find(itn, p, dev->type); t = ip_tunnel_find(itn, p, dev->type);
if (t) { if (t) {
if (t->dev != dev) if (t->dev != dev)
return -EEXIST; return -EEXIST;
} else { } else {
t = nt; t = tunnel;
if (dev->type != ARPHRD_ETHER) { if (dev->type != ARPHRD_ETHER) {
unsigned int nflags = 0; unsigned int nflags = 0;
...@@ -984,6 +996,7 @@ int ip_tunnel_init(struct net_device *dev) ...@@ -984,6 +996,7 @@ int ip_tunnel_init(struct net_device *dev)
} }
tunnel->dev = dev; tunnel->dev = dev;
tunnel->net = dev_net(dev);
strcpy(tunnel->parms.name, dev->name); strcpy(tunnel->parms.name, dev->name);
iph->version = 4; iph->version = 4;
iph->ihl = 5; iph->ihl = 5;
...@@ -994,8 +1007,8 @@ EXPORT_SYMBOL_GPL(ip_tunnel_init); ...@@ -994,8 +1007,8 @@ EXPORT_SYMBOL_GPL(ip_tunnel_init);
void ip_tunnel_uninit(struct net_device *dev) void ip_tunnel_uninit(struct net_device *dev)
{ {
struct net *net = dev_net(dev);
struct ip_tunnel *tunnel = netdev_priv(dev); struct ip_tunnel *tunnel = netdev_priv(dev);
struct net *net = tunnel->net;
struct ip_tunnel_net *itn; struct ip_tunnel_net *itn;
itn = net_generic(net, tunnel->ip_tnl_net_id); itn = net_generic(net, tunnel->ip_tnl_net_id);
......
...@@ -318,7 +318,7 @@ static int __net_init vti_init_net(struct net *net) ...@@ -318,7 +318,7 @@ static int __net_init vti_init_net(struct net *net)
static void __net_exit vti_exit_net(struct net *net) static void __net_exit vti_exit_net(struct net *net)
{ {
struct ip_tunnel_net *itn = net_generic(net, vti_net_id); struct ip_tunnel_net *itn = net_generic(net, vti_net_id);
ip_tunnel_delete_net(itn); ip_tunnel_delete_net(itn, &vti_link_ops);
} }
static struct pernet_operations vti_net_ops = { static struct pernet_operations vti_net_ops = {
......
...@@ -286,7 +286,6 @@ static void ipip_tunnel_setup(struct net_device *dev) ...@@ -286,7 +286,6 @@ static void ipip_tunnel_setup(struct net_device *dev)
dev->flags = IFF_NOARP; dev->flags = IFF_NOARP;
dev->iflink = 0; dev->iflink = 0;
dev->addr_len = 4; dev->addr_len = 4;
dev->features |= NETIF_F_NETNS_LOCAL;
dev->features |= NETIF_F_LLTX; dev->features |= NETIF_F_LLTX;
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
...@@ -437,7 +436,7 @@ static int __net_init ipip_init_net(struct net *net) ...@@ -437,7 +436,7 @@ static int __net_init ipip_init_net(struct net *net)
static void __net_exit ipip_exit_net(struct net *net) static void __net_exit ipip_exit_net(struct net *net)
{ {
struct ip_tunnel_net *itn = net_generic(net, ipip_net_id); struct ip_tunnel_net *itn = net_generic(net, ipip_net_id);
ip_tunnel_delete_net(itn); ip_tunnel_delete_net(itn, &ipip_link_ops);
} }
static struct pernet_operations ipip_net_ops = { static struct pernet_operations ipip_net_ops = {
......
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