--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:20 +0100
+Subject: L2TP:Adjust intf MTU, add underlay L3, L2 hdrs.
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, "R. Parameswaran" <parameswaran.r7@gmail.com>, "R . Parameswaran" <rparames@brocade.com>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-6-gprocida@google.com>
+
+From: "R. Parameswaran" <parameswaran.r7@gmail.com>
+
+commit b784e7ebfce8cfb16c6f95e14e8532d0768ab7ff upstream.
+
+Existing L2TP kernel code does not derive the optimal MTU for Ethernet
+pseudowires and instead leaves this to a userspace L2TP daemon or
+operator. If an MTU is not specified, the existing kernel code chooses
+an MTU that does not take account of all tunnel header overheads, which
+can lead to unwanted IP fragmentation. When L2TP is used without a
+control plane (userspace daemon), we would prefer that the kernel does a
+better job of choosing a default pseudowire MTU, taking account of all
+tunnel header overheads, including IP header options, if any. This patch
+addresses this.
+
+Change-set here uses the new kernel function, kernel_sock_ip_overhead(),
+to factor the outer IP overhead on the L2TP tunnel socket (including
+IP Options, if any) when calculating the default MTU for an Ethernet
+pseudowire, along with consideration of the inner Ethernet header.
+
+Signed-off-by: R. Parameswaran <rparames@brocade.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_eth.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 51 insertions(+), 4 deletions(-)
+
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -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"
+
+@@ -206,6 +209,53 @@ static void l2tp_eth_show(struct seq_fil
+ }
+ #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;
++ }
++ l3_overhead = kernel_sock_ip_overhead(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)
+ {
+ struct net_device *dev;
+@@ -249,10 +299,7 @@ static int l2tp_eth_create(struct net *n
+ }
+
+ 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;
++ l2tp_eth_adjust_mtu(tunnel, session, dev);
+
+ priv = netdev_priv(dev);
+ priv->dev = dev;
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:23 +0100
+Subject: l2tp: define parameters of l2tp_session_get*() as "const"
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-9-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 9aaef50c44f132e040dcd7686c8e78a3390037c5 upstream.
+
+Make l2tp_pernet()'s parameter constant, so that l2tp_session_get*() can
+declare their "net" variable as "const".
+Also constify "ifname" in l2tp_session_get_by_ifname().
+
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c | 7 ++++---
+ net/l2tp/l2tp_core.h | 5 +++--
+ 2 files changed, 7 insertions(+), 5 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -119,7 +119,7 @@ static inline struct l2tp_tunnel *l2tp_t
+ return sk->sk_user_data;
+ }
+
+-static inline struct l2tp_net *l2tp_pernet(struct net *net)
++static inline struct l2tp_net *l2tp_pernet(const struct net *net)
+ {
+ BUG_ON(!net);
+
+@@ -231,7 +231,7 @@ l2tp_session_id_hash(struct l2tp_tunnel
+ /* Lookup a session. A new reference is held on the returned session.
+ * Optionally calls session->ref() too if do_ref is true.
+ */
+-struct l2tp_session *l2tp_session_get(struct net *net,
++struct l2tp_session *l2tp_session_get(const struct net *net,
+ struct l2tp_tunnel *tunnel,
+ u32 session_id, bool do_ref)
+ {
+@@ -306,7 +306,8 @@ EXPORT_SYMBOL_GPL(l2tp_session_get_nth);
+ /* Lookup a session by interface name.
+ * This is very inefficient but is only used by management interfaces.
+ */
+-struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
++struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
++ const char *ifname,
+ bool do_ref)
+ {
+ struct l2tp_net *pn = l2tp_pernet(net);
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -231,12 +231,13 @@ out:
+ return tunnel;
+ }
+
+-struct l2tp_session *l2tp_session_get(struct net *net,
++struct l2tp_session *l2tp_session_get(const struct net *net,
+ struct l2tp_tunnel *tunnel,
+ u32 session_id, bool do_ref);
+ struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
+ bool do_ref);
+-struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
++struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
++ const char *ifname,
+ bool do_ref);
+ struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
+ struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:24 +0100
+Subject: l2tp: define parameters of l2tp_tunnel_find*() as "const"
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-10-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 2f858b928bf5a8174911aaec76b8b72a9ca0533d upstream.
+
+l2tp_tunnel_find() and l2tp_tunnel_find_nth() don't modify "net".
+
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c | 4 ++--
+ net/l2tp/l2tp_core.h | 4 ++--
+ 2 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -378,7 +378,7 @@ exist:
+
+ /* Lookup a tunnel by id
+ */
+-struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id)
++struct l2tp_tunnel *l2tp_tunnel_find(const struct net *net, u32 tunnel_id)
+ {
+ struct l2tp_tunnel *tunnel;
+ struct l2tp_net *pn = l2tp_pernet(net);
+@@ -396,7 +396,7 @@ struct l2tp_tunnel *l2tp_tunnel_find(str
+ }
+ EXPORT_SYMBOL_GPL(l2tp_tunnel_find);
+
+-struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth)
++struct l2tp_tunnel *l2tp_tunnel_find_nth(const struct net *net, int nth)
+ {
+ struct l2tp_net *pn = l2tp_pernet(net);
+ struct l2tp_tunnel *tunnel;
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -239,8 +239,8 @@ struct l2tp_session *l2tp_session_get_nt
+ struct l2tp_session *l2tp_session_get_by_ifname(const struct net *net,
+ const char *ifname,
+ bool do_ref);
+-struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
+-struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
++struct l2tp_tunnel *l2tp_tunnel_find(const struct net *net, u32 tunnel_id);
++struct l2tp_tunnel *l2tp_tunnel_find_nth(const struct net *net, int nth);
+
+ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id,
+ u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg,
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:34 +0100
+Subject: l2tp: don't register sessions in l2tp_session_create()
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-20-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 3953ae7b218df4d1e544b98a393666f9ae58a78c upstream.
+
+Sessions created by l2tp_session_create() aren't fully initialised:
+some pseudo-wire specific operations need to be done before making the
+session usable. Therefore the PPP and Ethernet pseudo-wires continue
+working on the returned l2tp session while it's already been exposed to
+the rest of the system.
+This can lead to various issues. In particular, the session may enter
+the deletion process before having been fully initialised, which will
+confuse the session removal code.
+
+This patch moves session registration out of l2tp_session_create(), so
+that callers can control when the session is exposed to the rest of the
+system. This is done by the new l2tp_session_register() function.
+
+Only pppol2tp_session_create() can be easily converted to avoid
+modifying its session after registration (the debug message is dropped
+in order to avoid the need for holding a reference on the session).
+
+For pppol2tp_connect() and l2tp_eth_create()), more work is needed.
+That'll be done in followup patches. For now, let's just register the
+session right after its creation, like it was done before. The only
+difference is that we can easily take a reference on the session before
+registering it, so, at least, we're sure it's not going to be freed
+while we're working on it.
+
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c | 21 +++++++--------------
+ net/l2tp/l2tp_core.h | 3 +++
+ net/l2tp/l2tp_eth.c | 9 +++++++++
+ net/l2tp/l2tp_ppp.c | 27 +++++++++++++++++++--------
+ 4 files changed, 38 insertions(+), 22 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -321,8 +321,8 @@ struct l2tp_session *l2tp_session_get_by
+ }
+ EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname);
+
+-static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
+- struct l2tp_session *session)
++int l2tp_session_register(struct l2tp_session *session,
++ struct l2tp_tunnel *tunnel)
+ {
+ struct l2tp_session *session_walk;
+ struct hlist_head *g_head;
+@@ -370,6 +370,10 @@ static int l2tp_session_add_to_tunnel(st
+ hlist_add_head(&session->hlist, head);
+ write_unlock_bh(&tunnel->hlist_lock);
+
++ /* Ignore management session in session count value */
++ if (session->session_id != 0)
++ atomic_inc(&l2tp_session_count);
++
+ return 0;
+
+ err_tlock_pnlock:
+@@ -379,6 +383,7 @@ err_tlock:
+
+ return err;
+ }
++EXPORT_SYMBOL_GPL(l2tp_session_register);
+
+ /* Lookup a tunnel by id
+ */
+@@ -1788,7 +1793,6 @@ EXPORT_SYMBOL_GPL(l2tp_session_set_heade
+ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
+ {
+ struct l2tp_session *session;
+- int err;
+
+ session = kzalloc(sizeof(struct l2tp_session) + priv_size, GFP_KERNEL);
+ if (session != NULL) {
+@@ -1845,17 +1849,6 @@ struct l2tp_session *l2tp_session_create
+
+ l2tp_session_inc_refcount(session);
+
+- err = l2tp_session_add_to_tunnel(tunnel, session);
+- if (err) {
+- kfree(session);
+-
+- return ERR_PTR(err);
+- }
+-
+- /* Ignore management session in session count value */
+- if (session->session_id != 0)
+- atomic_inc(&l2tp_session_count);
+-
+ return session;
+ }
+
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -259,6 +259,9 @@ struct l2tp_session *l2tp_session_create
+ struct l2tp_tunnel *tunnel,
+ u32 session_id, u32 peer_session_id,
+ struct l2tp_session_cfg *cfg);
++int l2tp_session_register(struct l2tp_session *session,
++ struct l2tp_tunnel *tunnel);
++
+ void __l2tp_session_unhash(struct l2tp_session *session);
+ int l2tp_session_delete(struct l2tp_session *session);
+ void l2tp_session_free(struct l2tp_session *session);
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -267,6 +267,13 @@ static int l2tp_eth_create(struct net *n
+ goto out;
+ }
+
++ l2tp_session_inc_refcount(session);
++ rc = l2tp_session_register(session, tunnel);
++ if (rc < 0) {
++ kfree(session);
++ goto out;
++ }
++
+ dev = alloc_netdev(sizeof(*priv), name, NET_NAME_UNKNOWN,
+ l2tp_eth_dev_setup);
+ if (!dev) {
+@@ -298,6 +305,7 @@ static int l2tp_eth_create(struct net *n
+ __module_get(THIS_MODULE);
+ /* Must be done after register_netdev() */
+ strlcpy(session->ifname, dev->name, IFNAMSIZ);
++ l2tp_session_dec_refcount(session);
+
+ dev_hold(dev);
+
+@@ -308,6 +316,7 @@ out_del_dev:
+ spriv->dev = NULL;
+ out_del_session:
+ l2tp_session_delete(session);
++ l2tp_session_dec_refcount(session);
+ out:
+ return rc;
+ }
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -722,6 +722,14 @@ static int pppol2tp_connect(struct socke
+ error = PTR_ERR(session);
+ goto end;
+ }
++
++ l2tp_session_inc_refcount(session);
++ error = l2tp_session_register(session, tunnel);
++ if (error < 0) {
++ kfree(session);
++ goto end;
++ }
++ drop_refcnt = true;
+ }
+
+ /* Associate session with its PPPoL2TP socket */
+@@ -807,7 +815,7 @@ static int pppol2tp_session_create(struc
+ /* Error if tunnel socket is not prepped */
+ if (!tunnel->sock) {
+ error = -ENOENT;
+- goto out;
++ goto err;
+ }
+
+ /* Default MTU values. */
+@@ -822,18 +830,21 @@ static int pppol2tp_session_create(struc
+ peer_session_id, cfg);
+ if (IS_ERR(session)) {
+ error = PTR_ERR(session);
+- goto out;
++ goto err;
+ }
+
+ ps = l2tp_session_priv(session);
+ ps->tunnel_sock = tunnel->sock;
+
+- l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n",
+- session->name);
+-
+- error = 0;
+-
+-out:
++ error = l2tp_session_register(session, tunnel);
++ if (error < 0)
++ goto err_sess;
++
++ return 0;
++
++err_sess:
++ kfree(session);
++err:
+ return error;
+ }
+
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:33 +0100
+Subject: l2tp: fix l2tp_eth module loading
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-19-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 9f775ead5e570e7e19015b9e4e2f3dd6e71a5935 upstream.
+
+The l2tp_eth module crashes if its netlink callbacks are run when the
+pernet data aren't initialised.
+
+We should normally register_pernet_device() before the genl callbacks.
+However, the pernet data only maintain a list of l2tpeth interfaces,
+and this list is never used. So let's just drop pernet handling
+instead.
+
+Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_eth.c | 51 ++-------------------------------------------------
+ 1 file changed, 2 insertions(+), 49 deletions(-)
+
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -44,7 +44,6 @@ struct l2tp_eth {
+ struct net_device *dev;
+ struct sock *tunnel_sock;
+ struct l2tp_session *session;
+- struct list_head list;
+ atomic_long_t tx_bytes;
+ atomic_long_t tx_packets;
+ atomic_long_t tx_dropped;
+@@ -58,17 +57,6 @@ struct l2tp_eth_sess {
+ struct net_device *dev;
+ };
+
+-/* per-net private data for this module */
+-static unsigned int l2tp_eth_net_id;
+-struct l2tp_eth_net {
+- struct list_head l2tp_eth_dev_list;
+- spinlock_t l2tp_eth_lock;
+-};
+-
+-static inline struct l2tp_eth_net *l2tp_eth_pernet(struct net *net)
+-{
+- return net_generic(net, l2tp_eth_net_id);
+-}
+
+ static int l2tp_eth_dev_init(struct net_device *dev)
+ {
+@@ -84,12 +72,6 @@ static int l2tp_eth_dev_init(struct net_
+
+ static void l2tp_eth_dev_uninit(struct net_device *dev)
+ {
+- struct l2tp_eth *priv = netdev_priv(dev);
+- struct l2tp_eth_net *pn = l2tp_eth_pernet(dev_net(dev));
+-
+- spin_lock(&pn->l2tp_eth_lock);
+- list_del_init(&priv->list);
+- spin_unlock(&pn->l2tp_eth_lock);
+ dev_put(dev);
+ }
+
+@@ -266,7 +248,6 @@ static int l2tp_eth_create(struct net *n
+ struct l2tp_eth *priv;
+ struct l2tp_eth_sess *spriv;
+ int rc;
+- struct l2tp_eth_net *pn;
+
+ if (cfg->ifname) {
+ dev = dev_get_by_name(net, cfg->ifname);
+@@ -299,7 +280,6 @@ static int l2tp_eth_create(struct net *n
+ priv = netdev_priv(dev);
+ priv->dev = dev;
+ priv->session = session;
+- INIT_LIST_HEAD(&priv->list);
+
+ priv->tunnel_sock = tunnel->sock;
+ session->recv_skb = l2tp_eth_dev_recv;
+@@ -320,10 +300,6 @@ static int l2tp_eth_create(struct net *n
+ strlcpy(session->ifname, dev->name, IFNAMSIZ);
+
+ dev_hold(dev);
+- pn = l2tp_eth_pernet(dev_net(dev));
+- spin_lock(&pn->l2tp_eth_lock);
+- list_add(&priv->list, &pn->l2tp_eth_dev_list);
+- spin_unlock(&pn->l2tp_eth_lock);
+
+ return 0;
+
+@@ -336,22 +312,6 @@ out:
+ return rc;
+ }
+
+-static __net_init int l2tp_eth_init_net(struct net *net)
+-{
+- struct l2tp_eth_net *pn = net_generic(net, l2tp_eth_net_id);
+-
+- INIT_LIST_HEAD(&pn->l2tp_eth_dev_list);
+- spin_lock_init(&pn->l2tp_eth_lock);
+-
+- return 0;
+-}
+-
+-static struct pernet_operations l2tp_eth_net_ops = {
+- .init = l2tp_eth_init_net,
+- .id = &l2tp_eth_net_id,
+- .size = sizeof(struct l2tp_eth_net),
+-};
+-
+
+ static const struct l2tp_nl_cmd_ops l2tp_eth_nl_cmd_ops = {
+ .session_create = l2tp_eth_create,
+@@ -365,25 +325,18 @@ static int __init l2tp_eth_init(void)
+
+ err = l2tp_nl_register_ops(L2TP_PWTYPE_ETH, &l2tp_eth_nl_cmd_ops);
+ if (err)
+- goto out;
+-
+- err = register_pernet_device(&l2tp_eth_net_ops);
+- if (err)
+- goto out_unreg;
++ goto err;
+
+ pr_info("L2TP ethernet pseudowire support (L2TPv3)\n");
+
+ return 0;
+
+-out_unreg:
+- l2tp_nl_unregister_ops(L2TP_PWTYPE_ETH);
+-out:
++err:
+ return err;
+ }
+
+ static void __exit l2tp_eth_exit(void)
+ {
+- unregister_pernet_device(&l2tp_eth_net_ops);
+ l2tp_nl_unregister_ops(L2TP_PWTYPE_ETH);
+ }
+
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:30 +0100
+Subject: l2tp: hold tunnel used while creating sessions with netlink
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-16-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit e702c1204eb57788ef189c839c8c779368267d70 upstream.
+
+Use l2tp_tunnel_get() to retrieve tunnel, so that it can't go away on
+us. Otherwise l2tp_tunnel_destruct() might release the last reference
+count concurrently, thus freeing the tunnel while we're using it.
+
+Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_netlink.c | 21 ++++++++++++---------
+ 1 file changed, 12 insertions(+), 9 deletions(-)
+
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -510,8 +510,9 @@ static int l2tp_nl_cmd_session_create(st
+ ret = -EINVAL;
+ goto out;
+ }
++
+ tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
+- tunnel = l2tp_tunnel_find(net, tunnel_id);
++ tunnel = l2tp_tunnel_get(net, tunnel_id);
+ if (!tunnel) {
+ ret = -ENODEV;
+ goto out;
+@@ -519,24 +520,24 @@ static int l2tp_nl_cmd_session_create(st
+
+ if (!info->attrs[L2TP_ATTR_SESSION_ID]) {
+ ret = -EINVAL;
+- goto out;
++ goto out_tunnel;
+ }
+ session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
+
+ if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) {
+ ret = -EINVAL;
+- goto out;
++ goto out_tunnel;
+ }
+ peer_session_id = nla_get_u32(info->attrs[L2TP_ATTR_PEER_SESSION_ID]);
+
+ if (!info->attrs[L2TP_ATTR_PW_TYPE]) {
+ ret = -EINVAL;
+- goto out;
++ goto out_tunnel;
+ }
+ cfg.pw_type = nla_get_u16(info->attrs[L2TP_ATTR_PW_TYPE]);
+ if (cfg.pw_type >= __L2TP_PWTYPE_MAX) {
+ ret = -EINVAL;
+- goto out;
++ goto out_tunnel;
+ }
+
+ if (tunnel->version > 2) {
+@@ -555,7 +556,7 @@ static int l2tp_nl_cmd_session_create(st
+ u16 len = nla_len(info->attrs[L2TP_ATTR_COOKIE]);
+ if (len > 8) {
+ ret = -EINVAL;
+- goto out;
++ goto out_tunnel;
+ }
+ cfg.cookie_len = len;
+ memcpy(&cfg.cookie[0], nla_data(info->attrs[L2TP_ATTR_COOKIE]), len);
+@@ -564,7 +565,7 @@ static int l2tp_nl_cmd_session_create(st
+ u16 len = nla_len(info->attrs[L2TP_ATTR_PEER_COOKIE]);
+ if (len > 8) {
+ ret = -EINVAL;
+- goto out;
++ goto out_tunnel;
+ }
+ cfg.peer_cookie_len = len;
+ memcpy(&cfg.peer_cookie[0], nla_data(info->attrs[L2TP_ATTR_PEER_COOKIE]), len);
+@@ -607,7 +608,7 @@ static int l2tp_nl_cmd_session_create(st
+ if ((l2tp_nl_cmd_ops[cfg.pw_type] == NULL) ||
+ (l2tp_nl_cmd_ops[cfg.pw_type]->session_create == NULL)) {
+ ret = -EPROTONOSUPPORT;
+- goto out;
++ goto out_tunnel;
+ }
+
+ /* Check that pseudowire-specific params are present */
+@@ -617,7 +618,7 @@ static int l2tp_nl_cmd_session_create(st
+ case L2TP_PWTYPE_ETH_VLAN:
+ if (!info->attrs[L2TP_ATTR_VLAN_ID]) {
+ ret = -EINVAL;
+- goto out;
++ goto out_tunnel;
+ }
+ break;
+ case L2TP_PWTYPE_ETH:
+@@ -645,6 +646,8 @@ static int l2tp_nl_cmd_session_create(st
+ }
+ }
+
++out_tunnel:
++ l2tp_tunnel_dec_refcount(tunnel);
+ out:
+ return ret;
+ }
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:28 +0100
+Subject: l2tp: hold tunnel while handling genl tunnel updates
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-14-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 8c0e421525c9eb50d68e8f633f703ca31680b746 upstream.
+
+We need to make sure the tunnel is not going to be destroyed by
+l2tp_tunnel_destruct() concurrently.
+
+Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_netlink.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -310,8 +310,8 @@ static int l2tp_nl_cmd_tunnel_modify(str
+ }
+ tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
+
+- tunnel = l2tp_tunnel_find(net, tunnel_id);
+- if (tunnel == NULL) {
++ tunnel = l2tp_tunnel_get(net, tunnel_id);
++ if (!tunnel) {
+ ret = -ENODEV;
+ goto out;
+ }
+@@ -322,6 +322,8 @@ static int l2tp_nl_cmd_tunnel_modify(str
+ ret = l2tp_tunnel_notify(&l2tp_nl_family, info,
+ tunnel, L2TP_CMD_TUNNEL_MODIFY);
+
++ l2tp_tunnel_dec_refcount(tunnel);
++
+ out:
+ return ret;
+ }
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:29 +0100
+Subject: l2tp: hold tunnel while handling genl TUNNEL_GET commands
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-15-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 4e4b21da3acc68a7ea55f850cacc13706b7480e9 upstream.
+
+Use l2tp_tunnel_get() instead of l2tp_tunnel_find() so that we get
+a reference on the tunnel, preventing l2tp_tunnel_destruct() from
+freeing it from under us.
+
+Also move l2tp_tunnel_get() below nlmsg_new() so that we only take
+the reference when needed.
+
+Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_netlink.c | 27 +++++++++++++++------------
+ 1 file changed, 15 insertions(+), 12 deletions(-)
+
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -436,34 +436,37 @@ static int l2tp_nl_cmd_tunnel_get(struct
+
+ if (!info->attrs[L2TP_ATTR_CONN_ID]) {
+ ret = -EINVAL;
+- goto out;
++ goto err;
+ }
+
+ tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
+
+- tunnel = l2tp_tunnel_find(net, tunnel_id);
+- if (tunnel == NULL) {
+- ret = -ENODEV;
+- goto out;
+- }
+-
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+- goto out;
++ goto err;
++ }
++
++ tunnel = l2tp_tunnel_get(net, tunnel_id);
++ if (!tunnel) {
++ ret = -ENODEV;
++ goto err_nlmsg;
+ }
+
+ ret = l2tp_nl_tunnel_send(msg, info->snd_portid, info->snd_seq,
+ NLM_F_ACK, tunnel, L2TP_CMD_TUNNEL_GET);
+ if (ret < 0)
+- goto err_out;
++ goto err_nlmsg_tunnel;
++
++ l2tp_tunnel_dec_refcount(tunnel);
+
+ return genlmsg_unicast(net, msg, info->snd_portid);
+
+-err_out:
++err_nlmsg_tunnel:
++ l2tp_tunnel_dec_refcount(tunnel);
++err_nlmsg:
+ nlmsg_free(msg);
+-
+-out:
++err:
+ return ret;
+ }
+
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:26 +0100
+Subject: l2tp: hold tunnel while looking up sessions in l2tp_netlink
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-12-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 54652eb12c1b72e9602d09cb2821d5760939190f upstream.
+
+l2tp_tunnel_find() doesn't take a reference on the returned tunnel.
+Therefore, it's unsafe to use it because the returned tunnel can go
+away on us anytime.
+
+Fix this by defining l2tp_tunnel_get(), which works like
+l2tp_tunnel_find(), but takes a reference on the returned tunnel.
+Caller then has to drop this reference using l2tp_tunnel_dec_refcount().
+
+As l2tp_tunnel_dec_refcount() needs to be moved to l2tp_core.h, let's
+simplify the patch and not move the L2TP_REFCNT_DEBUG part. This code
+has been broken (not even compiling) in May 2012 by
+commit a4ca44fa578c ("net: l2tp: Standardize logging styles")
+and fixed more than two years later by
+commit 29abe2fda54f ("l2tp: fix missing line continuation"). So it
+doesn't appear to be used by anyone.
+
+Same thing for l2tp_tunnel_free(); instead of moving it to l2tp_core.h,
+let's just simplify things and call kfree_rcu() directly in
+l2tp_tunnel_dec_refcount(). Extra assertions and debugging code
+provided by l2tp_tunnel_free() didn't help catching any of the
+reference counting and socket handling issues found while working on
+this series.
+
+Backporting Notes
+
+l2tp_core.c: This patch deletes some code / moves some code to
+l2tp_core.h and follows the patch (not including in this series) that
+switched from atomic to refcount_t. Moved code changed back to atomic.
+
+Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c | 66 +++++++++++++++---------------------------------
+ net/l2tp/l2tp_core.h | 13 +++++++++
+ net/l2tp/l2tp_netlink.c | 6 ++--
+ 3 files changed, 38 insertions(+), 47 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -112,7 +112,6 @@ struct l2tp_net {
+ spinlock_t l2tp_session_hlist_lock;
+ };
+
+-static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
+
+ static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
+ {
+@@ -126,39 +125,6 @@ static inline struct l2tp_net *l2tp_pern
+ return net_generic(net, l2tp_net_id);
+ }
+
+-/* Tunnel reference counts. Incremented per session that is added to
+- * the tunnel.
+- */
+-static inline void l2tp_tunnel_inc_refcount_1(struct l2tp_tunnel *tunnel)
+-{
+- atomic_inc(&tunnel->ref_count);
+-}
+-
+-static inline void l2tp_tunnel_dec_refcount_1(struct l2tp_tunnel *tunnel)
+-{
+- if (atomic_dec_and_test(&tunnel->ref_count))
+- l2tp_tunnel_free(tunnel);
+-}
+-#ifdef L2TP_REFCNT_DEBUG
+-#define l2tp_tunnel_inc_refcount(_t) \
+-do { \
+- pr_debug("l2tp_tunnel_inc_refcount: %s:%d %s: cnt=%d\n", \
+- __func__, __LINE__, (_t)->name, \
+- atomic_read(&_t->ref_count)); \
+- l2tp_tunnel_inc_refcount_1(_t); \
+-} while (0)
+-#define l2tp_tunnel_dec_refcount(_t) \
+-do { \
+- pr_debug("l2tp_tunnel_dec_refcount: %s:%d %s: cnt=%d\n", \
+- __func__, __LINE__, (_t)->name, \
+- atomic_read(&_t->ref_count)); \
+- l2tp_tunnel_dec_refcount_1(_t); \
+-} while (0)
+-#else
+-#define l2tp_tunnel_inc_refcount(t) l2tp_tunnel_inc_refcount_1(t)
+-#define l2tp_tunnel_dec_refcount(t) l2tp_tunnel_dec_refcount_1(t)
+-#endif
+-
+ /* Session hash global list for L2TPv3.
+ * The session_id SHOULD be random according to RFC3931, but several
+ * L2TP implementations use incrementing session_ids. So we do a real
+@@ -228,6 +194,27 @@ l2tp_session_id_hash(struct l2tp_tunnel
+ return &tunnel->session_hlist[hash_32(session_id, L2TP_HASH_BITS)];
+ }
+
++/* Lookup a tunnel. A new reference is held on the returned tunnel. */
++struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)
++{
++ const struct l2tp_net *pn = l2tp_pernet(net);
++ struct l2tp_tunnel *tunnel;
++
++ rcu_read_lock_bh();
++ list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
++ if (tunnel->tunnel_id == tunnel_id) {
++ l2tp_tunnel_inc_refcount(tunnel);
++ rcu_read_unlock_bh();
++
++ return tunnel;
++ }
++ }
++ rcu_read_unlock_bh();
++
++ return NULL;
++}
++EXPORT_SYMBOL_GPL(l2tp_tunnel_get);
++
+ /* Lookup a session. A new reference is held on the returned session.
+ * Optionally calls session->ref() too if do_ref is true.
+ */
+@@ -1346,17 +1333,6 @@ static void l2tp_udp_encap_destroy(struc
+ }
+ }
+
+-/* Really kill the tunnel.
+- * Come here only when all sessions have been cleared from the tunnel.
+- */
+-static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel)
+-{
+- BUG_ON(atomic_read(&tunnel->ref_count) != 0);
+- BUG_ON(tunnel->sock != NULL);
+- l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: free...\n", tunnel->name);
+- kfree_rcu(tunnel, rcu);
+-}
+-
+ /* Workqueue tunnel deletion function */
+ static void l2tp_tunnel_del_work(struct work_struct *work)
+ {
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -231,6 +231,8 @@ out:
+ return tunnel;
+ }
+
++struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id);
++
+ struct l2tp_session *l2tp_session_get(const struct net *net,
+ struct l2tp_tunnel *tunnel,
+ u32 session_id, bool do_ref);
+@@ -269,6 +271,17 @@ int l2tp_nl_register_ops(enum l2tp_pwtyp
+ void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
+ int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+
++static inline void l2tp_tunnel_inc_refcount(struct l2tp_tunnel *tunnel)
++{
++ atomic_inc(&tunnel->ref_count);
++}
++
++static inline void l2tp_tunnel_dec_refcount(struct l2tp_tunnel *tunnel)
++{
++ if (atomic_dec_and_test(&tunnel->ref_count))
++ kfree_rcu(tunnel, rcu);
++}
++
+ /* Session reference counts. Incremented when code obtains a reference
+ * to a session.
+ */
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -72,10 +72,12 @@ static struct l2tp_session *l2tp_nl_sess
+ (info->attrs[L2TP_ATTR_CONN_ID])) {
+ tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
+ session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
+- tunnel = l2tp_tunnel_find(net, tunnel_id);
+- if (tunnel)
++ tunnel = l2tp_tunnel_get(net, tunnel_id);
++ if (tunnel) {
+ session = l2tp_session_get(net, tunnel, session_id,
+ do_ref);
++ l2tp_tunnel_dec_refcount(tunnel);
++ }
+ }
+
+ return session;
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:27 +0100
+Subject: l2tp: hold tunnel while processing genl delete command
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-13-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit bb0a32ce4389e17e47e198d2cddaf141561581ad upstream.
+
+l2tp_nl_cmd_tunnel_delete() needs to take a reference on the tunnel, to
+prevent it from being concurrently freed by l2tp_tunnel_destruct().
+
+Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_netlink.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -280,8 +280,8 @@ static int l2tp_nl_cmd_tunnel_delete(str
+ }
+ tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
+
+- tunnel = l2tp_tunnel_find(net, tunnel_id);
+- if (tunnel == NULL) {
++ tunnel = l2tp_tunnel_get(net, tunnel_id);
++ if (!tunnel) {
+ ret = -ENODEV;
+ goto out;
+ }
+@@ -291,6 +291,8 @@ static int l2tp_nl_cmd_tunnel_delete(str
+
+ l2tp_tunnel_delete(tunnel);
+
++ l2tp_tunnel_dec_refcount(tunnel);
++
+ out:
+ return ret;
+ }
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:35 +0100
+Subject: l2tp: initialise l2tp_eth sessions before registering them
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-21-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit ee28de6bbd78c2e18111a0aef43ea746f28d2073 upstream.
+
+Sessions must be initialised before being made externally visible by
+l2tp_session_register(). Otherwise the session may be concurrently
+deleted before being initialised, which can confuse the deletion path
+and eventually lead to kernel oops.
+
+Therefore, we need to move l2tp_session_register() down in
+l2tp_eth_create(), but also handle the intermediate step where only the
+session or the netdevice has been registered.
+
+We can't just call l2tp_session_register() in ->ndo_init() because
+we'd have no way to properly undo this operation in ->ndo_uninit().
+Instead, let's register the session and the netdevice in two different
+steps and protect the session's device pointer with RCU.
+
+And now that we allow the session's .dev field to be NULL, we don't
+need to prevent the netdevice from being removed anymore. So we can
+drop the dev_hold() and dev_put() calls in l2tp_eth_create() and
+l2tp_eth_dev_uninit().
+
+Backporting Notes
+
+l2tp_eth.c: In l2tp_eth_create the "out" label was renamed to "err".
+There was one extra occurrence of "goto out" to update.
+
+Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_eth.c | 108 ++++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 76 insertions(+), 32 deletions(-)
+
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -54,7 +54,7 @@ struct l2tp_eth {
+
+ /* via l2tp_session_priv() */
+ struct l2tp_eth_sess {
+- struct net_device *dev;
++ struct net_device __rcu *dev;
+ };
+
+
+@@ -72,7 +72,14 @@ static int l2tp_eth_dev_init(struct net_
+
+ static void l2tp_eth_dev_uninit(struct net_device *dev)
+ {
+- dev_put(dev);
++ struct l2tp_eth *priv = netdev_priv(dev);
++ struct l2tp_eth_sess *spriv;
++
++ spriv = l2tp_session_priv(priv->session);
++ RCU_INIT_POINTER(spriv->dev, NULL);
++ /* No need for synchronize_net() here. We're called by
++ * unregister_netdev*(), which does the synchronisation for us.
++ */
+ }
+
+ static int l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+@@ -126,8 +133,8 @@ static void l2tp_eth_dev_setup(struct ne
+ 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);
+- struct net_device *dev = spriv->dev;
+- struct l2tp_eth *priv = netdev_priv(dev);
++ struct net_device *dev;
++ struct l2tp_eth *priv;
+
+ if (session->debug & L2TP_MSG_DATA) {
+ unsigned int length;
+@@ -151,16 +158,25 @@ static void l2tp_eth_dev_recv(struct l2t
+ skb_dst_drop(skb);
+ nf_reset(skb);
+
++ rcu_read_lock();
++ dev = rcu_dereference(spriv->dev);
++ if (!dev)
++ goto error_rcu;
++
++ priv = netdev_priv(dev);
+ if (dev_forward_skb(dev, skb) == NET_RX_SUCCESS) {
+ atomic_long_inc(&priv->rx_packets);
+ atomic_long_add(data_len, &priv->rx_bytes);
+ } else {
+ atomic_long_inc(&priv->rx_errors);
+ }
++ rcu_read_unlock();
++
+ return;
+
++error_rcu:
++ rcu_read_unlock();
+ error:
+- atomic_long_inc(&priv->rx_errors);
+ kfree_skb(skb);
+ }
+
+@@ -171,11 +187,15 @@ static void l2tp_eth_delete(struct l2tp_
+
+ if (session) {
+ spriv = l2tp_session_priv(session);
+- dev = spriv->dev;
++
++ rtnl_lock();
++ dev = rtnl_dereference(spriv->dev);
+ if (dev) {
+- unregister_netdev(dev);
+- spriv->dev = NULL;
++ unregister_netdevice(dev);
++ rtnl_unlock();
+ module_put(THIS_MODULE);
++ } else {
++ rtnl_unlock();
+ }
+ }
+ }
+@@ -185,9 +205,20 @@ static void l2tp_eth_show(struct seq_fil
+ {
+ struct l2tp_session *session = arg;
+ struct l2tp_eth_sess *spriv = l2tp_session_priv(session);
+- struct net_device *dev = spriv->dev;
++ struct net_device *dev;
++
++ rcu_read_lock();
++ dev = rcu_dereference(spriv->dev);
++ if (!dev) {
++ rcu_read_unlock();
++ return;
++ }
++ dev_hold(dev);
++ rcu_read_unlock();
+
+ seq_printf(m, " interface %s\n", dev->name);
++
++ dev_put(dev);
+ }
+ #endif
+
+@@ -254,7 +285,7 @@ static int l2tp_eth_create(struct net *n
+ if (dev) {
+ dev_put(dev);
+ rc = -EEXIST;
+- goto out;
++ goto err;
+ }
+ strlcpy(name, cfg->ifname, IFNAMSIZ);
+ } else
+@@ -264,21 +295,14 @@ static int l2tp_eth_create(struct net *n
+ peer_session_id, cfg);
+ if (IS_ERR(session)) {
+ rc = PTR_ERR(session);
+- goto out;
+- }
+-
+- l2tp_session_inc_refcount(session);
+- rc = l2tp_session_register(session, tunnel);
+- if (rc < 0) {
+- kfree(session);
+- goto out;
++ goto err;
+ }
+
+ dev = alloc_netdev(sizeof(*priv), name, NET_NAME_UNKNOWN,
+ l2tp_eth_dev_setup);
+ if (!dev) {
+ rc = -ENOMEM;
+- goto out_del_session;
++ goto err_sess;
+ }
+
+ dev_net_set(dev, net);
+@@ -296,28 +320,48 @@ static int l2tp_eth_create(struct net *n
+ #endif
+
+ spriv = l2tp_session_priv(session);
+- spriv->dev = dev;
+
+- rc = register_netdev(dev);
+- if (rc < 0)
+- goto out_del_dev;
++ l2tp_session_inc_refcount(session);
++
++ rtnl_lock();
++
++ /* Register both device and session while holding the rtnl lock. This
++ * ensures that l2tp_eth_delete() will see that there's a device to
++ * unregister, even if it happened to run before we assign spriv->dev.
++ */
++ rc = l2tp_session_register(session, tunnel);
++ if (rc < 0) {
++ rtnl_unlock();
++ goto err_sess_dev;
++ }
++
++ rc = register_netdevice(dev);
++ if (rc < 0) {
++ rtnl_unlock();
++ l2tp_session_delete(session);
++ l2tp_session_dec_refcount(session);
++ free_netdev(dev);
++
++ return rc;
++ }
+
+- __module_get(THIS_MODULE);
+- /* Must be done after register_netdev() */
+ strlcpy(session->ifname, dev->name, IFNAMSIZ);
++ rcu_assign_pointer(spriv->dev, dev);
++
++ rtnl_unlock();
++
+ l2tp_session_dec_refcount(session);
+
+- dev_hold(dev);
++ __module_get(THIS_MODULE);
+
+ return 0;
+
+-out_del_dev:
+- free_netdev(dev);
+- spriv->dev = NULL;
+-out_del_session:
+- l2tp_session_delete(session);
++err_sess_dev:
+ l2tp_session_dec_refcount(session);
+-out:
++ free_netdev(dev);
++err_sess:
++ kfree(session);
++err:
+ return rc;
+ }
+
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:37 +0100
+Subject: l2tp: initialise PPP sessions before registering them
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-23-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit f98be6c6359e7e4a61aaefb9964c1db31cb9ec0c upstream.
+
+pppol2tp_connect() initialises L2TP sessions after they've been exposed
+to the rest of the system by l2tp_session_register(). This puts
+sessions into transient states that are the source of several races, in
+particular with session's deletion path.
+
+This patch centralises the initialisation code into
+pppol2tp_session_init(), which is called before the registration phase.
+The only field that can't be set before session registration is the
+pppol2tp socket pointer, which has already been converted to RCU. So
+pppol2tp_connect() should now be race-free.
+
+The session's .session_close() callback is now set before registration.
+Therefore, it's always called when l2tp_core deletes the session, even
+if it was created by pppol2tp_session_create() and hasn't been plugged
+to a pppol2tp socket yet. That'd prevent session free because the extra
+reference taken by pppol2tp_session_close() wouldn't be dropped by the
+socket's ->sk_destruct() callback (pppol2tp_session_destruct()).
+We could set .session_close() only while connecting a session to its
+pppol2tp socket, or teach pppol2tp_session_close() to avoid grabbing a
+reference when the session isn't connected, but that'd require adding
+some form of synchronisation to be race free.
+
+Instead of that, we can just let the pppol2tp socket hold a reference
+on the session as soon as it starts depending on it (that is, in
+pppol2tp_connect()). Then we don't need to utilise
+pppol2tp_session_close() to hold a reference at the last moment to
+prevent l2tp_core from dropping it.
+
+When releasing the socket, pppol2tp_release() now deletes the session
+using the standard l2tp_session_delete() function, instead of merely
+removing it from hash tables. l2tp_session_delete() drops the reference
+the sessions holds on itself, but also makes sure it doesn't remove a
+session twice. So it can safely be called, even if l2tp_core already
+tried, or is concurrently trying, to remove the session.
+Finally, pppol2tp_session_destruct() drops the reference held by the
+socket.
+
+Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_ppp.c | 69 ++++++++++++++++++++++++++++------------------------
+ 1 file changed, 38 insertions(+), 31 deletions(-)
+
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -449,9 +449,6 @@ static void pppol2tp_session_close(struc
+ inet_shutdown(sk->sk_socket, SEND_SHUTDOWN);
+ sock_put(sk);
+ }
+-
+- /* Don't let the session go away before our socket does */
+- l2tp_session_inc_refcount(session);
+ }
+
+ /* Really kill the session socket. (Called from sock_put() if
+@@ -507,8 +504,7 @@ static int pppol2tp_release(struct socke
+ if (session != NULL) {
+ struct pppol2tp_session *ps;
+
+- __l2tp_session_unhash(session);
+- l2tp_session_queue_purge(session);
++ l2tp_session_delete(session);
+
+ ps = l2tp_session_priv(session);
+ mutex_lock(&ps->sk_lock);
+@@ -600,6 +596,35 @@ static void pppol2tp_show(struct seq_fil
+ }
+ #endif
+
++static void pppol2tp_session_init(struct l2tp_session *session)
++{
++ struct pppol2tp_session *ps;
++ struct dst_entry *dst;
++
++ session->recv_skb = pppol2tp_recv;
++ session->session_close = pppol2tp_session_close;
++#if IS_ENABLED(CONFIG_L2TP_DEBUGFS)
++ session->show = pppol2tp_show;
++#endif
++
++ ps = l2tp_session_priv(session);
++ mutex_init(&ps->sk_lock);
++ ps->tunnel_sock = session->tunnel->sock;
++ ps->owner = current->pid;
++
++ /* If PMTU discovery was enabled, use the MTU that was discovered */
++ dst = sk_dst_get(session->tunnel->sock);
++ if (dst) {
++ u32 pmtu = dst_mtu(dst);
++
++ if (pmtu) {
++ session->mtu = pmtu - PPPOL2TP_HEADER_OVERHEAD;
++ session->mru = pmtu - PPPOL2TP_HEADER_OVERHEAD;
++ }
++ dst_release(dst);
++ }
++}
++
+ /* connect() handler. Attach a PPPoX socket to a tunnel UDP socket
+ */
+ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
+@@ -611,7 +636,6 @@ static int pppol2tp_connect(struct socke
+ struct l2tp_session *session = NULL;
+ struct l2tp_tunnel *tunnel;
+ struct pppol2tp_session *ps;
+- struct dst_entry *dst;
+ struct l2tp_session_cfg cfg = { 0, };
+ int error = 0;
+ u32 tunnel_id, peer_tunnel_id;
+@@ -760,8 +784,8 @@ static int pppol2tp_connect(struct socke
+ goto end;
+ }
+
++ pppol2tp_session_init(session);
+ ps = l2tp_session_priv(session);
+- mutex_init(&ps->sk_lock);
+ l2tp_session_inc_refcount(session);
+
+ mutex_lock(&ps->sk_lock);
+@@ -774,26 +798,6 @@ static int pppol2tp_connect(struct socke
+ drop_refcnt = true;
+ }
+
+- ps->owner = current->pid;
+- ps->tunnel_sock = tunnel->sock;
+-
+- session->recv_skb = pppol2tp_recv;
+- session->session_close = pppol2tp_session_close;
+-#if IS_ENABLED(CONFIG_L2TP_DEBUGFS)
+- session->show = pppol2tp_show;
+-#endif
+-
+- /* If PMTU discovery was enabled, use the MTU that was discovered */
+- dst = sk_dst_get(tunnel->sock);
+- if (dst != NULL) {
+- u32 pmtu = dst_mtu(dst);
+-
+- if (pmtu != 0)
+- session->mtu = session->mru = pmtu -
+- PPPOL2TP_HEADER_OVERHEAD;
+- dst_release(dst);
+- }
+-
+ /* Special case: if source & dest session_id == 0x0000, this
+ * socket is being created to manage the tunnel. Just set up
+ * the internal context for use by ioctl() and sockopt()
+@@ -827,6 +831,12 @@ out_no_ppp:
+ rcu_assign_pointer(ps->sk, sk);
+ mutex_unlock(&ps->sk_lock);
+
++ /* Keep the reference we've grabbed on the session: sk doesn't expect
++ * the session to disappear. pppol2tp_session_destruct() is responsible
++ * for dropping it.
++ */
++ drop_refcnt = false;
++
+ sk->sk_state = PPPOX_CONNECTED;
+ l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n",
+ session->name);
+@@ -848,7 +858,6 @@ static int pppol2tp_session_create(struc
+ {
+ int error;
+ struct l2tp_session *session;
+- struct pppol2tp_session *ps;
+
+ /* Error if tunnel socket is not prepped */
+ if (!tunnel->sock) {
+@@ -871,9 +880,7 @@ static int pppol2tp_session_create(struc
+ goto err;
+ }
+
+- ps = l2tp_session_priv(session);
+- mutex_init(&ps->sk_lock);
+- ps->tunnel_sock = tunnel->sock;
++ pppol2tp_session_init(session);
+
+ error = l2tp_session_register(session, tunnel);
+ if (error < 0)
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:25 +0100
+Subject: l2tp: initialise session's refcount before making it reachable
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-11-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 9ee369a405c57613d7c83a3967780c3e30c52ecc upstream.
+
+Sessions must be fully initialised before calling
+l2tp_session_add_to_tunnel(). Otherwise, there's a short time frame
+where partially initialised sessions can be accessed by external users.
+
+Backporting Notes
+
+l2tp_core.c: moving code that had been converted from atomic to
+refcount_t by an earlier change (which isn't being included in this
+patch series).
+
+Fixes: dbdbc73b4478 ("l2tp: fix duplicate session creation")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -1847,6 +1847,8 @@ struct l2tp_session *l2tp_session_create
+
+ l2tp_session_set_header_len(session, tunnel->version);
+
++ l2tp_session_inc_refcount(session);
++
+ err = l2tp_session_add_to_tunnel(tunnel, session);
+ if (err) {
+ kfree(session);
+@@ -1854,10 +1856,6 @@ struct l2tp_session *l2tp_session_create
+ return ERR_PTR(err);
+ }
+
+- /* Bump the reference count. The session context is deleted
+- * only when this drops to zero.
+- */
+- l2tp_session_inc_refcount(session);
+ l2tp_tunnel_inc_refcount(tunnel);
+
+ /* Ensure tunnel socket isn't deleted */
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:32 +0100
+Subject: l2tp: pass tunnel pointer to ->session_create()
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-18-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit f026bc29a8e093edfbb2a77700454b285c97e8ad upstream.
+
+Using l2tp_tunnel_find() in pppol2tp_session_create() and
+l2tp_eth_create() is racy, because no reference is held on the
+returned session. These functions are only used to implement the
+->session_create callback which is run by l2tp_nl_cmd_session_create().
+Therefore searching for the parent tunnel isn't necessary because
+l2tp_nl_cmd_session_create() already has a pointer to it and holds a
+reference.
+
+This patch modifies ->session_create()'s prototype to directly pass the
+the parent tunnel as parameter, thus avoiding searching for it in
+pppol2tp_session_create() and l2tp_eth_create().
+
+Since we have to touch the ->session_create() call in
+l2tp_nl_cmd_session_create(), let's also remove the useless conditional:
+we know that ->session_create isn't NULL at this point because it's
+already been checked earlier in this same function.
+
+Finally, one might be tempted to think that the removed
+l2tp_tunnel_find() calls were harmless because they would return the
+same tunnel as the one held by l2tp_nl_cmd_session_create() anyway.
+But that tunnel might be removed and a new one created with same tunnel
+Id before the l2tp_tunnel_find() call. In this case l2tp_tunnel_find()
+would return the new tunnel which wouldn't be protected by the
+reference held by l2tp_nl_cmd_session_create().
+
+Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
+Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.h | 4 +++-
+ net/l2tp/l2tp_eth.c | 11 +++--------
+ net/l2tp/l2tp_netlink.c | 8 ++++----
+ net/l2tp/l2tp_ppp.c | 19 +++++++------------
+ 4 files changed, 17 insertions(+), 25 deletions(-)
+
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -201,7 +201,9 @@ struct l2tp_tunnel {
+ };
+
+ struct l2tp_nl_cmd_ops {
+- int (*session_create)(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg);
++ int (*session_create)(struct net *net, struct l2tp_tunnel *tunnel,
++ u32 session_id, u32 peer_session_id,
++ struct l2tp_session_cfg *cfg);
+ int (*session_delete)(struct l2tp_session *session);
+ };
+
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -256,23 +256,18 @@ static void l2tp_eth_adjust_mtu(struct l
+ 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)
++static int l2tp_eth_create(struct net *net, struct l2tp_tunnel *tunnel,
++ u32 session_id, u32 peer_session_id,
++ struct l2tp_session_cfg *cfg)
+ {
+ struct net_device *dev;
+ char name[IFNAMSIZ];
+- struct l2tp_tunnel *tunnel;
+ struct l2tp_session *session;
+ struct l2tp_eth *priv;
+ struct l2tp_eth_sess *spriv;
+ int rc;
+ struct l2tp_eth_net *pn;
+
+- tunnel = l2tp_tunnel_find(net, tunnel_id);
+- if (!tunnel) {
+- rc = -ENODEV;
+- goto out;
+- }
+-
+ if (cfg->ifname) {
+ dev = dev_get_by_name(net, cfg->ifname);
+ if (dev) {
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -632,10 +632,10 @@ static int l2tp_nl_cmd_session_create(st
+ break;
+ }
+
+- ret = -EPROTONOSUPPORT;
+- if (l2tp_nl_cmd_ops[cfg.pw_type]->session_create)
+- ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id,
+- session_id, peer_session_id, &cfg);
++ ret = l2tp_nl_cmd_ops[cfg.pw_type]->session_create(net, tunnel,
++ session_id,
++ peer_session_id,
++ &cfg);
+
+ if (ret >= 0) {
+ session = l2tp_session_get(net, tunnel, session_id, false);
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -795,25 +795,20 @@ end:
+
+ #ifdef CONFIG_L2TP_V3
+
+-/* Called when creating sessions via the netlink interface.
+- */
+-static int pppol2tp_session_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
++/* Called when creating sessions via the netlink interface. */
++static int pppol2tp_session_create(struct net *net, struct l2tp_tunnel *tunnel,
++ u32 session_id, u32 peer_session_id,
++ struct l2tp_session_cfg *cfg)
+ {
+ int error;
+- struct l2tp_tunnel *tunnel;
+ struct l2tp_session *session;
+ struct pppol2tp_session *ps;
+
+- tunnel = l2tp_tunnel_find(net, tunnel_id);
+-
+- /* Error if we can't find the tunnel */
+- error = -ENOENT;
+- if (tunnel == NULL)
+- goto out;
+-
+ /* Error if tunnel socket is not prepped */
+- if (tunnel->sock == NULL)
++ if (!tunnel->sock) {
++ error = -ENOENT;
+ goto out;
++ }
+
+ /* Default MTU values. */
+ if (cfg->mtu == 0)
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:31 +0100
+Subject: l2tp: prevent creation of sessions on terminated tunnels
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-17-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit f3c66d4e144a0904ea9b95d23ed9f8eb38c11bfb upstream.
+
+l2tp_tunnel_destruct() sets tunnel->sock to NULL, then removes the
+tunnel from the pernet list and finally closes all its sessions.
+Therefore, it's possible to add a session to a tunnel that is still
+reachable, but for which tunnel->sock has already been reset. This can
+make l2tp_session_create() dereference a NULL pointer when calling
+sock_hold(tunnel->sock).
+
+This patch adds the .acpt_newsess field to struct l2tp_tunnel, which is
+used by l2tp_tunnel_closeall() to prevent addition of new sessions to
+tunnels. Resetting tunnel->sock is done after l2tp_tunnel_closeall()
+returned, so that l2tp_session_add_to_tunnel() can safely take a
+reference on it when .acpt_newsess is true.
+
+The .acpt_newsess field is modified in l2tp_tunnel_closeall(), rather
+than in l2tp_tunnel_destruct(), so that it benefits all tunnel removal
+mechanisms. E.g. on UDP tunnels, a session could be added to a tunnel
+after l2tp_udp_encap_destroy() proceeded. This would prevent the tunnel
+from being removed because of the references held by this new session
+on the tunnel and its socket. Even though the session could be removed
+manually later on, this defeats the purpose of
+commit 9980d001cec8 ("l2tp: add udp encap socket destroy handler").
+
+Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c | 41 ++++++++++++++++++++++++++++-------------
+ net/l2tp/l2tp_core.h | 4 ++++
+ 2 files changed, 32 insertions(+), 13 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -328,13 +328,21 @@ static int l2tp_session_add_to_tunnel(st
+ struct hlist_head *g_head;
+ struct hlist_head *head;
+ struct l2tp_net *pn;
++ int err;
+
+ head = l2tp_session_id_hash(tunnel, session->session_id);
+
+ write_lock_bh(&tunnel->hlist_lock);
++ if (!tunnel->acpt_newsess) {
++ err = -ENODEV;
++ goto err_tlock;
++ }
++
+ hlist_for_each_entry(session_walk, head, hlist)
+- if (session_walk->session_id == session->session_id)
+- goto exist;
++ if (session_walk->session_id == session->session_id) {
++ err = -EEXIST;
++ goto err_tlock;
++ }
+
+ if (tunnel->version == L2TP_HDR_VER_3) {
+ pn = l2tp_pernet(tunnel->l2tp_net);
+@@ -342,12 +350,21 @@ static int l2tp_session_add_to_tunnel(st
+ session->session_id);
+
+ spin_lock_bh(&pn->l2tp_session_hlist_lock);
++
+ hlist_for_each_entry(session_walk, g_head, global_hlist)
+- if (session_walk->session_id == session->session_id)
+- goto exist_glob;
++ if (session_walk->session_id == session->session_id) {
++ err = -EEXIST;
++ goto err_tlock_pnlock;
++ }
+
++ l2tp_tunnel_inc_refcount(tunnel);
++ sock_hold(tunnel->sock);
+ hlist_add_head_rcu(&session->global_hlist, g_head);
++
+ spin_unlock_bh(&pn->l2tp_session_hlist_lock);
++ } else {
++ l2tp_tunnel_inc_refcount(tunnel);
++ sock_hold(tunnel->sock);
+ }
+
+ hlist_add_head(&session->hlist, head);
+@@ -355,12 +372,12 @@ static int l2tp_session_add_to_tunnel(st
+
+ return 0;
+
+-exist_glob:
++err_tlock_pnlock:
+ spin_unlock_bh(&pn->l2tp_session_hlist_lock);
+-exist:
++err_tlock:
+ write_unlock_bh(&tunnel->hlist_lock);
+
+- return -EEXIST;
++ return err;
+ }
+
+ /* Lookup a tunnel by id
+@@ -1246,7 +1263,6 @@ static void l2tp_tunnel_destruct(struct
+ /* Remove hooks into tunnel socket */
+ sk->sk_destruct = tunnel->old_sk_destruct;
+ sk->sk_user_data = NULL;
+- tunnel->sock = NULL;
+
+ /* Remove the tunnel struct from the tunnel list */
+ pn = l2tp_pernet(tunnel->l2tp_net);
+@@ -1256,6 +1272,8 @@ static void l2tp_tunnel_destruct(struct
+ atomic_dec(&l2tp_tunnel_count);
+
+ l2tp_tunnel_closeall(tunnel);
++
++ tunnel->sock = NULL;
+ l2tp_tunnel_dec_refcount(tunnel);
+
+ /* Call the original destructor */
+@@ -1280,6 +1298,7 @@ void l2tp_tunnel_closeall(struct l2tp_tu
+ tunnel->name);
+
+ write_lock_bh(&tunnel->hlist_lock);
++ tunnel->acpt_newsess = false;
+ for (hash = 0; hash < L2TP_HASH_SIZE; hash++) {
+ again:
+ hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) {
+@@ -1583,6 +1602,7 @@ int l2tp_tunnel_create(struct net *net,
+ tunnel->magic = L2TP_TUNNEL_MAGIC;
+ sprintf(&tunnel->name[0], "tunl %u", tunnel_id);
+ rwlock_init(&tunnel->hlist_lock);
++ tunnel->acpt_newsess = true;
+
+ /* The net we belong to */
+ tunnel->l2tp_net = net;
+@@ -1832,11 +1852,6 @@ struct l2tp_session *l2tp_session_create
+ return ERR_PTR(err);
+ }
+
+- l2tp_tunnel_inc_refcount(tunnel);
+-
+- /* Ensure tunnel socket isn't deleted */
+- sock_hold(tunnel->sock);
+-
+ /* Ignore management session in session count value */
+ if (session->session_id != 0)
+ atomic_inc(&l2tp_session_count);
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -162,6 +162,10 @@ struct l2tp_tunnel {
+
+ struct rcu_head rcu;
+ rwlock_t hlist_lock; /* protect session_hlist */
++ bool acpt_newsess; /* Indicates whether this
++ * tunnel accepts new sessions.
++ * Protected by hlist_lock.
++ */
+ struct hlist_head session_hlist[L2TP_HASH_SIZE];
+ /* hashed list of sessions,
+ * hashed by id */
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:36 +0100
+Subject: l2tp: protect sock pointer of struct pppol2tp_session with RCU
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-22-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit ee40fb2e1eb5bc0ddd3f2f83c6e39a454ef5a741 upstream.
+
+pppol2tp_session_create() registers sessions that can't have their
+corresponding socket initialised. This socket has to be created by
+userspace, then connected to the session by pppol2tp_connect().
+Therefore, we need to protect the pppol2tp socket pointer of L2TP
+sessions, so that it can safely be updated when userspace is connecting
+or closing the socket. This will eventually allow pppol2tp_connect()
+to avoid generating transient states while initialising its parts of the
+session.
+
+To this end, this patch protects the pppol2tp socket pointer using RCU.
+
+The pppol2tp socket pointer is still set in pppol2tp_connect(), but
+only once we know the function isn't going to fail. It's eventually
+reset by pppol2tp_release(), which now has to wait for a grace period
+to elapse before it can drop the last reference on the socket. This
+ensures that pppol2tp_session_get_sock() can safely grab a reference
+on the socket, even after ps->sk is reset to NULL but before this
+operation actually gets visible from pppol2tp_session_get_sock().
+
+The rest is standard RCU conversion: pppol2tp_recv(), which already
+runs in atomic context, is simply enclosed by rcu_read_lock() and
+rcu_read_unlock(), while other functions are converted to use
+pppol2tp_session_get_sock() followed by sock_put().
+pppol2tp_session_setsockopt() is a special case. It used to retrieve
+the pppol2tp socket from the L2TP session, which itself was retrieved
+from the pppol2tp socket. Therefore we can just avoid dereferencing
+ps->sk and directly use the original socket pointer instead.
+
+With all users of ps->sk now handling NULL and concurrent updates, the
+L2TP ->ref() and ->deref() callbacks aren't needed anymore. Therefore,
+rather than converting pppol2tp_session_sock_hold() and
+pppol2tp_session_sock_put(), we can just drop them.
+
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_ppp.c | 154 ++++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 101 insertions(+), 53 deletions(-)
+
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -122,8 +122,11 @@
+ struct pppol2tp_session {
+ int owner; /* pid that opened the socket */
+
+- struct sock *sock; /* Pointer to the session
++ struct mutex sk_lock; /* Protects .sk */
++ struct sock __rcu *sk; /* Pointer to the session
+ * PPPoX socket */
++ struct sock *__sk; /* Copy of .sk, for cleanup */
++ struct rcu_head rcu; /* For asynchronous release */
+ struct sock *tunnel_sock; /* Pointer to the tunnel UDP
+ * socket */
+ int flags; /* accessed by PPPIOCGFLAGS.
+@@ -138,6 +141,24 @@ static const struct ppp_channel_ops pppo
+
+ static const struct proto_ops pppol2tp_ops;
+
++/* Retrieves the pppol2tp socket associated to a session.
++ * A reference is held on the returned socket, so this function must be paired
++ * with sock_put().
++ */
++static struct sock *pppol2tp_session_get_sock(struct l2tp_session *session)
++{
++ struct pppol2tp_session *ps = l2tp_session_priv(session);
++ struct sock *sk;
++
++ rcu_read_lock();
++ sk = rcu_dereference(ps->sk);
++ if (sk)
++ sock_hold(sk);
++ rcu_read_unlock();
++
++ return sk;
++}
++
+ /* Helpers to obtain tunnel/session contexts from sockets.
+ */
+ static inline struct l2tp_session *pppol2tp_sock_to_session(struct sock *sk)
+@@ -224,7 +245,8 @@ static void pppol2tp_recv(struct l2tp_se
+ /* If the socket is bound, send it in to PPP's input queue. Otherwise
+ * queue it on the session socket.
+ */
+- sk = ps->sock;
++ rcu_read_lock();
++ sk = rcu_dereference(ps->sk);
+ if (sk == NULL)
+ goto no_sock;
+
+@@ -247,30 +269,16 @@ static void pppol2tp_recv(struct l2tp_se
+ kfree_skb(skb);
+ }
+ }
++ rcu_read_unlock();
+
+ return;
+
+ no_sock:
++ rcu_read_unlock();
+ l2tp_info(session, L2TP_MSG_DATA, "%s: no socket\n", session->name);
+ kfree_skb(skb);
+ }
+
+-static void pppol2tp_session_sock_hold(struct l2tp_session *session)
+-{
+- struct pppol2tp_session *ps = l2tp_session_priv(session);
+-
+- if (ps->sock)
+- sock_hold(ps->sock);
+-}
+-
+-static void pppol2tp_session_sock_put(struct l2tp_session *session)
+-{
+- struct pppol2tp_session *ps = l2tp_session_priv(session);
+-
+- if (ps->sock)
+- sock_put(ps->sock);
+-}
+-
+ /************************************************************************
+ * Transmit handling
+ ***********************************************************************/
+@@ -431,14 +439,16 @@ abort:
+ */
+ static void pppol2tp_session_close(struct l2tp_session *session)
+ {
+- struct pppol2tp_session *ps = l2tp_session_priv(session);
+- struct sock *sk = ps->sock;
+- struct socket *sock = sk->sk_socket;
++ struct sock *sk;
+
+ BUG_ON(session->magic != L2TP_SESSION_MAGIC);
+
+- if (sock)
+- inet_shutdown(sock, SEND_SHUTDOWN);
++ sk = pppol2tp_session_get_sock(session);
++ if (sk) {
++ if (sk->sk_socket)
++ inet_shutdown(sk->sk_socket, SEND_SHUTDOWN);
++ sock_put(sk);
++ }
+
+ /* Don't let the session go away before our socket does */
+ l2tp_session_inc_refcount(session);
+@@ -461,6 +471,14 @@ static void pppol2tp_session_destruct(st
+ }
+ }
+
++static void pppol2tp_put_sk(struct rcu_head *head)
++{
++ struct pppol2tp_session *ps;
++
++ ps = container_of(head, typeof(*ps), rcu);
++ sock_put(ps->__sk);
++}
++
+ /* Called when the PPPoX socket (session) is closed.
+ */
+ static int pppol2tp_release(struct socket *sock)
+@@ -486,11 +504,24 @@ static int pppol2tp_release(struct socke
+
+ session = pppol2tp_sock_to_session(sk);
+
+- /* Purge any queued data */
+ if (session != NULL) {
++ struct pppol2tp_session *ps;
++
+ __l2tp_session_unhash(session);
+ l2tp_session_queue_purge(session);
+- sock_put(sk);
++
++ ps = l2tp_session_priv(session);
++ mutex_lock(&ps->sk_lock);
++ ps->__sk = rcu_dereference_protected(ps->sk,
++ lockdep_is_held(&ps->sk_lock));
++ RCU_INIT_POINTER(ps->sk, NULL);
++ mutex_unlock(&ps->sk_lock);
++ call_rcu(&ps->rcu, pppol2tp_put_sk);
++
++ /* Rely on the sock_put() call at the end of the function for
++ * dropping the reference held by pppol2tp_sock_to_session().
++ * The last reference will be dropped by pppol2tp_put_sk().
++ */
+ }
+ release_sock(sk);
+
+@@ -557,12 +588,14 @@ out:
+ static void pppol2tp_show(struct seq_file *m, void *arg)
+ {
+ struct l2tp_session *session = arg;
+- struct pppol2tp_session *ps = l2tp_session_priv(session);
++ struct sock *sk;
++
++ sk = pppol2tp_session_get_sock(session);
++ if (sk) {
++ struct pppox_sock *po = pppox_sk(sk);
+
+- if (ps) {
+- struct pppox_sock *po = pppox_sk(ps->sock);
+- if (po)
+- seq_printf(m, " interface %s\n", ppp_dev_name(&po->chan));
++ seq_printf(m, " interface %s\n", ppp_dev_name(&po->chan));
++ sock_put(sk);
+ }
+ }
+ #endif
+@@ -700,13 +733,17 @@ static int pppol2tp_connect(struct socke
+ /* Using a pre-existing session is fine as long as it hasn't
+ * been connected yet.
+ */
+- if (ps->sock) {
++ mutex_lock(&ps->sk_lock);
++ if (rcu_dereference_protected(ps->sk,
++ lockdep_is_held(&ps->sk_lock))) {
++ mutex_unlock(&ps->sk_lock);
+ error = -EEXIST;
+ goto end;
+ }
+
+ /* consistency checks */
+ if (ps->tunnel_sock != tunnel->sock) {
++ mutex_unlock(&ps->sk_lock);
+ error = -EEXIST;
+ goto end;
+ }
+@@ -723,19 +760,21 @@ static int pppol2tp_connect(struct socke
+ goto end;
+ }
+
++ ps = l2tp_session_priv(session);
++ mutex_init(&ps->sk_lock);
+ l2tp_session_inc_refcount(session);
++
++ mutex_lock(&ps->sk_lock);
+ error = l2tp_session_register(session, tunnel);
+ if (error < 0) {
++ mutex_unlock(&ps->sk_lock);
+ kfree(session);
+ goto end;
+ }
+ drop_refcnt = true;
+ }
+
+- /* Associate session with its PPPoL2TP socket */
+- ps = l2tp_session_priv(session);
+ ps->owner = current->pid;
+- ps->sock = sk;
+ ps->tunnel_sock = tunnel->sock;
+
+ session->recv_skb = pppol2tp_recv;
+@@ -744,12 +783,6 @@ static int pppol2tp_connect(struct socke
+ session->show = pppol2tp_show;
+ #endif
+
+- /* We need to know each time a skb is dropped from the reorder
+- * queue.
+- */
+- session->ref = pppol2tp_session_sock_hold;
+- session->deref = pppol2tp_session_sock_put;
+-
+ /* If PMTU discovery was enabled, use the MTU that was discovered */
+ dst = sk_dst_get(tunnel->sock);
+ if (dst != NULL) {
+@@ -783,12 +816,17 @@ static int pppol2tp_connect(struct socke
+ po->chan.mtu = session->mtu;
+
+ error = ppp_register_net_channel(sock_net(sk), &po->chan);
+- if (error)
++ if (error) {
++ mutex_unlock(&ps->sk_lock);
+ goto end;
++ }
+
+ out_no_ppp:
+ /* This is how we get the session context from the socket. */
+ sk->sk_user_data = session;
++ rcu_assign_pointer(ps->sk, sk);
++ mutex_unlock(&ps->sk_lock);
++
+ sk->sk_state = PPPOX_CONNECTED;
+ l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n",
+ session->name);
+@@ -834,6 +872,7 @@ static int pppol2tp_session_create(struc
+ }
+
+ ps = l2tp_session_priv(session);
++ mutex_init(&ps->sk_lock);
+ ps->tunnel_sock = tunnel->sock;
+
+ error = l2tp_session_register(session, tunnel);
+@@ -1005,12 +1044,10 @@ static int pppol2tp_session_ioctl(struct
+ "%s: pppol2tp_session_ioctl(cmd=%#x, arg=%#lx)\n",
+ session->name, cmd, arg);
+
+- sk = ps->sock;
++ sk = pppol2tp_session_get_sock(session);
+ if (!sk)
+ return -EBADR;
+
+- sock_hold(sk);
+-
+ switch (cmd) {
+ case SIOCGIFMTU:
+ err = -ENXIO;
+@@ -1286,7 +1323,6 @@ static int pppol2tp_session_setsockopt(s
+ int optname, int val)
+ {
+ int err = 0;
+- struct pppol2tp_session *ps = l2tp_session_priv(session);
+
+ switch (optname) {
+ case PPPOL2TP_SO_RECVSEQ:
+@@ -1307,8 +1343,8 @@ static int pppol2tp_session_setsockopt(s
+ }
+ session->send_seq = val ? -1 : 0;
+ {
+- struct sock *ssk = ps->sock;
+- struct pppox_sock *po = pppox_sk(ssk);
++ struct pppox_sock *po = pppox_sk(sk);
++
+ po->chan.hdrlen = val ? PPPOL2TP_L2TP_HDR_SIZE_SEQ :
+ PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
+ }
+@@ -1644,8 +1680,9 @@ static void pppol2tp_seq_session_show(st
+ {
+ struct l2tp_session *session = v;
+ struct l2tp_tunnel *tunnel = session->tunnel;
+- struct pppol2tp_session *ps = l2tp_session_priv(session);
+- struct pppox_sock *po = pppox_sk(ps->sock);
++ unsigned char state;
++ char user_data_ok;
++ struct sock *sk;
+ u32 ip = 0;
+ u16 port = 0;
+
+@@ -1655,6 +1692,15 @@ static void pppol2tp_seq_session_show(st
+ port = ntohs(inet->inet_sport);
+ }
+
++ sk = pppol2tp_session_get_sock(session);
++ if (sk) {
++ state = sk->sk_state;
++ user_data_ok = (session == sk->sk_user_data) ? 'Y' : 'N';
++ } else {
++ state = 0;
++ user_data_ok = 'N';
++ }
++
+ seq_printf(m, " SESSION '%s' %08X/%d %04X/%04X -> "
+ "%04X/%04X %d %c\n",
+ session->name, ip, port,
+@@ -1662,9 +1708,7 @@ static void pppol2tp_seq_session_show(st
+ session->session_id,
+ tunnel->peer_tunnel_id,
+ session->peer_session_id,
+- ps->sock->sk_state,
+- (session == ps->sock->sk_user_data) ?
+- 'Y' : 'N');
++ state, user_data_ok);
+ seq_printf(m, " %d/%d/%c/%c/%s %08x %u\n",
+ session->mtu, session->mru,
+ session->recv_seq ? 'R' : '-',
+@@ -1681,8 +1725,12 @@ static void pppol2tp_seq_session_show(st
+ atomic_long_read(&session->stats.rx_bytes),
+ atomic_long_read(&session->stats.rx_errors));
+
+- if (po)
++ if (sk) {
++ struct pppox_sock *po = pppox_sk(sk);
++
+ seq_printf(m, " interface %s\n", ppp_dev_name(&po->chan));
++ sock_put(sk);
++ }
+ }
+
+ static int pppol2tp_seq_show(struct seq_file *m, void *v)
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:22 +0100
+Subject: l2tp: remove l2tp_session_find()
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-8-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 55a3ce3b9d98f752df9e2cfb1cba7e715522428a upstream.
+
+This function isn't used anymore.
+
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c | 51 +--------------------------------------------------
+ net/l2tp/l2tp_core.h | 3 ---
+ 2 files changed, 1 insertion(+), 53 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -216,27 +216,6 @@ static void l2tp_tunnel_sock_put(struct
+ sock_put(sk);
+ }
+
+-/* Lookup a session by id in the global session list
+- */
+-static struct l2tp_session *l2tp_session_find_2(struct net *net, u32 session_id)
+-{
+- struct l2tp_net *pn = l2tp_pernet(net);
+- struct hlist_head *session_list =
+- l2tp_session_id_hash_2(pn, session_id);
+- struct l2tp_session *session;
+-
+- rcu_read_lock_bh();
+- hlist_for_each_entry_rcu(session, session_list, global_hlist) {
+- if (session->session_id == session_id) {
+- rcu_read_unlock_bh();
+- return session;
+- }
+- }
+- rcu_read_unlock_bh();
+-
+- return NULL;
+-}
+-
+ /* Session hash list.
+ * The session_id SHOULD be random according to RFC2661, but several
+ * L2TP implementations (Cisco and Microsoft) use incrementing
+@@ -249,35 +228,7 @@ l2tp_session_id_hash(struct l2tp_tunnel
+ return &tunnel->session_hlist[hash_32(session_id, L2TP_HASH_BITS)];
+ }
+
+-/* Lookup a session by id
+- */
+-struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id)
+-{
+- struct hlist_head *session_list;
+- struct l2tp_session *session;
+-
+- /* In L2TPv3, session_ids are unique over all tunnels and we
+- * sometimes need to look them up before we know the
+- * tunnel.
+- */
+- if (tunnel == NULL)
+- return l2tp_session_find_2(net, session_id);
+-
+- session_list = l2tp_session_id_hash(tunnel, session_id);
+- read_lock_bh(&tunnel->hlist_lock);
+- hlist_for_each_entry(session, session_list, hlist) {
+- if (session->session_id == session_id) {
+- read_unlock_bh(&tunnel->hlist_lock);
+- return session;
+- }
+- }
+- read_unlock_bh(&tunnel->hlist_lock);
+-
+- return NULL;
+-}
+-EXPORT_SYMBOL_GPL(l2tp_session_find);
+-
+-/* Like l2tp_session_find() but takes a reference on the returned session.
++/* Lookup a session. A new reference is held on the returned session.
+ * Optionally calls session->ref() too if do_ref is true.
+ */
+ struct l2tp_session *l2tp_session_get(struct net *net,
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -234,9 +234,6 @@ out:
+ struct l2tp_session *l2tp_session_get(struct net *net,
+ struct l2tp_tunnel *tunnel,
+ u32 session_id, bool do_ref);
+-struct l2tp_session *l2tp_session_find(struct net *net,
+- struct l2tp_tunnel *tunnel,
+- u32 session_id);
+ struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
+ bool do_ref);
+ struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:21 +0100
+Subject: l2tp: remove useless duplicate session detection in l2tp_netlink
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, Guillaume Nault <g.nault@alphalink.fr>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-7-gprocida@google.com>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit af87ae465abdc070de0dc35d6c6a9e7a8cd82987 upstream.
+
+There's no point in checking for duplicate sessions at the beginning of
+l2tp_nl_cmd_session_create(); the ->session_create() callbacks already
+return -EEXIST when the session already exists.
+
+Furthermore, even if l2tp_session_find() returns NULL, a new session
+might be created right after the test. So relying on ->session_create()
+to avoid duplicate session is the only sane behaviour.
+
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_netlink.c | 5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -513,11 +513,6 @@ static int l2tp_nl_cmd_session_create(st
+ goto out;
+ }
+ session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
+- session = l2tp_session_find(net, tunnel, session_id);
+- if (session) {
+- ret = -EEXIST;
+- goto out;
+- }
+
+ if (!info->attrs[L2TP_ATTR_PEER_SESSION_ID]) {
+ ret = -EINVAL;
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:17 +0100
+Subject: net: l2tp: deprecate PPPOL2TP_MSG_* in favour of L2TP_MSG_*
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, "Asbjørn Sloth Tønnesen" <asbjorn@asbjorn.st>, "David S . Miller" <davem@davemloft.net>, "Giuliano Procida" <gprocida@google.com>
+Message-ID: <20200521233937.175182-3-gprocida@google.com>
+
+From: Asbjørn Sloth Tønnesen <asbjorn@asbjorn.st>
+
+commit 47c3e7783be4e142b861d34b5c2e223330b05d8a upstream.
+
+PPPOL2TP_MSG_* and L2TP_MSG_* are duplicates, and are being used
+interchangeably in the kernel, so let's standardize on L2TP_MSG_*
+internally, and keep PPPOL2TP_MSG_* defined in UAPI for compatibility.
+
+Signed-off-by: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/networking/l2tp.txt | 8 ++++----
+ include/uapi/linux/if_pppol2tp.h | 13 ++++++-------
+ 2 files changed, 10 insertions(+), 11 deletions(-)
+
+--- a/Documentation/networking/l2tp.txt
++++ b/Documentation/networking/l2tp.txt
+@@ -177,10 +177,10 @@ setsockopt on the PPPoX socket to set a
+
+ The following debug mask bits are available:
+
+-PPPOL2TP_MSG_DEBUG verbose debug (if compiled in)
+-PPPOL2TP_MSG_CONTROL userspace - kernel interface
+-PPPOL2TP_MSG_SEQ sequence numbers handling
+-PPPOL2TP_MSG_DATA data packets
++L2TP_MSG_DEBUG verbose debug (if compiled in)
++L2TP_MSG_CONTROL userspace - kernel interface
++L2TP_MSG_SEQ sequence numbers handling
++L2TP_MSG_DATA data packets
+
+ If enabled, files under a l2tp debugfs directory can be used to dump
+ kernel state about L2TP tunnels and sessions. To access it, the
+--- a/include/uapi/linux/if_pppol2tp.h
++++ b/include/uapi/linux/if_pppol2tp.h
+@@ -18,6 +18,7 @@
+ #include <linux/types.h>
+ #include <linux/in.h>
+ #include <linux/in6.h>
++#include <linux/l2tp.h>
+
+ /* Structure used to connect() the socket to a particular tunnel UDP
+ * socket over IPv4.
+@@ -90,14 +91,12 @@ enum {
+ PPPOL2TP_SO_REORDERTO = 5,
+ };
+
+-/* Debug message categories for the DEBUG socket option */
++/* Debug message categories for the DEBUG socket option (deprecated) */
+ enum {
+- PPPOL2TP_MSG_DEBUG = (1 << 0), /* verbose debug (if
+- * compiled in) */
+- PPPOL2TP_MSG_CONTROL = (1 << 1), /* userspace - kernel
+- * interface */
+- PPPOL2TP_MSG_SEQ = (1 << 2), /* sequence numbers */
+- PPPOL2TP_MSG_DATA = (1 << 3), /* data packets */
++ PPPOL2TP_MSG_DEBUG = L2TP_MSG_DEBUG,
++ PPPOL2TP_MSG_CONTROL = L2TP_MSG_CONTROL,
++ PPPOL2TP_MSG_SEQ = L2TP_MSG_SEQ,
++ PPPOL2TP_MSG_DATA = L2TP_MSG_DATA,
+ };
+
+
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:16 +0100
+Subject: net: l2tp: export debug flags to UAPI
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, "Asbjørn Sloth Tønnesen" <asbjorn@asbjorn.st>, "David S . Miller" <davem@davemloft.net>, "Giuliano Procida" <gprocida@google.com>
+Message-ID: <20200521233937.175182-2-gprocida@google.com>
+
+From: Asbjørn Sloth Tønnesen <asbjorn@asbjorn.st>
+
+commit 41c43fbee68f4f9a2a9675d83bca91c77862d7f0 upstream.
+
+Move the L2TP_MSG_* definitions to UAPI, as it is part of
+the netlink API.
+
+Signed-off-by: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/uapi/linux/l2tp.h | 17 ++++++++++++++++-
+ net/l2tp/l2tp_core.h | 10 ----------
+ 2 files changed, 16 insertions(+), 11 deletions(-)
+
+--- a/include/uapi/linux/l2tp.h
++++ b/include/uapi/linux/l2tp.h
+@@ -108,7 +108,7 @@ enum {
+ L2TP_ATTR_VLAN_ID, /* u16 */
+ L2TP_ATTR_COOKIE, /* 0, 4 or 8 bytes */
+ L2TP_ATTR_PEER_COOKIE, /* 0, 4 or 8 bytes */
+- L2TP_ATTR_DEBUG, /* u32 */
++ L2TP_ATTR_DEBUG, /* u32, enum l2tp_debug_flags */
+ L2TP_ATTR_RECV_SEQ, /* u8 */
+ L2TP_ATTR_SEND_SEQ, /* u8 */
+ L2TP_ATTR_LNS_MODE, /* u8 */
+@@ -175,6 +175,21 @@ enum l2tp_seqmode {
+ L2TP_SEQ_ALL = 2,
+ };
+
++/**
++ * enum l2tp_debug_flags - debug message categories for L2TP tunnels/sessions
++ *
++ * @L2TP_MSG_DEBUG: verbose debug (if compiled in)
++ * @L2TP_MSG_CONTROL: userspace - kernel interface
++ * @L2TP_MSG_SEQ: sequence numbers
++ * @L2TP_MSG_DATA: data packets
++ */
++enum l2tp_debug_flags {
++ L2TP_MSG_DEBUG = (1 << 0),
++ L2TP_MSG_CONTROL = (1 << 1),
++ L2TP_MSG_SEQ = (1 << 2),
++ L2TP_MSG_DATA = (1 << 3),
++};
++
+ /*
+ * NETLINK_GENERIC related info
+ */
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -23,16 +23,6 @@
+ #define L2TP_HASH_BITS_2 8
+ #define L2TP_HASH_SIZE_2 (1 << L2TP_HASH_BITS_2)
+
+-/* Debug message categories for the DEBUG socket option */
+-enum {
+- L2TP_MSG_DEBUG = (1 << 0), /* verbose debug (if
+- * compiled in) */
+- L2TP_MSG_CONTROL = (1 << 1), /* userspace - kernel
+- * interface */
+- L2TP_MSG_SEQ = (1 << 2), /* sequence numbers */
+- L2TP_MSG_DATA = (1 << 3), /* data packets */
+-};
+-
+ struct sk_buff;
+
+ struct l2tp_stats {
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:18 +0100
+Subject: net: l2tp: ppp: change PPPOL2TP_MSG_* => L2TP_MSG_*
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, "Asbjørn Sloth Tønnesen" <asbjorn@asbjorn.st>, "David S . Miller" <davem@davemloft.net>, "Giuliano Procida" <gprocida@google.com>
+Message-ID: <20200521233937.175182-4-gprocida@google.com>
+
+From: Asbjørn Sloth Tønnesen <asbjorn@asbjorn.st>
+
+commit fba40c632c6473fa89660e870a6042c0fe733f8c upstream.
+
+Signed-off-by: Asbjoern Sloth Toennesen <asbjorn@asbjorn.st>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_ppp.c | 54 ++++++++++++++++++++++++++--------------------------
+ 1 file changed, 27 insertions(+), 27 deletions(-)
+
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -231,14 +231,14 @@ static void pppol2tp_recv(struct l2tp_se
+ if (sk->sk_state & PPPOX_BOUND) {
+ struct pppox_sock *po;
+
+- l2tp_dbg(session, PPPOL2TP_MSG_DATA,
++ l2tp_dbg(session, L2TP_MSG_DATA,
+ "%s: recv %d byte data frame, passing to ppp\n",
+ session->name, data_len);
+
+ po = pppox_sk(sk);
+ ppp_input(&po->chan, skb);
+ } else {
+- l2tp_dbg(session, PPPOL2TP_MSG_DATA,
++ l2tp_dbg(session, L2TP_MSG_DATA,
+ "%s: recv %d byte data frame, passing to L2TP socket\n",
+ session->name, data_len);
+
+@@ -251,7 +251,7 @@ static void pppol2tp_recv(struct l2tp_se
+ return;
+
+ no_sock:
+- l2tp_info(session, PPPOL2TP_MSG_DATA, "%s: no socket\n", session->name);
++ l2tp_info(session, L2TP_MSG_DATA, "%s: no socket\n", session->name);
+ kfree_skb(skb);
+ }
+
+@@ -782,7 +782,7 @@ out_no_ppp:
+ /* This is how we get the session context from the socket. */
+ sk->sk_user_data = session;
+ sk->sk_state = PPPOX_CONNECTED;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: created\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n",
+ session->name);
+
+ end:
+@@ -833,7 +833,7 @@ static int pppol2tp_session_create(struc
+ ps = l2tp_session_priv(session);
+ ps->tunnel_sock = tunnel->sock;
+
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: created\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n",
+ session->name);
+
+ error = 0;
+@@ -995,7 +995,7 @@ static int pppol2tp_session_ioctl(struct
+ struct l2tp_tunnel *tunnel = session->tunnel;
+ struct pppol2tp_ioc_stats stats;
+
+- l2tp_dbg(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_dbg(session, L2TP_MSG_CONTROL,
+ "%s: pppol2tp_session_ioctl(cmd=%#x, arg=%#lx)\n",
+ session->name, cmd, arg);
+
+@@ -1018,7 +1018,7 @@ static int pppol2tp_session_ioctl(struct
+ if (copy_to_user((void __user *) arg, &ifr, sizeof(struct ifreq)))
+ break;
+
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get mtu=%d\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: get mtu=%d\n",
+ session->name, session->mtu);
+ err = 0;
+ break;
+@@ -1034,7 +1034,7 @@ static int pppol2tp_session_ioctl(struct
+
+ session->mtu = ifr.ifr_mtu;
+
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set mtu=%d\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: set mtu=%d\n",
+ session->name, session->mtu);
+ err = 0;
+ break;
+@@ -1048,7 +1048,7 @@ static int pppol2tp_session_ioctl(struct
+ if (put_user(session->mru, (int __user *) arg))
+ break;
+
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get mru=%d\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: get mru=%d\n",
+ session->name, session->mru);
+ err = 0;
+ break;
+@@ -1063,7 +1063,7 @@ static int pppol2tp_session_ioctl(struct
+ break;
+
+ session->mru = val;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set mru=%d\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: set mru=%d\n",
+ session->name, session->mru);
+ err = 0;
+ break;
+@@ -1073,7 +1073,7 @@ static int pppol2tp_session_ioctl(struct
+ if (put_user(ps->flags, (int __user *) arg))
+ break;
+
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get flags=%d\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: get flags=%d\n",
+ session->name, ps->flags);
+ err = 0;
+ break;
+@@ -1083,7 +1083,7 @@ static int pppol2tp_session_ioctl(struct
+ if (get_user(val, (int __user *) arg))
+ break;
+ ps->flags = val;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set flags=%d\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: set flags=%d\n",
+ session->name, ps->flags);
+ err = 0;
+ break;
+@@ -1100,7 +1100,7 @@ static int pppol2tp_session_ioctl(struct
+ if (copy_to_user((void __user *) arg, &stats,
+ sizeof(stats)))
+ break;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get L2TP stats\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: get L2TP stats\n",
+ session->name);
+ err = 0;
+ break;
+@@ -1128,7 +1128,7 @@ static int pppol2tp_tunnel_ioctl(struct
+ struct sock *sk;
+ struct pppol2tp_ioc_stats stats;
+
+- l2tp_dbg(tunnel, PPPOL2TP_MSG_CONTROL,
++ l2tp_dbg(tunnel, L2TP_MSG_CONTROL,
+ "%s: pppol2tp_tunnel_ioctl(cmd=%#x, arg=%#lx)\n",
+ tunnel->name, cmd, arg);
+
+@@ -1171,7 +1171,7 @@ static int pppol2tp_tunnel_ioctl(struct
+ err = -EFAULT;
+ break;
+ }
+- l2tp_info(tunnel, PPPOL2TP_MSG_CONTROL, "%s: get L2TP stats\n",
++ l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: get L2TP stats\n",
+ tunnel->name);
+ err = 0;
+ break;
+@@ -1261,7 +1261,7 @@ static int pppol2tp_tunnel_setsockopt(st
+ switch (optname) {
+ case PPPOL2TP_SO_DEBUG:
+ tunnel->debug = val;
+- l2tp_info(tunnel, PPPOL2TP_MSG_CONTROL, "%s: set debug=%x\n",
++ l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: set debug=%x\n",
+ tunnel->name, tunnel->debug);
+ break;
+
+@@ -1289,7 +1289,7 @@ static int pppol2tp_session_setsockopt(s
+ break;
+ }
+ session->recv_seq = val ? -1 : 0;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_info(session, L2TP_MSG_CONTROL,
+ "%s: set recv_seq=%d\n",
+ session->name, session->recv_seq);
+ break;
+@@ -1307,7 +1307,7 @@ static int pppol2tp_session_setsockopt(s
+ PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
+ }
+ l2tp_session_set_header_len(session, session->tunnel->version);
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_info(session, L2TP_MSG_CONTROL,
+ "%s: set send_seq=%d\n",
+ session->name, session->send_seq);
+ break;
+@@ -1318,20 +1318,20 @@ static int pppol2tp_session_setsockopt(s
+ break;
+ }
+ session->lns_mode = val ? -1 : 0;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_info(session, L2TP_MSG_CONTROL,
+ "%s: set lns_mode=%d\n",
+ session->name, session->lns_mode);
+ break;
+
+ case PPPOL2TP_SO_DEBUG:
+ session->debug = val;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: set debug=%x\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: set debug=%x\n",
+ session->name, session->debug);
+ break;
+
+ case PPPOL2TP_SO_REORDERTO:
+ session->reorder_timeout = msecs_to_jiffies(val);
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_info(session, L2TP_MSG_CONTROL,
+ "%s: set reorder_timeout=%d\n",
+ session->name, session->reorder_timeout);
+ break;
+@@ -1412,7 +1412,7 @@ static int pppol2tp_tunnel_getsockopt(st
+ switch (optname) {
+ case PPPOL2TP_SO_DEBUG:
+ *val = tunnel->debug;
+- l2tp_info(tunnel, PPPOL2TP_MSG_CONTROL, "%s: get debug=%x\n",
++ l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: get debug=%x\n",
+ tunnel->name, tunnel->debug);
+ break;
+
+@@ -1435,31 +1435,31 @@ static int pppol2tp_session_getsockopt(s
+ switch (optname) {
+ case PPPOL2TP_SO_RECVSEQ:
+ *val = session->recv_seq;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_info(session, L2TP_MSG_CONTROL,
+ "%s: get recv_seq=%d\n", session->name, *val);
+ break;
+
+ case PPPOL2TP_SO_SENDSEQ:
+ *val = session->send_seq;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_info(session, L2TP_MSG_CONTROL,
+ "%s: get send_seq=%d\n", session->name, *val);
+ break;
+
+ case PPPOL2TP_SO_LNSMODE:
+ *val = session->lns_mode;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_info(session, L2TP_MSG_CONTROL,
+ "%s: get lns_mode=%d\n", session->name, *val);
+ break;
+
+ case PPPOL2TP_SO_DEBUG:
+ *val = session->debug;
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL, "%s: get debug=%d\n",
++ l2tp_info(session, L2TP_MSG_CONTROL, "%s: get debug=%d\n",
+ session->name, *val);
+ break;
+
+ case PPPOL2TP_SO_REORDERTO:
+ *val = (int) jiffies_to_msecs(session->reorder_timeout);
+- l2tp_info(session, PPPOL2TP_MSG_CONTROL,
++ l2tp_info(session, L2TP_MSG_CONTROL,
+ "%s: get reorder_timeout=%d\n", session->name, *val);
+ break;
+
--- /dev/null
+From foo@baz Fri 22 May 2020 02:11:00 PM CEST
+From: Giuliano Procida <gprocida@google.com>
+Date: Fri, 22 May 2020 00:39:19 +0100
+Subject: New kernel function to get IP overhead on a socket.
+To: greg@kroah.com
+Cc: stable@vger.kernel.org, "R. Parameswaran" <parameswaran.r7@gmail.com>, "R . Parameswaran" <rparames@brocade.com>, "David S . Miller" <davem@davemloft.net>, Giuliano Procida <gprocida@google.com>
+Message-ID: <20200521233937.175182-5-gprocida@google.com>
+
+From: "R. Parameswaran" <parameswaran.r7@gmail.com>
+
+commit 113c3075931a334f899008f6c753abe70a3a9323 upstream.
+
+A new function, kernel_sock_ip_overhead(), is provided
+to calculate the cumulative overhead imposed by the IP
+Header and IP options, if any, on a socket's payload.
+The new function returns an overhead of zero for sockets
+that do not belong to the IPv4 or IPv6 address families.
+This is used in the L2TP code path to compute the
+total outer IP overhead on the L2TP tunnel socket when
+calculating the default MTU for Ethernet pseudowires.
+
+Signed-off-by: R. Parameswaran <rparames@brocade.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/net.h | 3 +++
+ net/socket.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 49 insertions(+)
+
+--- a/include/linux/net.h
++++ b/include/linux/net.h
+@@ -298,6 +298,9 @@ int kernel_sendpage(struct socket *sock,
+ int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg);
+ int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how);
+
++/* Following routine returns the IP overhead imposed by a socket. */
++u32 kernel_sock_ip_overhead(struct sock *sk);
++
+ #define MODULE_ALIAS_NETPROTO(proto) \
+ MODULE_ALIAS("net-pf-" __stringify(proto))
+
+--- a/net/socket.c
++++ b/net/socket.c
+@@ -3321,3 +3321,49 @@ int kernel_sock_shutdown(struct socket *
+ return sock->ops->shutdown(sock, how);
+ }
+ EXPORT_SYMBOL(kernel_sock_shutdown);
++
++/* This routine returns the IP overhead imposed by a socket i.e.
++ * the length of the underlying IP header, depending on whether
++ * this is an IPv4 or IPv6 socket and the length from IP options turned
++ * on at the socket.
++ */
++u32 kernel_sock_ip_overhead(struct sock *sk)
++{
++ struct inet_sock *inet;
++ struct ip_options_rcu *opt;
++ u32 overhead = 0;
++ bool owned_by_user;
++#if IS_ENABLED(CONFIG_IPV6)
++ struct ipv6_pinfo *np;
++ struct ipv6_txoptions *optv6 = NULL;
++#endif /* IS_ENABLED(CONFIG_IPV6) */
++
++ if (!sk)
++ return overhead;
++
++ owned_by_user = sock_owned_by_user(sk);
++ switch (sk->sk_family) {
++ case AF_INET:
++ inet = inet_sk(sk);
++ overhead += sizeof(struct iphdr);
++ opt = rcu_dereference_protected(inet->inet_opt,
++ owned_by_user);
++ if (opt)
++ overhead += opt->opt.optlen;
++ return overhead;
++#if IS_ENABLED(CONFIG_IPV6)
++ case AF_INET6:
++ np = inet6_sk(sk);
++ overhead += sizeof(struct ipv6hdr);
++ if (np)
++ optv6 = rcu_dereference_protected(np->opt,
++ owned_by_user);
++ if (optv6)
++ overhead += (optv6->opt_flen + optv6->opt_nflen);
++ return overhead;
++#endif /* IS_ENABLED(CONFIG_IPV6) */
++ default: /* Returns 0 overhead if the socket is not ipv4 or ipv6 */
++ return overhead;
++ }
++}
++EXPORT_SYMBOL(kernel_sock_ip_overhead);
padata-purge-get_cpu-and-reorder_via_wq-from-padata_.patch
arm64-fix-the-flush_icache_range-arguments-in-machin.patch
watchdog-fix-the-race-between-the-release-of-watchdog_core_data-and-cdev.patch
+net-l2tp-export-debug-flags-to-uapi.patch
+net-l2tp-deprecate-pppol2tp_msg_-in-favour-of-l2tp_msg_.patch
+net-l2tp-ppp-change-pppol2tp_msg_-l2tp_msg_.patch
+new-kernel-function-to-get-ip-overhead-on-a-socket.patch
+l2tp-adjust-intf-mtu-add-underlay-l3-l2-hdrs.patch
+l2tp-remove-useless-duplicate-session-detection-in-l2tp_netlink.patch
+l2tp-remove-l2tp_session_find.patch
+l2tp-define-parameters-of-l2tp_session_get-as-const.patch
+l2tp-define-parameters-of-l2tp_tunnel_find-as-const.patch
+l2tp-initialise-session-s-refcount-before-making-it-reachable.patch
+l2tp-hold-tunnel-while-looking-up-sessions-in-l2tp_netlink.patch
+l2tp-hold-tunnel-while-processing-genl-delete-command.patch
+l2tp-hold-tunnel-while-handling-genl-tunnel-updates.patch
+l2tp-hold-tunnel-while-handling-genl-tunnel_get-commands.patch
+l2tp-hold-tunnel-used-while-creating-sessions-with-netlink.patch
+l2tp-prevent-creation-of-sessions-on-terminated-tunnels.patch
+l2tp-pass-tunnel-pointer-to-session_create.patch
+l2tp-fix-l2tp_eth-module-loading.patch
+l2tp-don-t-register-sessions-in-l2tp_session_create.patch
+l2tp-initialise-l2tp_eth-sessions-before-registering-them.patch
+l2tp-protect-sock-pointer-of-struct-pppol2tp_session-with-rcu.patch
+l2tp-initialise-ppp-sessions-before-registering-them.patch