]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/l2tp/l2tp_eth.c
Merge branch 'ufs-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[karo-tx-linux.git] / net / l2tp / l2tp_eth.c
index 6fd41d7afe1ef27592970de333c75928264dc275..4de2ec94b08cbf5aba016da754b799c46d5f378a 100644 (file)
@@ -30,6 +30,9 @@
 #include <net/xfrm.h>
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
 
 #include "l2tp_core.h"
 
@@ -111,12 +114,13 @@ static void l2tp_eth_get_stats64(struct net_device *dev,
 {
        struct l2tp_eth *priv = netdev_priv(dev);
 
-       stats->tx_bytes   = atomic_long_read(&priv->tx_bytes);
-       stats->tx_packets = atomic_long_read(&priv->tx_packets);
-       stats->tx_dropped = atomic_long_read(&priv->tx_dropped);
-       stats->rx_bytes   = atomic_long_read(&priv->rx_bytes);
-       stats->rx_packets = atomic_long_read(&priv->rx_packets);
-       stats->rx_errors  = atomic_long_read(&priv->rx_errors);
+       stats->tx_bytes   = (unsigned long) atomic_long_read(&priv->tx_bytes);
+       stats->tx_packets = (unsigned long) atomic_long_read(&priv->tx_packets);
+       stats->tx_dropped = (unsigned long) atomic_long_read(&priv->tx_dropped);
+       stats->rx_bytes   = (unsigned long) atomic_long_read(&priv->rx_bytes);
+       stats->rx_packets = (unsigned long) atomic_long_read(&priv->rx_packets);
+       stats->rx_errors  = (unsigned long) atomic_long_read(&priv->rx_errors);
+
 }
 
 static const struct net_device_ops l2tp_eth_netdev_ops = {
@@ -127,13 +131,18 @@ static const struct net_device_ops l2tp_eth_netdev_ops = {
        .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->destructor         = free_netdev;
+       dev->needs_free_netdev  = true;
 }
 
 static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len)
@@ -204,8 +213,58 @@ static void l2tp_eth_show(struct seq_file *m, void *arg)
 }
 #endif
 
+static void l2tp_eth_adjust_mtu(struct l2tp_tunnel *tunnel,
+                               struct l2tp_session *session,
+                               struct net_device *dev)
+{
+       unsigned int overhead = 0;
+       struct dst_entry *dst;
+       u32 l3_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 (session->mtu != 0) {
+               dev->mtu = session->mtu;
+               dev->needed_headroom += session->hdr_len;
+               return;
+       }
+       lock_sock(tunnel->sock);
+       l3_overhead = kernel_sock_ip_overhead(tunnel->sock);
+       release_sock(tunnel->sock);
+       if (l3_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.
+                */
+               return;
+       }
+       /* 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 PMTU discovery was enabled, use discovered MTU on L2TP device */
+       dst = sk_dst_get(tunnel->sock);
+       if (dst) {
+               /* dst_mtu will use PMTU if found, else fallback to intf MTU */
+               u32 pmtu = dst_mtu(dst);
+
+               if (pmtu != 0)
+                       dev->mtu = pmtu;
+               dst_release(dst);
+       }
+       session->mtu = dev->mtu - overhead;
+       dev->mtu = session->mtu;
+       dev->needed_headroom += session->hdr_len;
+}
+
 static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
 {
+       unsigned char name_assign_type;
        struct net_device *dev;
        char name[IFNAMSIZ];
        struct l2tp_tunnel *tunnel;
@@ -222,15 +281,12 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p
        }
 
        if (cfg->ifname) {
-               dev = dev_get_by_name(net, cfg->ifname);
-               if (dev) {
-                       dev_put(dev);
-                       rc = -EEXIST;
-                       goto out;
-               }
                strlcpy(name, cfg->ifname, IFNAMSIZ);
-       } else
+               name_assign_type = NET_NAME_USER;
+       } else {
                strcpy(name, L2TP_ETH_DEV_NAME);
+               name_assign_type = NET_NAME_ENUM;
+       }
 
        session = l2tp_session_create(sizeof(*spriv), tunnel, session_id,
                                      peer_session_id, cfg);
@@ -239,7 +295,7 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p
                goto out;
        }
 
-       dev = alloc_netdev(sizeof(*priv), name, NET_NAME_UNKNOWN,
+       dev = alloc_netdev(sizeof(*priv), name, name_assign_type,
                           l2tp_eth_dev_setup);
        if (!dev) {
                rc = -ENOMEM;
@@ -247,12 +303,9 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p
        }
 
        dev_net_set(dev, net);
-       if (session->mtu == 0)
-               session->mtu = dev->mtu - session->hdr_len;
-       dev->mtu = session->mtu;
-       dev->needed_headroom += session->hdr_len;
        dev->min_mtu = 0;
        dev->max_mtu = ETH_MAX_MTU;
+       l2tp_eth_adjust_mtu(tunnel, session, dev);
 
        priv = netdev_priv(dev);
        priv->dev = dev;