Commit 7a70e39b authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

[PPP] L2TP: Fix skb handling in pppol2tp_recv_core

The function pppol2tp_recv_core doesn't handle non-linear packets properly.
It also fails to check the remote offset field.

This patch fixes these problems.  It also removes an unnecessary check on
the UDP header which has already been performed by the UDP layer.
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent a14d6abc
...@@ -491,44 +491,46 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) ...@@ -491,44 +491,46 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
u16 hdrflags; u16 hdrflags;
u16 tunnel_id, session_id; u16 tunnel_id, session_id;
int length; int length;
struct udphdr *uh; int offset;
tunnel = pppol2tp_sock_to_tunnel(sock); tunnel = pppol2tp_sock_to_tunnel(sock);
if (tunnel == NULL) if (tunnel == NULL)
goto error; goto error;
/* UDP always verifies the packet length. */
__skb_pull(skb, sizeof(struct udphdr));
/* Short packet? */ /* Short packet? */
if (skb->len < sizeof(struct udphdr)) { if (!pskb_may_pull(skb, 12)) {
PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO, PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO,
"%s: recv short packet (len=%d)\n", tunnel->name, skb->len); "%s: recv short packet (len=%d)\n", tunnel->name, skb->len);
goto error; goto error;
} }
/* Point to L2TP header */ /* Point to L2TP header */
ptr = skb->data + sizeof(struct udphdr); ptr = skb->data;
/* Get L2TP header flags */ /* Get L2TP header flags */
hdrflags = ntohs(*(__be16*)ptr); hdrflags = ntohs(*(__be16*)ptr);
/* Trace packet contents, if enabled */ /* Trace packet contents, if enabled */
if (tunnel->debug & PPPOL2TP_MSG_DATA) { if (tunnel->debug & PPPOL2TP_MSG_DATA) {
length = min(16u, skb->len);
if (!pskb_may_pull(skb, length))
goto error;
printk(KERN_DEBUG "%s: recv: ", tunnel->name); printk(KERN_DEBUG "%s: recv: ", tunnel->name);
for (length = 0; length < 16; length++) offset = 0;
printk(" %02X", ptr[length]); do {
printk(" %02X", ptr[offset]);
} while (++offset < length);
printk("\n"); printk("\n");
} }
/* Get length of L2TP packet */ /* Get length of L2TP packet */
uh = (struct udphdr *) skb_transport_header(skb); length = skb->len;
length = ntohs(uh->len) - sizeof(struct udphdr);
/* Too short? */
if (length < 12) {
PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO,
"%s: recv short L2TP packet (len=%d)\n", tunnel->name, length);
goto error;
}
/* If type is control packet, it is handled by userspace. */ /* If type is control packet, it is handled by userspace. */
if (hdrflags & L2TP_HDRFLAG_T) { if (hdrflags & L2TP_HDRFLAG_T) {
...@@ -606,7 +608,6 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) ...@@ -606,7 +608,6 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
"%s: recv data has no seq numbers when required. " "%s: recv data has no seq numbers when required. "
"Discarding\n", session->name); "Discarding\n", session->name);
session->stats.rx_seq_discards++; session->stats.rx_seq_discards++;
session->stats.rx_errors++;
goto discard; goto discard;
} }
...@@ -625,7 +626,6 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) ...@@ -625,7 +626,6 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
"%s: recv data has no seq numbers when required. " "%s: recv data has no seq numbers when required. "
"Discarding\n", session->name); "Discarding\n", session->name);
session->stats.rx_seq_discards++; session->stats.rx_seq_discards++;
session->stats.rx_errors++;
goto discard; goto discard;
} }
...@@ -634,10 +634,14 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) ...@@ -634,10 +634,14 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
} }
/* If offset bit set, skip it. */ /* If offset bit set, skip it. */
if (hdrflags & L2TP_HDRFLAG_O) if (hdrflags & L2TP_HDRFLAG_O) {
ptr += 2 + ntohs(*(__be16 *) ptr); offset = ntohs(*(__be16 *)ptr);
skb->transport_header += 2 + offset;
if (!pskb_may_pull(skb, skb_transport_offset(skb) + 2))
goto discard;
}
skb_pull(skb, ptr - skb->data); __skb_pull(skb, skb_transport_offset(skb));
/* Skip PPP header, if present. In testing, Microsoft L2TP clients /* Skip PPP header, if present. In testing, Microsoft L2TP clients
* don't send the PPP header (PPP header compression enabled), but * don't send the PPP header (PPP header compression enabled), but
...@@ -673,7 +677,6 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) ...@@ -673,7 +677,6 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
*/ */
if (PPPOL2TP_SKB_CB(skb)->ns != session->nr) { if (PPPOL2TP_SKB_CB(skb)->ns != session->nr) {
session->stats.rx_seq_discards++; session->stats.rx_seq_discards++;
session->stats.rx_errors++;
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG, PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
"%s: oos pkt %hu len %d discarded, " "%s: oos pkt %hu len %d discarded, "
"waiting for %hu, reorder_q_len=%d\n", "waiting for %hu, reorder_q_len=%d\n",
...@@ -698,6 +701,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) ...@@ -698,6 +701,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
return 0; return 0;
discard: discard:
session->stats.rx_errors++;
kfree_skb(skb); kfree_skb(skb);
sock_put(session->sock); sock_put(session->sock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment