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

Reply via email to