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: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
struct ip_tunnel __rcu *next;
struct hlist_node hash_node;
struct net_device *dev;
struct ip_tunnel __rcu *next;
struct hlist_node hash_node;
struct net_device *dev;
+ struct net *net; /* netns for packet i/o */
int err_count; /* Number of arrived ICMP errors */
unsigned long err_time; /* Time when the last ICMP error
int err_count; /* Number of arrived ICMP errors */
unsigned long err_time; /* Time when the last ICMP error
tunnel = netdev_priv(dev);
tunnel->parms = *parms;
tunnel = netdev_priv(dev);
tunnel->parms = *parms;
err = register_netdevice(dev);
if (err)
err = register_netdevice(dev);
if (err)
tstats->rx_bytes += skb->len;
u64_stats_update_end(&tstats->syncp);
tstats->rx_bytes += skb->len;
u64_stats_update_end(&tstats->syncp);
+ if (tunnel->net != dev_net(tunnel->dev))
+ skb_scrub_packet(skb);
+
if (tunnel->dev->type == ARPHRD_ETHER) {
skb->protocol = eth_type_trans(skb, tunnel->dev);
skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
if (tunnel->dev->type == ARPHRD_ETHER) {
skb->protocol = eth_type_trans(skb, tunnel->dev);
skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN);
tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph);
}
tos = ipv6_get_dsfield((const struct ipv6hdr *)inner_iph);
}
- rt = ip_route_output_tunnel(dev_net(dev), &fl4,
+ rt = ip_route_output_tunnel(tunnel->net, &fl4,
protocol,
dst, tnl_params->saddr,
tunnel->parms.o_key,
protocol,
dst, tnl_params->saddr,
tunnel->parms.o_key,
+ if (tunnel->net != dev_net(dev))
+ skb_scrub_packet(skb);
+
if (tunnel->err_count > 0) {
if (time_before(jiffies,
tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
if (tunnel->err_count > 0) {
if (time_before(jiffies,
tunnel->err_time + IPTUNNEL_ERR_TIMEO)) {
if (ip_tunnel_find(itn, p, dev->type))
return -EEXIST;
if (ip_tunnel_find(itn, p, dev->type))
return -EEXIST;
nt->parms = *p;
err = register_netdevice(dev);
if (err)
nt->parms = *p;
err = register_netdevice(dev);
if (err)
static void ipip6_tunnel_uninit(struct net_device *dev)
{
static void ipip6_tunnel_uninit(struct net_device *dev)
{
- struct net *net = dev_net(dev);
- struct sit_net *sitn = net_generic(net, sit_net_id);
+ struct ip_tunnel *tunnel = netdev_priv(dev);
+ struct sit_net *sitn = net_generic(tunnel->net, sit_net_id);
if (dev == sitn->fb_tunnel_dev) {
RCU_INIT_POINTER(sitn->tunnels_wc[0], NULL);
} else {
if (dev == sitn->fb_tunnel_dev) {
RCU_INIT_POINTER(sitn->tunnels_wc[0], NULL);
} else {
- ipip6_tunnel_unlink(sitn, netdev_priv(dev));
- ipip6_tunnel_del_prl(netdev_priv(dev), NULL);
+ ipip6_tunnel_unlink(sitn, tunnel);
+ ipip6_tunnel_del_prl(tunnel, NULL);
tstats->rx_packets++;
tstats->rx_bytes += skb->len;
tstats->rx_packets++;
tstats->rx_bytes += skb->len;
+ if (tunnel->net != dev_net(tunnel->dev))
+ skb_scrub_packet(skb);
- rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
+ rt = ip_route_output_ports(tunnel->net, &fl4, NULL,
dst, tiph->saddr,
0, 0,
IPPROTO_IPV6, RT_TOS(tos),
dst, tiph->saddr,
0, 0,
IPPROTO_IPV6, RT_TOS(tos),
+ if (tunnel->net != dev_net(dev))
+ skb_scrub_packet(skb);
+
/*
* Okay, now see if we can stuff it in the buffer as-is.
*/
/*
* Okay, now see if we can stuff it in the buffer as-is.
*/
iph = &tunnel->parms.iph;
if (iph->daddr) {
iph = &tunnel->parms.iph;
if (iph->daddr) {
- struct rtable *rt = ip_route_output_ports(dev_net(dev), &fl4, NULL,
+ struct rtable *rt = ip_route_output_ports(tunnel->net, &fl4,
+ NULL,
iph->daddr, iph->saddr,
0, 0,
IPPROTO_IPV6,
iph->daddr, iph->saddr,
0, 0,
IPPROTO_IPV6,
}
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) {
dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr);
if (tdev) {
dev->hard_header_len = tdev->hard_header_len + sizeof(struct iphdr);
static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
{
static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
{
- struct net *net = dev_net(t->dev);
+ struct net *net = t->net;
struct sit_net *sitn = net_generic(net, sit_net_id);
ipip6_tunnel_unlink(sitn, t);
struct sit_net *sitn = net_generic(net, sit_net_id);
ipip6_tunnel_unlink(sitn, t);
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
dev->iflink = 0;
dev->addr_len = 4;
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
dev->iflink = 0;
dev->addr_len = 4;
- dev->features |= NETIF_F_NETNS_LOCAL;
dev->features |= NETIF_F_LLTX;
}
dev->features |= NETIF_F_LLTX;
}
struct ip_tunnel *tunnel = netdev_priv(dev);
tunnel->dev = dev;
struct ip_tunnel *tunnel = netdev_priv(dev);
tunnel->dev = dev;
+ tunnel->net = dev_net(dev);
memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
struct sit_net *sitn = net_generic(net, sit_net_id);
tunnel->dev = dev;
struct sit_net *sitn = net_generic(net, sit_net_id);
tunnel->dev = dev;
+ tunnel->net = dev_net(dev);
strcpy(tunnel->parms.name, dev->name);
iph->version = 4;
strcpy(tunnel->parms.name, dev->name);
iph->version = 4;
static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_head *head)
{
static void __net_exit sit_destroy_tunnels(struct sit_net *sitn, struct list_head *head)
{
+ struct net *net = dev_net(sitn->fb_tunnel_dev);
+ struct net_device *dev, *aux;
+ for_each_netdev_safe(net, dev, aux)
+ if (dev->rtnl_link_ops == &sit_link_ops)
+ unregister_netdevice_queue(dev, head);
+
for (prio = 1; prio < 4; prio++) {
int h;
for (h = 0; h < HASH_SIZE; h++) {
for (prio = 1; prio < 4; prio++) {
int h;
for (h = 0; h < HASH_SIZE; h++) {
t = rtnl_dereference(sitn->tunnels[prio][h]);
while (t != NULL) {
t = rtnl_dereference(sitn->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 (dev_net(t->dev) != net)
+ unregister_netdevice_queue(t->dev,
+ head);
t = rtnl_dereference(t->next);
}
}
t = rtnl_dereference(t->next);
}
}
goto err_alloc_dev;
}
dev_net_set(sitn->fb_tunnel_dev, net);
goto err_alloc_dev;
}
dev_net_set(sitn->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.
+ */
+ sitn->fb_tunnel_dev->features |= NETIF_F_NETNS_LOCAL;
err = ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);
if (err)
err = ipip6_fb_tunnel_init(sitn->fb_tunnel_dev);
if (err)