l2tp: refactor udp recv to lookup to not use sk_user_data

Modify UDP decap to not use the tunnel pointer which comes from the
sock's sk_user_data when parsing the L2TP header. By looking up the
destination session using only the packet contents we avoid potential
UDP 5-tuple aliasing issues which arise from depending on the socket
that received the packet.

Drop the useless error messages on short packet or on failing to find
a session since the tunnel pointer might point to a different tunnel
if multiple sockets use the same 5-tuple.

Short packets (those not big enough to contain an L2TP header) are no
longer counted in the tunnel's invalid counter because we can't derive
the tunnel until we parse the l2tp header to lookup the session.

l2tp_udp_encap_recv was a small wrapper around l2tp_udp_recv_core which
used sk_user_data to derive a tunnel pointer in an RCU-safe way. But
we no longer need the tunnel pointer, so remove that code and combine
the two functions.

Signed-off-by: James Chapman <jchapman@katalix.com>
Reviewed-by: Tom Parkin <tparkin@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
James Chapman 2024-06-20 12:22:40 +01:00 committed by David S. Miller
parent 2a3339f6c9
commit ff6a2ac23c

View file

@ -926,19 +926,14 @@ static void l2tp_session_queue_purge(struct l2tp_session *session)
} }
} }
/* Internal UDP receive frame. Do the real work of receiving an L2TP data frame /* UDP encapsulation receive handler. See net/ipv4/udp.c for details. */
* here. The skb is not on a list when we get here. int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
* Returns 0 if the packet was a data packet and was successfully passed on.
* Returns 1 if the packet was not a good data packet and could not be
* forwarded. All such packets are passed up to userspace to deal with.
*/
static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
{ {
struct l2tp_session *session = NULL; struct l2tp_session *session = NULL;
struct l2tp_tunnel *orig_tunnel = tunnel; struct l2tp_tunnel *tunnel = NULL;
struct net *net = sock_net(sk);
unsigned char *ptr, *optr; unsigned char *ptr, *optr;
u16 hdrflags; u16 hdrflags;
u32 tunnel_id, session_id;
u16 version; u16 version;
int length; int length;
@ -948,11 +943,8 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
__skb_pull(skb, sizeof(struct udphdr)); __skb_pull(skb, sizeof(struct udphdr));
/* Short packet? */ /* Short packet? */
if (!pskb_may_pull(skb, L2TP_HDR_SIZE_MAX)) { if (!pskb_may_pull(skb, L2TP_HDR_SIZE_MAX))
pr_debug_ratelimited("%s: recv short packet (len=%d)\n", goto pass;
tunnel->name, skb->len);
goto invalid;
}
/* Point to L2TP header */ /* Point to L2TP header */
optr = skb->data; optr = skb->data;
@ -975,6 +967,8 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
ptr += 2; ptr += 2;
if (version == L2TP_HDR_VER_2) { if (version == L2TP_HDR_VER_2) {
u16 tunnel_id, session_id;
/* If length is present, skip it */ /* If length is present, skip it */
if (hdrflags & L2TP_HDRFLAG_L) if (hdrflags & L2TP_HDRFLAG_L)
ptr += 2; ptr += 2;
@ -982,49 +976,35 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
/* Extract tunnel and session ID */ /* Extract tunnel and session ID */
tunnel_id = ntohs(*(__be16 *)ptr); tunnel_id = ntohs(*(__be16 *)ptr);
ptr += 2; ptr += 2;
if (tunnel_id != tunnel->tunnel_id) {
/* We are receiving trafic for another tunnel, probably
* because we have several tunnels between the same
* IP/port quadruple, look it up.
*/
struct l2tp_tunnel *alt_tunnel;
alt_tunnel = l2tp_tunnel_get(tunnel->l2tp_net, tunnel_id);
if (!alt_tunnel)
goto pass;
tunnel = alt_tunnel;
}
session_id = ntohs(*(__be16 *)ptr); session_id = ntohs(*(__be16 *)ptr);
ptr += 2; ptr += 2;
session = l2tp_v2_session_get(net, tunnel_id, session_id);
} else { } else {
u32 session_id;
ptr += 2; /* skip reserved bits */ ptr += 2; /* skip reserved bits */
tunnel_id = tunnel->tunnel_id;
session_id = ntohl(*(__be32 *)ptr); session_id = ntohl(*(__be32 *)ptr);
ptr += 4; ptr += 4;
session = l2tp_v3_session_get(net, sk, session_id);
} }
/* Check protocol version */
if (version != tunnel->version) {
pr_debug_ratelimited("%s: recv protocol version mismatch: got %d expected %d\n",
tunnel->name, version, tunnel->version);
goto invalid;
}
/* Find the session context */
session = l2tp_tunnel_get_session(tunnel, session_id);
if (!session || !session->recv_skb) { if (!session || !session->recv_skb) {
if (session) if (session)
l2tp_session_dec_refcount(session); l2tp_session_dec_refcount(session);
/* Not found? Pass to userspace to deal with */ /* Not found? Pass to userspace to deal with */
pr_debug_ratelimited("%s: no session found (%u/%u). Passing up.\n",
tunnel->name, tunnel_id, session_id);
goto pass; goto pass;
} }
if (tunnel->version == L2TP_HDR_VER_3 && tunnel = session->tunnel;
/* Check protocol version */
if (version != tunnel->version)
goto invalid;
if (version == L2TP_HDR_VER_3 &&
l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) { l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) {
l2tp_session_dec_refcount(session); l2tp_session_dec_refcount(session);
goto invalid; goto invalid;
@ -1033,9 +1013,6 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
l2tp_recv_common(session, skb, ptr, optr, hdrflags, length); l2tp_recv_common(session, skb, ptr, optr, hdrflags, length);
l2tp_session_dec_refcount(session); l2tp_session_dec_refcount(session);
if (tunnel != orig_tunnel)
l2tp_tunnel_dec_refcount(tunnel);
return 0; return 0;
invalid: invalid:
@ -1045,42 +1022,11 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
/* Put UDP header back */ /* Put UDP header back */
__skb_push(skb, sizeof(struct udphdr)); __skb_push(skb, sizeof(struct udphdr));
if (tunnel != orig_tunnel)
l2tp_tunnel_dec_refcount(tunnel);
return 1;
}
/* UDP encapsulation receive and error receive handlers.
* See net/ipv4/udp.c for details.
*
* Note that these functions are called from inside an
* RCU-protected region, but without the socket being locked.
*
* Hence we use rcu_dereference_sk_user_data to access the
* tunnel data structure rather the usual l2tp_sk_to_tunnel
* accessor function.
*/
int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
struct l2tp_tunnel *tunnel;
tunnel = rcu_dereference_sk_user_data(sk);
if (!tunnel)
goto pass_up;
if (WARN_ON(tunnel->magic != L2TP_TUNNEL_MAGIC))
goto pass_up;
if (l2tp_udp_recv_core(tunnel, skb))
goto pass_up;
return 0;
pass_up:
return 1; return 1;
} }
EXPORT_SYMBOL_GPL(l2tp_udp_encap_recv); EXPORT_SYMBOL_GPL(l2tp_udp_encap_recv);
/* UDP encapsulation receive error handler. See net/ipv4/udp.c for details. */
static void l2tp_udp_encap_err_recv(struct sock *sk, struct sk_buff *skb, int err, static void l2tp_udp_encap_err_recv(struct sock *sk, struct sk_buff *skb, int err,
__be16 port, u32 info, u8 *payload) __be16 port, u32 info, u8 *payload)
{ {