gtp: fix receival path of version 0

After this patch, it already forwards a (yet malformed) packet to
the real device. GTPv1 receival path yet untested but similar
changes that were done to GTPv0 has been done.
This commit is contained in:
Pablo Neira Ayuso 2014-02-05 01:08:54 +01:00
parent e01b17c8eb
commit b968ddf47f
1 changed files with 36 additions and 10 deletions

46
gtp.c
View File

@ -242,21 +242,28 @@ static inline struct gtp_instance *sk_to_gti(struct sock *sk)
* Return codes: 0: succes, <0: error, >0: passed up to userspace UDP */ * Return codes: 0: succes, <0: error, >0: passed up to userspace UDP */
static int gtp0_udp_encap_recv(struct sock *sk, struct sk_buff *skb) static int gtp0_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{ {
struct gtp0_header *gtp0 = (struct gtp0_header *) skb_transport_header(skb); struct gtp0_header *gtp0;
struct gtp_instance *gti; struct gtp_instance *gti;
struct pdp_ctx *pctx; struct pdp_ctx *pctx;
uint64_t tid; uint64_t tid;
int rc; int rc;
pr_info("gtp0 udp received\n");
/* resolve the GTP instance to which the socket belongs */ /* resolve the GTP instance to which the socket belongs */
gti = sk_to_gti(sk); gti = sk_to_gti(sk);
if (!gti) if (!gti)
goto user; goto user;
/* UDP always verifies the packet length. */
__skb_pull(skb, sizeof(struct udphdr));
/* check for sufficient header size */ /* check for sufficient header size */
if (!pskb_may_pull(skb, sizeof(struct udphdr) + sizeof(*gtp0))) if (!pskb_may_pull(skb, sizeof(*gtp0)))
goto drop_put; goto drop_put;
gtp0 = (struct gtp0_header *)skb->data;
/* check for GTP Version 0 */ /* check for GTP Version 0 */
if ((gtp0->flags >> 5) != 0) if ((gtp0->flags >> 5) != 0)
goto drop_put; goto drop_put;
@ -273,8 +280,13 @@ static int gtp0_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
if (!pctx) if (!pctx)
goto drop_put_rcu; goto drop_put_rcu;
/* get rid of the UDP and GTP header */ /* get rid of the GTP header */
__skb_pull(skb, sizeof(struct udphdr) + sizeof(*gtp0)); __skb_pull(skb, sizeof(*gtp0));
/* We're about to requeue the skb, so return resources
* to its current owner (a socket receive buffer).
*/
skb_orphan(skb);
/* FIXME: check if the inner IP header has the source address /* FIXME: check if the inner IP header has the source address
* assigned to the current MS */ * assigned to the current MS */
@ -303,24 +315,31 @@ user:
* Return codes: 0: succes, <0: error, >0: passed up to userspace UDP */ * Return codes: 0: succes, <0: error, >0: passed up to userspace UDP */
static int gtp1u_udp_encap_recv(struct sock *sk, struct sk_buff *skb) static int gtp1u_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{ {
struct gtp1_header_short *gtp1 = struct gtp1_header_short *gtp1;
(struct gtp1_header_short *) skb_transport_header(skb); struct gtp0_header *gtp0;
struct gtp0_header *gtp0 = (struct gtp0_header *) gtp1;
struct gtp_instance *gti; struct gtp_instance *gti;
struct pdp_ctx *pctx; struct pdp_ctx *pctx;
unsigned int min_len = sizeof(*gtp1); unsigned int min_len = sizeof(*gtp1);
uint64_t tid; uint64_t tid;
int rc; int rc;
pr_info("gtp1 udp received\n");
/* resolve the GTP instance to which the socket belongs */ /* resolve the GTP instance to which the socket belongs */
gti = sk_to_gti(sk); gti = sk_to_gti(sk);
if (!gti) if (!gti)
goto user; goto user;
/* UDP always verifies the packet length. */
__skb_pull(skb, sizeof(struct udphdr));
/* check for sufficient header size */ /* check for sufficient header size */
if (!pskb_may_pull(skb, sizeof(struct udphdr) + sizeof(*gtp1))) if (!pskb_may_pull(skb, sizeof(*gtp1)))
goto drop_put; goto drop_put;
gtp1 = (struct gtp1_header_short *)skb->data;
gtp0 = (struct gtp0_header *)gtp1;
/* check for GTP Version 1 */ /* check for GTP Version 1 */
if ((gtp0->flags >> 5) != 1) if ((gtp0->flags >> 5) != 1)
goto drop_put; goto drop_put;
@ -357,12 +376,17 @@ static int gtp1u_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
if (!pctx) if (!pctx)
goto drop_put_rcu; goto drop_put_rcu;
/* get rid of the UDP and GTP header */ /* get rid of the GTP header */
__skb_pull(skb, sizeof(struct udphdr) + sizeof(*gtp1)); __skb_pull(skb, sizeof(*gtp1));
/* FIXME: check if the inner IP header has the source address /* FIXME: check if the inner IP header has the source address
* assigned to the current MS */ * assigned to the current MS */
/* We're about to requeue the skb, so return resources
* to its current owner (a socket receive buffer).
*/
skb_orphan(skb);
/* re-submit via virtual tunnel device into regular network /* re-submit via virtual tunnel device into regular network
* stack */ * stack */
secpath_reset(skb); secpath_reset(skb);
@ -731,6 +755,7 @@ static int gtp_create_bind_sock(struct gtp_instance *gti)
sk = gti->sock0->sk; sk = gti->sock0->sk;
udp_sk(sk)->encap_type = UDP_ENCAP_GTP0; udp_sk(sk)->encap_type = UDP_ENCAP_GTP0;
udp_sk(sk)->encap_rcv = gtp0_udp_encap_recv; udp_sk(sk)->encap_rcv = gtp0_udp_encap_recv;
sk->sk_user_data = gti;
udp_encap_enable(); udp_encap_enable();
/* Create and bind the socket for GTP1 user-plane */ /* Create and bind the socket for GTP1 user-plane */
@ -748,6 +773,7 @@ static int gtp_create_bind_sock(struct gtp_instance *gti)
sk = gti->sock1u->sk; sk = gti->sock1u->sk;
udp_sk(sk)->encap_type = UDP_ENCAP_GTP1U; udp_sk(sk)->encap_type = UDP_ENCAP_GTP1U;
udp_sk(sk)->encap_rcv = gtp1u_udp_encap_recv; udp_sk(sk)->encap_rcv = gtp1u_udp_encap_recv;
sk->sk_user_data = gti;
return 0; return 0;