Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91781004 authored by James Chapman's avatar James Chapman Committed by David S. Miller
Browse files

[PPP]: L2TP: Fix oops in transmit and receive paths



Changes made on 18-sep to fix skb handling in the pppol2tp driver
broke the transmit and receive paths. Users are only running into this
now because distros are now using 2.6.23 and I must have messed up
when I tested the change.

For receive, we now do our own calculation of how much to pull from
the skb (variable length L2TP header) rather than using
skb_transport_offset(). Also, if the skb isn't a data packet, it must
be passed back to UDP with skb->data pointing to the UDP header.

For transmit, make sure skb->sk is set up because ip_queue_xmit()
needs it.

Signed-off-by: default avatarJames Chapman <jchapman@katalix.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6a9fb947
Loading
Loading
Loading
Loading
+18 −7
Original line number Diff line number Diff line
@@ -488,7 +488,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
{
	struct pppol2tp_session *session = NULL;
	struct pppol2tp_tunnel *tunnel;
	unsigned char *ptr;
	unsigned char *ptr, *optr;
	u16 hdrflags;
	u16 tunnel_id, session_id;
	int length;
@@ -496,7 +496,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)

	tunnel = pppol2tp_sock_to_tunnel(sock);
	if (tunnel == NULL)
		goto error;
		goto no_tunnel;

	/* UDP always verifies the packet length. */
	__skb_pull(skb, sizeof(struct udphdr));
@@ -509,7 +509,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
	}

	/* Point to L2TP header */
	ptr = skb->data;
	optr = ptr = skb->data;

	/* Get L2TP header flags */
	hdrflags = ntohs(*(__be16*)ptr);
@@ -637,12 +637,14 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
	/* If offset bit set, skip it. */
	if (hdrflags & L2TP_HDRFLAG_O) {
		offset = ntohs(*(__be16 *)ptr);
		skb->transport_header += 2 + offset;
		if (!pskb_may_pull(skb, skb_transport_offset(skb) + 2))
			goto discard;
		ptr += 2 + offset;
	}

	__skb_pull(skb, skb_transport_offset(skb));
	offset = ptr - optr;
	if (!pskb_may_pull(skb, offset))
		goto discard;

	__skb_pull(skb, offset);

	/* Skip PPP header, if present.	 In testing, Microsoft L2TP clients
	 * don't send the PPP header (PPP header compression enabled), but
@@ -652,6 +654,9 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
	 * Note that skb->data[] isn't dereferenced from a u16 ptr here since
	 * the field may be unaligned.
	 */
	if (!pskb_may_pull(skb, 2))
		goto discard;

	if ((skb->data[0] == 0xff) && (skb->data[1] == 0x03))
		skb_pull(skb, 2);

@@ -709,6 +714,10 @@ discard:
	return 0;

error:
	/* Put UDP header back */
	__skb_push(skb, sizeof(struct udphdr));

no_tunnel:
	return 1;
}

@@ -1050,6 +1059,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
	/* Get routing info from the tunnel socket */
	dst_release(skb->dst);
	skb->dst = sk_dst_get(sk_tun);
	skb_orphan(skb);
	skb->sk = sk_tun;

	/* Queue the packet to IP for output */
	len = skb->len;