summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@gnumonks.org>2014-03-18 14:12:24 +0100
committerPablo Neira Ayuso <pablo@gnumonks.org>2014-03-18 18:30:01 +0100
commitf427e3fc9da1f241b34f3ea7225f8e656824271e (patch)
treedbe6d914166b7613981d79830de08021490b5752
parent4c86bcaab430dc0c374fd3da6a170d9f6b6adf64 (diff)
gtp: fix wrong stripping of UDP and GTP headers
We cannot strip the UDP header until we are sure that this is a data packet, otherwise this passes a malformed packet to userspace. The header stripping is now the very last operation in the processing of GTP packets.
-rw-r--r--gtp.c58
1 files changed, 27 insertions, 31 deletions
diff --git a/gtp.c b/gtp.c
index 283a268..f7244ac 100644
--- a/gtp.c
+++ b/gtp.c
@@ -186,26 +186,29 @@ static inline struct gtp_instance *sk_to_gti(struct sock *sk)
/* Check if the inner IP header has the source address assigned to the
* current MS.
*/
-static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx)
+static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
+ unsigned int hdrlen)
{
bool ret = false;
if (skb->protocol == ntohs(ETH_P_IP)) {
struct iphdr *iph;
- if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+ if (!pskb_may_pull(skb, hdrlen + sizeof(struct iphdr)))
return false;
- iph = (struct iphdr *)skb->data;
+ iph = (struct iphdr *)
+ (skb->data + hdrlen + sizeof(struct iphdr));
ret = (iph->saddr != pctx->ms_addr.ip4.s_addr);
} else if (skb->protocol == ntohs(ETH_P_IPV6)) {
struct ipv6hdr *ip6h;
- if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+ if (!pskb_may_pull(skb, hdrlen + sizeof(struct ipv6hdr)))
return false;
- ip6h = (struct ipv6hdr *)skb->data;
+ ip6h = (struct ipv6hdr *)
+ (skb->data + hdrlen + sizeof(struct ipv6hdr));
ret = memcmp(&ip6h->saddr, &pctx->ms_addr.ip6,
sizeof(struct in6_addr)) == 0;
}
@@ -218,15 +221,16 @@ static int gtp0_udp_encap_recv(struct gtp_instance *gti, struct sk_buff *skb)
{
struct gtp0_header *gtp0;
struct pdp_ctx *pctx;
+ unsigned int hdrlen = sizeof(struct udphdr) + sizeof(*gtp0);
int ret = 0;
pr_info("gtp0 udp received\n");
/* check for sufficient header size */
- if (!pskb_may_pull(skb, sizeof(*gtp0)))
+ if (!pskb_may_pull(skb, hdrlen))
return -1;
- gtp0 = (struct gtp0_header *)skb->data;
+ gtp0 = (struct gtp0_header *)(skb->data + sizeof(struct udphdr));
/* check for GTP Version 0 */
if ((gtp0->flags >> 5) != GTP_V0)
@@ -244,12 +248,13 @@ static int gtp0_udp_encap_recv(struct gtp_instance *gti, struct sk_buff *skb)
goto out_rcu;
}
- /* get rid of the GTP header */
- __skb_pull(skb, sizeof(*gtp0));
-
- if (!gtp_check_src_ms(skb, pctx))
+ if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
ret = -1;
+ goto out_rcu;
+ }
+ /* get rid of the GTP + UDP headers */
+ __skb_pull(skb, hdrlen);
out_rcu:
rcu_read_unlock();
return ret;
@@ -270,16 +275,16 @@ static int gtp1u_udp_encap_recv(struct gtp_instance *gti, struct sk_buff *skb)
{
struct gtp1_header *gtp1;
struct pdp_ctx *pctx;
- unsigned int gtp1_hdrlen = sizeof(*gtp1);
+ unsigned int hdrlen = sizeof(struct udphdr) + sizeof(*gtp1);
int ret = 0;
pr_info("gtp1 udp received\n");
/* check for sufficient header size */
- if (!pskb_may_pull(skb, sizeof(*gtp1)))
+ if (!pskb_may_pull(skb, hdrlen))
return -1;
- gtp1 = (struct gtp1_header *)skb->data;
+ gtp1 = (struct gtp1_header *)(skb->data + sizeof(struct udphdr));
/* check for GTP Version 1 */
if ((gtp1->flags >> 5) != GTP_V1)
@@ -290,10 +295,10 @@ static int gtp1u_udp_encap_recv(struct gtp_instance *gti, struct sk_buff *skb)
return 1;
/* look-up table for faster length computing */
- gtp1_hdrlen = gtp1u_header_len[gtp1->flags & GTP1_F_MASK];
+ hdrlen += gtp1u_header_len[gtp1->flags & GTP1_F_MASK];
- /* check for sufficient header size */
- if (gtp1_hdrlen && !pskb_may_pull(skb, gtp1_hdrlen))
+ /* check for sufficient header size for extension */
+ if (!pskb_may_pull(skb, hdrlen))
return -1;
/* look-up the PDP context for the Tunnel ID */
@@ -304,14 +309,13 @@ static int gtp1u_udp_encap_recv(struct gtp_instance *gti, struct sk_buff *skb)
goto out_rcu;
}
- /* get rid of the GTP header */
- __skb_pull(skb, sizeof(*gtp1) + gtp1_hdrlen);
-
- /* FIXME: actually take care of extension header chain */
-
- if (!gtp_check_src_ms(skb, pctx))
+ if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
ret = -1;
+ goto out_rcu;
+ }
+ /* get rid of the UDP + GTP header + extensions */
+ __skb_pull(skb, hdrlen);
out_rcu:
rcu_read_unlock();
return ret;
@@ -332,14 +336,6 @@ static int gtp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
if (!gti)
goto user;
- /* UDP verifies the packet length, but this may be fragmented, so make
- * sure the UDP header is linear.
- */
- if (!pskb_may_pull(skb, sizeof(struct udphdr)))
- goto user_put;
-
- __skb_pull(skb, sizeof(struct udphdr));
-
switch (udp_sk(sk)->encap_type) {
case UDP_ENCAP_GTP0:
ret = gtp0_udp_encap_recv(gti, skb);