]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
l2tp: lookup tunnel from socket without using sk_user_data
authorJames Chapman <jchapman@katalix.com>
Mon, 29 Jul 2024 15:38:00 +0000 (16:38 +0100)
committerDavid S. Miller <davem@davemloft.net>
Wed, 31 Jul 2024 08:25:12 +0000 (09:25 +0100)
l2tp_sk_to_tunnel derives the tunnel from sk_user_data. Instead,
lookup the tunnel by walking the tunnel IDR for a tunnel using the
indicated sock. This is slow but l2tp_sk_to_tunnel is not used in
the datapath so performance isn't critical.

l2tp_tunnel_destruct needs a variant of l2tp_sk_to_tunnel which does
not bump the tunnel refcount since the tunnel refcount is already 0.

Change l2tp_sk_to_tunnel sk arg to const since it does not modify sk.

Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: Tom Parkin <tparkin@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_ip.c
net/l2tp/l2tp_ip6.c

index c80ab3f26084fb77b37f321d3bade521be66848b..c97cd0fd851482004cba7c91e7c82127a84110ba 100644 (file)
@@ -150,15 +150,43 @@ static void l2tp_session_free(struct l2tp_session *session)
        kfree(session);
 }
 
-struct l2tp_tunnel *l2tp_sk_to_tunnel(struct sock *sk)
+static struct l2tp_tunnel *__l2tp_sk_to_tunnel(const struct sock *sk)
 {
-       struct l2tp_tunnel *tunnel = sk->sk_user_data;
+       const struct net *net = sock_net(sk);
+       unsigned long tunnel_id, tmp;
+       struct l2tp_tunnel *tunnel;
+       struct l2tp_net *pn;
+
+       WARN_ON_ONCE(!rcu_read_lock_bh_held());
+       pn = l2tp_pernet(net);
+       idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
+               if (tunnel && tunnel->sock == sk)
+                       return tunnel;
+       }
+
+       return NULL;
+}
 
-       if (tunnel)
-               if (WARN_ON(tunnel->magic != L2TP_TUNNEL_MAGIC))
-                       return NULL;
+struct l2tp_tunnel *l2tp_sk_to_tunnel(const struct sock *sk)
+{
+       const struct net *net = sock_net(sk);
+       unsigned long tunnel_id, tmp;
+       struct l2tp_tunnel *tunnel;
+       struct l2tp_net *pn;
+
+       rcu_read_lock_bh();
+       pn = l2tp_pernet(net);
+       idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
+               if (tunnel &&
+                   tunnel->sock == sk &&
+                   refcount_inc_not_zero(&tunnel->ref_count)) {
+                       rcu_read_unlock_bh();
+                       return tunnel;
+               }
+       }
+       rcu_read_unlock_bh();
 
-       return tunnel;
+       return NULL;
 }
 EXPORT_SYMBOL_GPL(l2tp_sk_to_tunnel);
 
@@ -1213,8 +1241,10 @@ EXPORT_SYMBOL_GPL(l2tp_xmit_skb);
  */
 static void l2tp_tunnel_destruct(struct sock *sk)
 {
-       struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk);
+       struct l2tp_tunnel *tunnel;
 
+       rcu_read_lock_bh();
+       tunnel = __l2tp_sk_to_tunnel(sk);
        if (!tunnel)
                goto end;
 
@@ -1242,6 +1272,7 @@ static void l2tp_tunnel_destruct(struct sock *sk)
 
        kfree_rcu(tunnel, rcu);
 end:
+       rcu_read_unlock_bh();
        return;
 }
 
@@ -1308,10 +1339,13 @@ static void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel)
 /* Tunnel socket destroy hook for UDP encapsulation */
 static void l2tp_udp_encap_destroy(struct sock *sk)
 {
-       struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk);
+       struct l2tp_tunnel *tunnel;
 
-       if (tunnel)
+       tunnel = l2tp_sk_to_tunnel(sk);
+       if (tunnel) {
                l2tp_tunnel_delete(tunnel);
+               l2tp_tunnel_dec_refcount(tunnel);
+       }
 }
 
 static void l2tp_tunnel_remove(struct net *net, struct l2tp_tunnel *tunnel)
index 8ac81bc1bc6fa33d3cbb43da70c74c4c6d373de8..a41cf6795df0c3e0ede80ce16b08a6e31f72fa2f 100644 (file)
@@ -273,10 +273,7 @@ void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type);
 /* IOCTL helper for IP encap modules. */
 int l2tp_ioctl(struct sock *sk, int cmd, int *karg);
 
-/* Extract the tunnel structure from a socket's sk_user_data pointer,
- * validating the tunnel magic feather.
- */
-struct l2tp_tunnel *l2tp_sk_to_tunnel(struct sock *sk);
+struct l2tp_tunnel *l2tp_sk_to_tunnel(const struct sock *sk);
 
 static inline int l2tp_get_l2specific_len(struct l2tp_session *session)
 {
index e48aa177d74c456ca9e55cd1d7efd7f311677a84..78243f993cda0fb39e95ecab31de6e059e6d3013 100644 (file)
@@ -235,14 +235,17 @@ static void l2tp_ip_close(struct sock *sk, long timeout)
 
 static void l2tp_ip_destroy_sock(struct sock *sk)
 {
-       struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk);
+       struct l2tp_tunnel *tunnel;
        struct sk_buff *skb;
 
        while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL)
                kfree_skb(skb);
 
-       if (tunnel)
+       tunnel = l2tp_sk_to_tunnel(sk);
+       if (tunnel) {
                l2tp_tunnel_delete(tunnel);
+               l2tp_tunnel_dec_refcount(tunnel);
+       }
 }
 
 static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
index d217ff1f229e4837921d4f14df3cf9eb567aa0a0..3b0465f2d60d266711e967b574f4c10d61fcf808 100644 (file)
@@ -246,14 +246,17 @@ static void l2tp_ip6_close(struct sock *sk, long timeout)
 
 static void l2tp_ip6_destroy_sock(struct sock *sk)
 {
-       struct l2tp_tunnel *tunnel = l2tp_sk_to_tunnel(sk);
+       struct l2tp_tunnel *tunnel;
 
        lock_sock(sk);
        ip6_flush_pending_frames(sk);
        release_sock(sk);
 
-       if (tunnel)
+       tunnel = l2tp_sk_to_tunnel(sk);
+       if (tunnel) {
                l2tp_tunnel_delete(tunnel);
+               l2tp_tunnel_dec_refcount(tunnel);
+       }
 }
 
 static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)