summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/l2tp/l2tp_core.c21
1 files changed, 21 insertions, 0 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 8d21ff25f160..2ab45e3f48bf 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -794,6 +794,7 @@ static void l2tp_session_queue_purge(struct l2tp_session *session)
static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
{
struct l2tp_session *session = NULL;
+ struct l2tp_tunnel *orig_tunnel = tunnel;
unsigned char *ptr, *optr;
u16 hdrflags;
u32 tunnel_id, session_id;
@@ -845,6 +846,20 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
/* Extract tunnel and session ID */
tunnel_id = ntohs(*(__be16 *)ptr);
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 || alt_tunnel->version != L2TP_HDR_VER_2)
+ goto pass;
+ tunnel = alt_tunnel;
+ }
+
session_id = ntohs(*(__be16 *)ptr);
ptr += 2;
} else {
@@ -875,6 +890,9 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb)
l2tp_recv_common(session, skb, ptr, optr, hdrflags, length);
l2tp_session_dec_refcount(session);
+ if (tunnel != orig_tunnel)
+ l2tp_tunnel_dec_refcount(tunnel);
+
return 0;
invalid:
@@ -884,6 +902,9 @@ pass:
/* Put UDP header back */
__skb_push(skb, sizeof(struct udphdr));
+ if (tunnel != orig_tunnel)
+ l2tp_tunnel_dec_refcount(tunnel);
+
return 1;
}