@@ -525,11 +525,10 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
* after the session-id.
*/
void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
- unsigned char *ptr, unsigned char *optr, u16 hdrflags,
+ int offset, u16 hdrflags,
int length, int (*payload_hook)(struct sk_buff *skb))
{
struct l2tp_tunnel *tunnel = session->tunnel;
- int offset;
u32 ns, nr;
/* The ref count is increased since we now hold a pointer to
@@ -542,14 +541,17 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
/* Parse and check optional cookie */
if (session->peer_cookie_len > 0) {
- if (memcmp(ptr, &session->peer_cookie[0], session->peer_cookie_len)) {
+ if (!pskb_may_pull(skb, offset + session->peer_cookie_len) ||
+ memcmp(skb->data + offset,
+ &session->peer_cookie[0],
+ session->peer_cookie_len)) {
PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO,
"%s: cookie mismatch (%u/%u). Discarding.\n",
tunnel->name, tunnel->tunnel_id, session->session_id);
session->stats.rx_cookie_discards++;
goto discard;
}
- ptr += session->peer_cookie_len;
+ offset += session->peer_cookie_len;
}
/* Handle the optional sequence numbers. Sequence numbers are
@@ -563,10 +565,12 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
L2TP_SKB_CB(skb)->has_seq = 0;
if (tunnel->version == L2TP_HDR_VER_2) {
if (hdrflags & L2TP_HDRFLAG_S) {
- ns = ntohs(*(__be16 *) ptr);
- ptr += 2;
- nr = ntohs(*(__be16 *) ptr);
- ptr += 2;
+ if (!pskb_may_pull(skb, offset + 4))
+ goto discard;
+ ns = ntohs(*(__be16 *) (skb->data + offset));
+ offset += 2;
+ nr = ntohs(*(__be16 *) (skb->data + offset));
+ offset += 2;
/* Store L2TP info in the skb */
L2TP_SKB_CB(skb)->ns = ns;
@@ -577,8 +581,11 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
session->name, ns, nr, session->nr);
}
} else if (session->l2specific_type == L2TP_L2SPECTYPE_DEFAULT) {
- u32 l2h = ntohl(*(__be32 *) ptr);
+ u32 l2h;
+ if (!pskb_may_pull(skb, offset + 4))
+ goto discard;
+ l2h = ntohl(*(__be32 *) (skb->data + offset));
if (l2h & 0x40000000) {
ns = l2h & 0x00ffffff;
@@ -593,7 +600,7 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
}
/* Advance past L2-specific header, if present */
- ptr += session->l2specific_len;
+ offset += session->l2specific_len;
if (L2TP_SKB_CB(skb)->has_seq) {
/* Received a packet with sequence numbers. If we're the LNS,
@@ -647,13 +654,13 @@ void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb,
if (tunnel->version == L2TP_HDR_VER_2) {
/* If offset bit set, skip it. */
if (hdrflags & L2TP_HDRFLAG_O) {
- offset = ntohs(*(__be16 *)ptr);
- ptr += 2 + offset;
+ if (!pskb_may_pull(skb, offset + 2))
+ goto discard;
+ offset += 2 + ntohs(*(__be16 *)(skb->data + offset));
}
} else
- ptr += session->offset;
+ offset += session->offset;
- offset = ptr - optr;
if (!pskb_may_pull(skb, offset))
goto discard;
@@ -735,10 +742,10 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
int (*payload_hook)(struct sk_buff *skb))
{
struct l2tp_session *session = NULL;
- unsigned char *ptr, *optr;
+ unsigned char *ptr;
u16 hdrflags;
u32 tunnel_id, session_id;
- int offset;
+ int hlen;
u16 version;
int length;
@@ -756,20 +763,22 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
}
/* Point to L2TP header */
- optr = ptr = skb->data;
+ ptr = skb->data;
/* Trace packet contents, if enabled */
if (tunnel->debug & L2TP_MSG_DATA) {
+ int i;
+
length = min(32u, skb->len);
if (!pskb_may_pull(skb, length))
goto error;
-
+ ptr = skb->data;
printk(KERN_DEBUG "%s: recv: ", tunnel->name);
- offset = 0;
+ i = 0;
do {
- printk(" %02X", ptr[offset]);
- } while (++offset < length);
+ printk(" %02X", ptr[i]);
+ } while (++i < length);
printk("\n");
}
@@ -797,23 +806,23 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
}
/* Skip flags */
- ptr += 2;
+ hlen = 2;
if (tunnel->version == L2TP_HDR_VER_2) {
/* If length is present, skip it */
if (hdrflags & L2TP_HDRFLAG_L)
- ptr += 2;
+ hlen += 2;
/* Extract tunnel and session ID */
- tunnel_id = ntohs(*(__be16 *) ptr);
- ptr += 2;
- session_id = ntohs(*(__be16 *) ptr);
- ptr += 2;
+ tunnel_id = ntohs(*(__be16 *) (ptr + hlen));
+ hlen += 2;
+ session_id = ntohs(*(__be16 *) (ptr + hlen));
+ hlen += 2;
} else {
- ptr += 2; /* skip reserved bits */
+ hlen += 2; /* skip reserved bits */
tunnel_id = tunnel->tunnel_id;
- session_id = ntohl(*(__be32 *) ptr);
- ptr += 4;
+ session_id = ntohl(*(__be32 *) (ptr + hlen));
+ hlen += 4;
}
/* Find the session context */
@@ -826,7 +835,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
goto error;
}
- l2tp_recv_common(session, skb, ptr, optr, hdrflags, length, payload_hook);
+ l2tp_recv_common(session, skb, hlen, hdrflags, length, payload_hook);
return 0;
@@ -232,7 +232,7 @@ extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg);
extern int l2tp_session_delete(struct l2tp_session *session);
extern void l2tp_session_free(struct l2tp_session *session);
-extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, unsigned char *ptr, unsigned char *optr, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb));
+extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, int offset, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb));
extern int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb);
extern int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len);
@@ -123,20 +123,15 @@ static int l2tp_ip_recv(struct sk_buff *skb)
struct sock *sk;
u32 session_id;
u32 tunnel_id;
- unsigned char *ptr, *optr;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel = NULL;
- int length;
- int offset;
-
- /* Point to L2TP header */
- optr = ptr = skb->data;
+ int hlen;
if (!pskb_may_pull(skb, 4))
goto discard;
- session_id = ntohl(*((__be32 *) ptr));
- ptr += 4;
+ session_id = ntohl(*(__be32 *) skb->data);
+ hlen = 4;
/* RFC3931: L2TP/IP packets have the first 4 bytes containing
* the session_id. If it is 0, the packet is a L2TP control
@@ -158,21 +153,22 @@ static int l2tp_ip_recv(struct sk_buff *skb)
/* Trace packet contents, if enabled */
if (tunnel->debug & L2TP_MSG_DATA) {
- length = min(32u, skb->len);
+ int i, length = min(32u, skb->len);
+
if (!pskb_may_pull(skb, length))
goto discard;
printk(KERN_DEBUG "%s: ip recv: ", tunnel->name);
- offset = 0;
+ i = 0;
do {
- printk(" %02X", ptr[offset]);
- } while (++offset < length);
+ printk(" %02X", skb->data[i]);
+ } while (++i < length);
printk("\n");
}
- l2tp_recv_common(session, skb, ptr, optr, 0, skb->len, tunnel->recv_payload_hook);
+ l2tp_recv_common(session, skb, hlen, 0, skb->len, tunnel->recv_payload_hook);
return 0;