ip_tunnel: Emit events for post-register MTU changes

For tunnels created with IFLA_MTU, MTU of the netdevice is set by
rtnl_create_link() (called from rtnl_newlink()) before the device is
registered. However without IFLA_MTU that's not done.

rtnl_newlink() proceeds by calling struct rtnl_link_ops.newlink, which
via ip_tunnel_newlink() calls register_netdevice(), and that emits
NETDEV_REGISTER. Thus any listeners that inspect the netdevice get the
MTU of 0.

After ip_tunnel_newlink() corrects the MTU after registering the
netdevice, but since there's no event, the listeners don't get to know
about the MTU until something else happens--such as a NETDEV_UP event.
That's not ideal.

So instead of setting the MTU directly, go through dev_set_mtu(), which
takes care of distributing the necessary NETDEV_PRECHANGEMTU and
NETDEV_CHANGEMTU events.

Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Signed-off-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Petr Machata 2018-03-22 19:53:33 +02:00 коммит произвёл David S. Miller
Родитель f36b7534b8
Коммит f6cc9c054e
1 изменённых файлов: 21 добавлений и 5 удалений

Просмотреть файл

@ -362,13 +362,18 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net,
struct ip_tunnel *nt; struct ip_tunnel *nt;
struct net_device *dev; struct net_device *dev;
int t_hlen; int t_hlen;
int mtu;
int err;
BUG_ON(!itn->fb_tunnel_dev); BUG_ON(!itn->fb_tunnel_dev);
dev = __ip_tunnel_create(net, itn->fb_tunnel_dev->rtnl_link_ops, parms); dev = __ip_tunnel_create(net, itn->fb_tunnel_dev->rtnl_link_ops, parms);
if (IS_ERR(dev)) if (IS_ERR(dev))
return ERR_CAST(dev); return ERR_CAST(dev);
dev->mtu = ip_tunnel_bind_dev(dev); mtu = ip_tunnel_bind_dev(dev);
err = dev_set_mtu(dev, mtu);
if (err)
goto err_dev_set_mtu;
nt = netdev_priv(dev); nt = netdev_priv(dev);
t_hlen = nt->hlen + sizeof(struct iphdr); t_hlen = nt->hlen + sizeof(struct iphdr);
@ -376,6 +381,10 @@ static struct ip_tunnel *ip_tunnel_create(struct net *net,
dev->max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen; dev->max_mtu = 0xFFF8 - dev->hard_header_len - t_hlen;
ip_tunnel_add(itn, nt); ip_tunnel_add(itn, nt);
return nt; return nt;
err_dev_set_mtu:
unregister_netdevice(dev);
return ERR_PTR(err);
} }
int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb, int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
@ -1102,17 +1111,24 @@ int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[],
nt->fwmark = fwmark; nt->fwmark = fwmark;
err = register_netdevice(dev); err = register_netdevice(dev);
if (err) if (err)
goto out; goto err_register_netdevice;
if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS]) if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS])
eth_hw_addr_random(dev); eth_hw_addr_random(dev);
mtu = ip_tunnel_bind_dev(dev); mtu = ip_tunnel_bind_dev(dev);
if (!tb[IFLA_MTU]) if (!tb[IFLA_MTU]) {
dev->mtu = mtu; err = dev_set_mtu(dev, mtu);
if (err)
goto err_dev_set_mtu;
}
ip_tunnel_add(itn, nt); ip_tunnel_add(itn, nt);
out: return 0;
err_dev_set_mtu:
unregister_netdevice(dev);
err_register_netdevice:
return err; return err;
} }
EXPORT_SYMBOL_GPL(ip_tunnel_newlink); EXPORT_SYMBOL_GPL(ip_tunnel_newlink);