Constrain the MTU of upper VLAN devices if the MTU of the L2TP Ethernet device is configured to its default optimal size, which does not leave space for a nested VLAN tag without causing fragmentation.
Refactor l2tp_eth_adjust_mtu() so that it can also be used to determine the optimal size when the L2TP device's MTU is changed. This function needed to move before the net_device_ops definition in order to avoid a forward declaration, but instead the definition of net_device_ops is moved so that the refactoring changes are better represented in the diff. Signed-off-by: Edwin Peer <edwin.p...@broadcom.com> --- net/l2tp/l2tp_eth.c | 114 ++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 51 deletions(-) diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index fd5ac2788e45..6fbb900bc3d1 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -103,28 +103,6 @@ static void l2tp_eth_get_stats64(struct net_device *dev, } -static const struct net_device_ops l2tp_eth_netdev_ops = { - .ndo_init = l2tp_eth_dev_init, - .ndo_uninit = l2tp_eth_dev_uninit, - .ndo_start_xmit = l2tp_eth_dev_xmit, - .ndo_get_stats64 = l2tp_eth_get_stats64, - .ndo_set_mac_address = eth_mac_addr, -}; - -static struct device_type l2tpeth_type = { - .name = "l2tpeth", -}; - -static void l2tp_eth_dev_setup(struct net_device *dev) -{ - SET_NETDEV_DEVTYPE(dev, &l2tpeth_type); - ether_setup(dev); - dev->priv_flags &= ~IFF_TX_SKB_SHARING; - dev->features |= NETIF_F_LLTX; - dev->netdev_ops = &l2tp_eth_netdev_ops; - dev->needs_free_netdev = true; -} - static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len) { struct l2tp_eth_sess *spriv = l2tp_session_priv(session); @@ -215,44 +193,73 @@ static void l2tp_eth_show(struct seq_file *m, void *arg) dev_put(dev); } -static void l2tp_eth_adjust_mtu(struct l2tp_tunnel *tunnel, - struct l2tp_session *session, - struct net_device *dev) +static unsigned int l2tp_eth_best_mtu(struct net_device *dev) { - unsigned int overhead = 0; - u32 l3_overhead = 0; - u32 mtu; + struct l2tp_eth *priv = netdev_priv(dev); + struct l2tp_session *session = priv->session; + struct l2tp_tunnel *tunnel = session->tunnel; + unsigned int mtu, overhead = 0; - /* if the encap is UDP, account for UDP header size */ - if (tunnel->encap == L2TP_ENCAPTYPE_UDP) { - overhead += sizeof(struct udphdr); - dev->needed_headroom += sizeof(struct udphdr); + if (tunnel->sock) { + lock_sock(tunnel->sock); + overhead = kernel_sock_ip_overhead(tunnel->sock); + release_sock(tunnel->sock); } - lock_sock(tunnel->sock); - l3_overhead = kernel_sock_ip_overhead(tunnel->sock); - release_sock(tunnel->sock); - - if (l3_overhead == 0) { + if (overhead == 0) { /* L3 Overhead couldn't be identified, this could be * because tunnel->sock was NULL or the socket's * address family was not IPv4 or IPv6, - * dev mtu stays at 1500. + * assume existing MTU is best */ - return; + return dev->mtu; } - /* Adjust MTU, factor overhead - underlay L3, overlay L2 hdr - * UDP overhead, if any, was already factored in above. - */ - overhead += session->hdr_len + ETH_HLEN + l3_overhead; + /* if the encap is UDP, account for UDP header size */ + if (tunnel->encap == L2TP_ENCAPTYPE_UDP) + overhead += sizeof(struct udphdr); + + /* Maximize MTU, factor in overhead - overlay L2 and Geneve header. + * UDP overhead, if any, and underlay L3 already factored in above. + */ + overhead += session->hdr_len + ETH_HLEN; mtu = l2tp_tunnel_dst_mtu(tunnel) - overhead; if (mtu < dev->min_mtu || mtu > dev->max_mtu) - dev->mtu = ETH_DATA_LEN - overhead; - else - dev->mtu = mtu; + mtu = ETH_DATA_LEN - overhead; - dev->needed_headroom += session->hdr_len; + return mtu; +} + +static int l2tp_eth_change_mtu(struct net_device *dev, int new_mtu) +{ + unsigned int best_mtu = l2tp_eth_best_mtu(dev); + + dev->mtu = new_mtu; + __vlan_constrain_mtu(dev, best_mtu); + return 0; +} + +static const struct net_device_ops l2tp_eth_netdev_ops = { + .ndo_init = l2tp_eth_dev_init, + .ndo_uninit = l2tp_eth_dev_uninit, + .ndo_start_xmit = l2tp_eth_dev_xmit, + .ndo_get_stats64 = l2tp_eth_get_stats64, + .ndo_set_mac_address = eth_mac_addr, + .ndo_change_mtu = l2tp_eth_change_mtu, +}; + +static struct device_type l2tpeth_type = { + .name = "l2tpeth", +}; + +static void l2tp_eth_dev_setup(struct net_device *dev) +{ + SET_NETDEV_DEVTYPE(dev, &l2tpeth_type); + ether_setup(dev); + dev->priv_flags &= ~IFF_TX_SKB_SHARING; + dev->features |= NETIF_F_LLTX; + dev->netdev_ops = &l2tp_eth_netdev_ops; + dev->needs_free_netdev = true; } static int l2tp_eth_create(struct net *net, struct l2tp_tunnel *tunnel, @@ -289,14 +296,19 @@ static int l2tp_eth_create(struct net *net, struct l2tp_tunnel *tunnel, goto err_sess; } - dev_net_set(dev, net); - dev->min_mtu = 0; - dev->max_mtu = ETH_MAX_MTU; - l2tp_eth_adjust_mtu(tunnel, session, dev); + if (tunnel->encap == L2TP_ENCAPTYPE_UDP) + dev->needed_headroom += sizeof(struct udphdr); + dev->needed_headroom += session->hdr_len; priv = netdev_priv(dev); priv->session = session; + dev_net_set(dev, net); + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; + dev->mtu = l2tp_eth_best_mtu(dev); + dev->priv_flags |= IFF_NO_VLAN_ROOM; + session->recv_skb = l2tp_eth_dev_recv; session->session_close = l2tp_eth_delete; if (IS_ENABLED(CONFIG_L2TP_DEBUGFS)) -- 2.26.2