@@ -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))
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.peer@broadcom.com> --- net/l2tp/l2tp_eth.c | 114 ++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 51 deletions(-)