diff mbox

[net-next,v4,4/7] gtp: consolidate gtp socket rx path

Message ID 20170221101855.23940-5-aschultz@tpip.net
State Deferred, archived
Delegated to: David Miller
Headers show

Commit Message

Andreas Schultz Feb. 21, 2017, 10:18 a.m. UTC
Add network device to gtp context in preparation for splitting
the TEID from the network device.

Use this to rework the socker rx path. Move the common RX part
of v0 and v1 into a helper. Also move the final rx part into
that helper as well.

Signed-off-by: Andreas Schultz <aschultz@tpip.net>
---
 drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 44 insertions(+), 36 deletions(-)

Comments

Tom Herbert Feb. 22, 2017, 5:41 p.m. UTC | #1
On Tue, Feb 21, 2017 at 2:18 AM, Andreas Schultz <aschultz@tpip.net> wrote:
> Add network device to gtp context in preparation for splitting
> the TEID from the network device.
>
> Use this to rework the socker rx path. Move the common RX part
> of v0 and v1 into a helper. Also move the final rx part into
> that helper as well.
>
Andeas,

How are these GTP kernel patches being tested? Is it possible to
create some sort of GTP network device that separates out just the
datapath for development in the same way that VXLAN did this?

Tom

> Signed-off-by: Andreas Schultz <aschultz@tpip.net>
> ---
>  drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++-------------------------
>  1 file changed, 44 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
> index 961fb3c..fc0fff5 100644
> --- a/drivers/net/gtp.c
> +++ b/drivers/net/gtp.c
> @@ -58,6 +58,8 @@ struct pdp_ctx {
>         struct in_addr          ms_addr_ip4;
>         struct in_addr          sgsn_addr_ip4;
>
> +       struct net_device       *dev;
> +
>         atomic_t                tx_seq;
>         struct rcu_head         rcu_head;
>  };
> @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
>         return false;
>  }
>
> +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen,
> +                 bool xnet)
> +{
> +       struct pcpu_sw_netstats *stats;
> +
> +       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
> +               netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
> +               return 1;
> +       }
> +
> +       /* Get rid of the GTP + UDP headers. */
> +       if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet))
> +               return -1;
> +
> +       netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n");
> +
> +       /* Now that the UDP and the GTP header have been removed, set up the
> +        * new network header. This is required by the upper layer to
> +        * calculate the transport header.
> +        */
> +       skb_reset_network_header(skb);
> +
> +       skb->dev = pctx->dev;
> +
> +       stats = this_cpu_ptr(pctx->dev->tstats);
> +       u64_stats_update_begin(&stats->syncp);
> +       stats->rx_packets++;
> +       stats->rx_bytes += skb->len;
> +       u64_stats_update_end(&stats->syncp);
> +
> +       netif_rx(skb);
> +       return 0;
> +}
> +
>  /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
>  static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
>                                bool xnet)
> @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
>                 return 1;
>         }
>
> -       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
> -               netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
> -               return 1;
> -       }
> -
> -       /* Get rid of the GTP + UDP headers. */
> -       return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
> +       return gtp_rx(pctx, skb, hdrlen, xnet);
>  }
>
>  static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
> @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
>                 return 1;
>         }
>
> -       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
> -               netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
> -               return 1;
> -       }
> -
> -       /* Get rid of the GTP + UDP headers. */
> -       return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
> +       return gtp_rx(pctx, skb, hdrlen, xnet);
>  }
>
>  static void gtp_encap_destroy(struct sock *sk)
> @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp)
>   */
>  static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
>  {
> -       struct pcpu_sw_netstats *stats;
>         struct gtp_dev *gtp;
> +       int ret = 0;
>         bool xnet;
> -       int ret;
>
>         gtp = rcu_dereference_sk_user_data(sk);
>         if (!gtp)
> @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
>         switch (ret) {
>         case 1:
>                 netdev_dbg(gtp->dev, "pass up to the process\n");
> -               return 1;
> +               break;
>         case 0:
> -               netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n");
>                 break;
>         case -1:
>                 netdev_dbg(gtp->dev, "GTP packet has been dropped\n");
>                 kfree_skb(skb);
> -               return 0;
> +               ret = 0;
> +               break;
>         }
>
> -       /* Now that the UDP and the GTP header have been removed, set up the
> -        * new network header. This is required by the upper layer to
> -        * calculate the transport header.
> -        */
> -       skb_reset_network_header(skb);
> -
> -       skb->dev = gtp->dev;
> -
> -       stats = this_cpu_ptr(gtp->dev->tstats);
> -       u64_stats_update_begin(&stats->syncp);
> -       stats->rx_packets++;
> -       stats->rx_bytes += skb->len;
> -       u64_stats_update_end(&stats->syncp);
> -
> -       netif_rx(skb);
> -
> -       return 0;
> +       return ret;
>  }
>
>  static int gtp_dev_init(struct net_device *dev)
> @@ -951,6 +958,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info)
>         if (pctx == NULL)
>                 return -ENOMEM;
>
> +       pctx->dev = gtp->dev;
>         ipv4_pdp_fill(pctx, info);
>         atomic_set(&pctx->tx_seq, 0);
>
> --
> 2.10.2
>
Andreas Schultz Feb. 23, 2017, 9:19 a.m. UTC | #2
Hi Tom,

----- On Feb 22, 2017, at 6:41 PM, Tom Herbert tom@herbertland.com wrote:

> On Tue, Feb 21, 2017 at 2:18 AM, Andreas Schultz <aschultz@tpip.net> wrote:
>> Add network device to gtp context in preparation for splitting
>> the TEID from the network device.
>>
>> Use this to rework the socker rx path. Move the common RX part
>> of v0 and v1 into a helper. Also move the final rx part into
>> that helper as well.
>>
> Andeas,
> 
> How are these GTP kernel patches being tested?

We rn each version in a test setup with a ePDG and a SGW that
connects to a full GGSN/P-GW instance (based on erGW).
We also run performance test (less often) with a commercial
test software.

> Is it possible to > create some sort of GTP network device
> that separates out just the datapath for development in the
> same way that VXLAN did this?

We had this discussion about another patch:
(http://marc.info/?l=linux-netdev&m=148611438811696&w=2)

Currently the kernel module only supports the GGSN/P-GW side
of the GTP tunnel. This is because we check the UE IP address
in the GTP socket and use the destination IP in the network
interface to find the PDP context.

For a deployment in a real EPC, doing it the other way makes no
sense with the current code. However for a test setup it makes
perfect sense (either to use it as a driver to test other GTP
nodes or to test out own implementation).

So, I hope that we can integrate this soonish.

libgtpnl contains two tools that be used for testing. gtp-link
creates a network device and GTP sockets and keeps them alive.
gtp-tunnel can then be used add PDP context to that. The only
missing part for a bidirectional test setup is the above
mentioned patch with the direction flag and support for that
in the libgtpnl tools.

Adding static tunnel support into the kernel module in any form
makes IMHO no sense. GTP as defined by 3GPP always need a control
instance and there are much better options for static tunnel
encapsulations.

Andreas

> 
> Tom
> 
>> Signed-off-by: Andreas Schultz <aschultz@tpip.net>
>> ---
>>  drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++-------------------------
>>  1 file changed, 44 insertions(+), 36 deletions(-)
>>
>> diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
>> index 961fb3c..fc0fff5 100644
>> --- a/drivers/net/gtp.c
>> +++ b/drivers/net/gtp.c
>> @@ -58,6 +58,8 @@ struct pdp_ctx {
>>         struct in_addr          ms_addr_ip4;
>>         struct in_addr          sgsn_addr_ip4;
>>
>> +       struct net_device       *dev;
>> +
>>         atomic_t                tx_seq;
>>         struct rcu_head         rcu_head;
>>  };
>> @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct
>> pdp_ctx *pctx,
>>         return false;
>>  }
>>
>> +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int
>> hdrlen,
>> +                 bool xnet)
>> +{
>> +       struct pcpu_sw_netstats *stats;
>> +
>> +       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
>> +               netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
>> +               return 1;
>> +       }
>> +
>> +       /* Get rid of the GTP + UDP headers. */
>> +       if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet))
>> +               return -1;
>> +
>> +       netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n");
>> +
>> +       /* Now that the UDP and the GTP header have been removed, set up the
>> +        * new network header. This is required by the upper layer to
>> +        * calculate the transport header.
>> +        */
>> +       skb_reset_network_header(skb);
>> +
>> +       skb->dev = pctx->dev;
>> +
>> +       stats = this_cpu_ptr(pctx->dev->tstats);
>> +       u64_stats_update_begin(&stats->syncp);
>> +       stats->rx_packets++;
>> +       stats->rx_bytes += skb->len;
>> +       u64_stats_update_end(&stats->syncp);
>> +
>> +       netif_rx(skb);
>> +       return 0;
>> +}
>> +
>>  /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
>>  static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
>>                                bool xnet)
>> @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct
>> sk_buff *skb,
>>                 return 1;
>>         }
>>
>> -       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
>> -               netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
>> -               return 1;
>> -       }
>> -
>> -       /* Get rid of the GTP + UDP headers. */
>> -       return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
>> +       return gtp_rx(pctx, skb, hdrlen, xnet);
>>  }
>>
>>  static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
>> @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct
>> sk_buff *skb,
>>                 return 1;
>>         }
>>
>> -       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
>> -               netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
>> -               return 1;
>> -       }
>> -
>> -       /* Get rid of the GTP + UDP headers. */
>> -       return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
>> +       return gtp_rx(pctx, skb, hdrlen, xnet);
>>  }
>>
>>  static void gtp_encap_destroy(struct sock *sk)
>> @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp)
>>   */
>>  static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
>>  {
>> -       struct pcpu_sw_netstats *stats;
>>         struct gtp_dev *gtp;
>> +       int ret = 0;
>>         bool xnet;
>> -       int ret;
>>
>>         gtp = rcu_dereference_sk_user_data(sk);
>>         if (!gtp)
>> @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff
>> *skb)
>>         switch (ret) {
>>         case 1:
>>                 netdev_dbg(gtp->dev, "pass up to the process\n");
>> -               return 1;
>> +               break;
>>         case 0:
>> -               netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n");
>>                 break;
>>         case -1:
>>                 netdev_dbg(gtp->dev, "GTP packet has been dropped\n");
>>                 kfree_skb(skb);
>> -               return 0;
>> +               ret = 0;
>> +               break;
>>         }
>>
>> -       /* Now that the UDP and the GTP header have been removed, set up the
>> -        * new network header. This is required by the upper layer to
>> -        * calculate the transport header.
>> -        */
>> -       skb_reset_network_header(skb);
>> -
>> -       skb->dev = gtp->dev;
>> -
>> -       stats = this_cpu_ptr(gtp->dev->tstats);
>> -       u64_stats_update_begin(&stats->syncp);
>> -       stats->rx_packets++;
>> -       stats->rx_bytes += skb->len;
>> -       u64_stats_update_end(&stats->syncp);
>> -
>> -       netif_rx(skb);
>> -
>> -       return 0;
>> +       return ret;
>>  }
>>
>>  static int gtp_dev_init(struct net_device *dev)
>> @@ -951,6 +958,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct
>> genl_info *info)
>>         if (pctx == NULL)
>>                 return -ENOMEM;
>>
>> +       pctx->dev = gtp->dev;
>>         ipv4_pdp_fill(pctx, info);
>>         atomic_set(&pctx->tx_seq, 0);
>>
>> --
>> 2.10.2
Tom Herbert Feb. 23, 2017, 4:28 p.m. UTC | #3
On Thu, Feb 23, 2017 at 1:19 AM, Andreas Schultz <aschultz@tpip.net> wrote:
> Hi Tom,
>
> ----- On Feb 22, 2017, at 6:41 PM, Tom Herbert tom@herbertland.com wrote:
>
>> On Tue, Feb 21, 2017 at 2:18 AM, Andreas Schultz <aschultz@tpip.net> wrote:
>>> Add network device to gtp context in preparation for splitting
>>> the TEID from the network device.
>>>
>>> Use this to rework the socker rx path. Move the common RX part
>>> of v0 and v1 into a helper. Also move the final rx part into
>>> that helper as well.
>>>
>> Andeas,
>>
>> How are these GTP kernel patches being tested?
>
> We rn each version in a test setup with a ePDG and a SGW that
> connects to a full GGSN/P-GW instance (based on erGW).
> We also run performance test (less often) with a commercial
> test software.
>
>> Is it possible to > create some sort of GTP network device
>> that separates out just the datapath for development in the
>> same way that VXLAN did this?
>
> We had this discussion about another patch:
> (http://marc.info/?l=linux-netdev&m=148611438811696&w=2)
>
> Currently the kernel module only supports the GGSN/P-GW side
> of the GTP tunnel. This is because we check the UE IP address
> in the GTP socket and use the destination IP in the network
> interface to find the PDP context.
>
> For a deployment in a real EPC, doing it the other way makes no
> sense with the current code. However for a test setup it makes
> perfect sense (either to use it as a driver to test other GTP
> nodes or to test out own implementation).
>
> So, I hope that we can integrate this soonish.
>
> libgtpnl contains two tools that be used for testing. gtp-link
> creates a network device and GTP sockets and keeps them alive.
> gtp-tunnel can then be used add PDP context to that. The only
> missing part for a bidirectional test setup is the above
> mentioned patch with the direction flag and support for that
> in the libgtpnl tools.
>
If there is a way for us to test this without setting up a full mobile
network please provide details on how to do that in the commit log.

> Adding static tunnel support into the kernel module in any form
> makes IMHO no sense. GTP as defined by 3GPP always need a control
> instance and there are much better options for static tunnel
> encapsulations.
>
That misses the point. From the kernel point of view GTP is just
another encapsulation protocol and we now have a lot of experience
with those. The problem is when you post patches to improve it or fix
issues, if there is no practical way to perform independent  analysis
or investigation. This makes GTP no different than a proprietary
protocol to us and that severely limits the value of trying to work
with the community.

Tom

> Andreas
>
>>
>> Tom
>>
>>> Signed-off-by: Andreas Schultz <aschultz@tpip.net>
>>> ---
>>>  drivers/net/gtp.c | 80 ++++++++++++++++++++++++++++++-------------------------
>>>  1 file changed, 44 insertions(+), 36 deletions(-)
>>>
>>> diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
>>> index 961fb3c..fc0fff5 100644
>>> --- a/drivers/net/gtp.c
>>> +++ b/drivers/net/gtp.c
>>> @@ -58,6 +58,8 @@ struct pdp_ctx {
>>>         struct in_addr          ms_addr_ip4;
>>>         struct in_addr          sgsn_addr_ip4;
>>>
>>> +       struct net_device       *dev;
>>> +
>>>         atomic_t                tx_seq;
>>>         struct rcu_head         rcu_head;
>>>  };
>>> @@ -175,6 +177,40 @@ static bool gtp_check_src_ms(struct sk_buff *skb, struct
>>> pdp_ctx *pctx,
>>>         return false;
>>>  }
>>>
>>> +static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int
>>> hdrlen,
>>> +                 bool xnet)
>>> +{
>>> +       struct pcpu_sw_netstats *stats;
>>> +
>>> +       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
>>> +               netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
>>> +               return 1;
>>> +       }
>>> +
>>> +       /* Get rid of the GTP + UDP headers. */
>>> +       if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet))
>>> +               return -1;
>>> +
>>> +       netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n");
>>> +
>>> +       /* Now that the UDP and the GTP header have been removed, set up the
>>> +        * new network header. This is required by the upper layer to
>>> +        * calculate the transport header.
>>> +        */
>>> +       skb_reset_network_header(skb);
>>> +
>>> +       skb->dev = pctx->dev;
>>> +
>>> +       stats = this_cpu_ptr(pctx->dev->tstats);
>>> +       u64_stats_update_begin(&stats->syncp);
>>> +       stats->rx_packets++;
>>> +       stats->rx_bytes += skb->len;
>>> +       u64_stats_update_end(&stats->syncp);
>>> +
>>> +       netif_rx(skb);
>>> +       return 0;
>>> +}
>>> +
>>>  /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
>>>  static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
>>>                                bool xnet)
>>> @@ -201,13 +237,7 @@ static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct
>>> sk_buff *skb,
>>>                 return 1;
>>>         }
>>>
>>> -       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
>>> -               netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
>>> -               return 1;
>>> -       }
>>> -
>>> -       /* Get rid of the GTP + UDP headers. */
>>> -       return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
>>> +       return gtp_rx(pctx, skb, hdrlen, xnet);
>>>  }
>>>
>>>  static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
>>> @@ -250,13 +280,7 @@ static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct
>>> sk_buff *skb,
>>>                 return 1;
>>>         }
>>>
>>> -       if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
>>> -               netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
>>> -               return 1;
>>> -       }
>>> -
>>> -       /* Get rid of the GTP + UDP headers. */
>>> -       return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
>>> +       return gtp_rx(pctx, skb, hdrlen, xnet);
>>>  }
>>>
>>>  static void gtp_encap_destroy(struct sock *sk)
>>> @@ -290,10 +314,9 @@ static void gtp_encap_disable(struct gtp_dev *gtp)
>>>   */
>>>  static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
>>>  {
>>> -       struct pcpu_sw_netstats *stats;
>>>         struct gtp_dev *gtp;
>>> +       int ret = 0;
>>>         bool xnet;
>>> -       int ret;
>>>
>>>         gtp = rcu_dereference_sk_user_data(sk);
>>>         if (!gtp)
>>> @@ -319,33 +342,17 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff
>>> *skb)
>>>         switch (ret) {
>>>         case 1:
>>>                 netdev_dbg(gtp->dev, "pass up to the process\n");
>>> -               return 1;
>>> +               break;
>>>         case 0:
>>> -               netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n");
>>>                 break;
>>>         case -1:
>>>                 netdev_dbg(gtp->dev, "GTP packet has been dropped\n");
>>>                 kfree_skb(skb);
>>> -               return 0;
>>> +               ret = 0;
>>> +               break;
>>>         }
>>>
>>> -       /* Now that the UDP and the GTP header have been removed, set up the
>>> -        * new network header. This is required by the upper layer to
>>> -        * calculate the transport header.
>>> -        */
>>> -       skb_reset_network_header(skb);
>>> -
>>> -       skb->dev = gtp->dev;
>>> -
>>> -       stats = this_cpu_ptr(gtp->dev->tstats);
>>> -       u64_stats_update_begin(&stats->syncp);
>>> -       stats->rx_packets++;
>>> -       stats->rx_bytes += skb->len;
>>> -       u64_stats_update_end(&stats->syncp);
>>> -
>>> -       netif_rx(skb);
>>> -
>>> -       return 0;
>>> +       return ret;
>>>  }
>>>
>>>  static int gtp_dev_init(struct net_device *dev)
>>> @@ -951,6 +958,7 @@ static int ipv4_pdp_add(struct gtp_dev *gtp, struct
>>> genl_info *info)
>>>         if (pctx == NULL)
>>>                 return -ENOMEM;
>>>
>>> +       pctx->dev = gtp->dev;
>>>         ipv4_pdp_fill(pctx, info);
>>>         atomic_set(&pctx->tx_seq, 0);
>>>
>>> --
>>> 2.10.2
Harald Welte Feb. 23, 2017, 4:46 p.m. UTC | #4
Hi Tom,

On Thu, Feb 23, 2017 at 08:28:47AM -0800, Tom Herbert wrote:

> If there is a way for us to test this without setting up a full mobile
> network please provide details on how to do that in the commit log.

There are different ways how to do this.  With the existing in-kernel
code (that lacks the "SGSN role" patch which would be for testing only),
you can e.g. use two relatively small C-language programs from the
openggnsn package [http://git.osmocom.org/openggsn/]:

* OpenGGSN with support for libgtpnl and the kernel GTP-U module
* sgsnemu (included in openggsn source tree) which implements a minimal
  SGSN-side of the tunnel.  It will
  * perform the GTP-C signaling required with OpenGGSN (which in turn
    will then instruct the kernel to open a  tunnel via the netlink
    interface).
  * start a tun/tap device for the "mobile station end" of the tunnel
    perform en/decapsulation of data between that tun/tap device and GTP
    in userspace

The wiki at https://osmocom.org/projects/openggsn/wiki/OpenGGSN contains
step-by-step instructions how to build and configure OpenGGSN with
support for kernel GTP-U. It's nothing more than autotools based
compile+install of libgtpnl followed by "./configure --enable-gtp-linux"
and "make" for OpenGGSN 

What is not described on the above page is how to use sgsnemu to
simulate the SGSN side, as within Osmocom we typically run the open
source OsmoSGSN (a more "real" SGSN).

So using two small binaries that can be compiled without much external
dependencies (and very few lines of configuration), it is possible to do
some functional testing of the kernel GTP module.  For performance
testing this of course won't work, as sgsnemu is running entirely in
userspace.

For performance testing, you would need a SGSN-side implementation of
GTP-U that performs equally well or better than the GGSN-side
implementation in the kernel.  One option is the patch that has recently
been submitted to netdev and which is under discussion.  However, then
you simply test one implementation against itself, which provides no
interoperability guarantees with other implementations, and thus also
limited in different regards.

> > Adding static tunnel support into the kernel module in any form
> > makes IMHO no sense. GTP as defined by 3GPP always need a control
> > instance and there are much better options for static tunnel
> > encapsulations.
> >
> That misses the point. From the kernel point of view GTP is just
> another encapsulation protocol and we now have a lot of experience
> with those. The problem is when you post patches to improve it or fix
> issues, if there is no practical way to perform independent  analysis
> or investigation. This makes GTP no different than a proprietary
> protocol to us and that severely limits the value of trying to work
> with the community.

I wholeheartedly agree.  That's one of the reasons why I recently posted
a RFC about what to do for (regression and other) testing of the kernel
GTP-U module.

I'll try to cook up some instructions extending
https://osmocom.org/projects/openggsn/wiki/OpenGGSN to cover also
sgsnemu for a basic use case of establishing one single tunnel.  That's
of course like a manual "HOWTO" and not yet anything that can be tested
automatically.

Regards,
	Harald
Tom Herbert Feb. 23, 2017, 6:07 p.m. UTC | #5
On Thu, Feb 23, 2017 at 8:46 AM, Harald Welte <laforge@gnumonks.org> wrote:
> Hi Tom,
>
> On Thu, Feb 23, 2017 at 08:28:47AM -0800, Tom Herbert wrote:
>
>> If there is a way for us to test this without setting up a full mobile
>> network please provide details on how to do that in the commit log.
>
> There are different ways how to do this.  With the existing in-kernel
> code (that lacks the "SGSN role" patch which would be for testing only),
> you can e.g. use two relatively small C-language programs from the
> openggnsn package [http://git.osmocom.org/openggsn/]:
>
> * OpenGGSN with support for libgtpnl and the kernel GTP-U module
> * sgsnemu (included in openggsn source tree) which implements a minimal
>   SGSN-side of the tunnel.  It will
>   * perform the GTP-C signaling required with OpenGGSN (which in turn
>     will then instruct the kernel to open a  tunnel via the netlink
>     interface).
>   * start a tun/tap device for the "mobile station end" of the tunnel
>     perform en/decapsulation of data between that tun/tap device and GTP
>     in userspace
>
> The wiki at https://osmocom.org/projects/openggsn/wiki/OpenGGSN contains
> step-by-step instructions how to build and configure OpenGGSN with
> support for kernel GTP-U. It's nothing more than autotools based
> compile+install of libgtpnl followed by "./configure --enable-gtp-linux"
> and "make" for OpenGGSN
>
> What is not described on the above page is how to use sgsnemu to
> simulate the SGSN side, as within Osmocom we typically run the open
> source OsmoSGSN (a more "real" SGSN).
>
> So using two small binaries that can be compiled without much external
> dependencies (and very few lines of configuration), it is possible to do
> some functional testing of the kernel GTP module.  For performance
> testing this of course won't work, as sgsnemu is running entirely in
> userspace.
>
I'm looking at the GTP encapsulation, it's not particularly complex.
Is there any real reason why we can't just implement a netdev
interface with static tunnels and configuration to do performance
testing and development? For instance, if we want to add GSO/GRO
support to GTP that's all we really need, the control plane should be
inconsequential in that case.

> For performance testing, you would need a SGSN-side implementation of
> GTP-U that performs equally well or better than the GGSN-side
> implementation in the kernel.  One option is the patch that has recently
> been submitted to netdev and which is under discussion.  However, then
> you simply test one implementation against itself, which provides no
> interoperability guarantees with other implementations, and thus also
> limited in different regards.
>
That's always true of any protocol we implement-- you can only show
interoperability with what you test against. The best thing we can do
to help GTP is not treat it as being something special! Treat it like
any another encapsulation protocol to support in the kernel that needs
to be tested, maintained, have good performance, be interoperable,
etc.

>> > Adding static tunnel support into the kernel module in any form
>> > makes IMHO no sense. GTP as defined by 3GPP always need a control
>> > instance and there are much better options for static tunnel
>> > encapsulations.
>> >
>> That misses the point. From the kernel point of view GTP is just
>> another encapsulation protocol and we now have a lot of experience
>> with those. The problem is when you post patches to improve it or fix
>> issues, if there is no practical way to perform independent  analysis
>> or investigation. This makes GTP no different than a proprietary
>> protocol to us and that severely limits the value of trying to work
>> with the community.
>
> I wholeheartedly agree.  That's one of the reasons why I recently posted
> a RFC about what to do for (regression and other) testing of the kernel
> GTP-U module.
>
> I'll try to cook up some instructions extending
> https://osmocom.org/projects/openggsn/wiki/OpenGGSN to cover also
> sgsnemu for a basic use case of establishing one single tunnel.  That's
> of course like a manual "HOWTO" and not yet anything that can be tested
> automatically.
>
That would be good. Thanks!

> Regards,
>         Harald
>
> --
> - Harald Welte <laforge@gnumonks.org>           http://laforge.gnumonks.org/
> ============================================================================
> "Privacy in residential applications is a desirable marketing option."
>                                                   (ETSI EN 300 175-7 Ch. A6)
Harald Welte Feb. 23, 2017, 9:17 p.m. UTC | #6
Hi Tom,

On Thu, Feb 23, 2017 at 10:07:03AM -0800, Tom Herbert wrote:
> I'm looking at the GTP encapsulation, it's not particularly complex.
> Is there any real reason why we can't just implement a netdev
> interface with static tunnels and configuration to do performance
> testing and development? For instance, if we want to add GSO/GRO
> support to GTP that's all we really need, the control plane should be
> inconsequential in that case.

As outlined several times in this thread, GTP tunneling is not
symmetric.  The current mainline code can only act as one of the two
roles (GGSN/G-GW), not as the other one.  The rationale is that in 3GPP
networks, that is the only point in the network that takes native IP
data (e.g.from the internet) and puts it into GTP.  At all other places
in the network, you don't have this combination.  By the time the inner
IP data arrives at the mobile phone, it is no longer encapsulated in
GTP, as the lower layers have been adapted one or multiple times to
other protocols by other network elements.

There's a patch that has recently been submitted which adds the
capability for the SGSN/S-GW side of a GTP-U tunnel, but that patch has
so far not been merged (due to concersn about its netlink interface),
and the patch *only* exists for testing, it has no real-world
application in 3GPP networks.

So as of now, it is not possible to run both endpoints of a tunnel in
Linux.

> > For performance testing, you would need a SGSN-side implementation of
> > I'll try to cook up some instructions extending
> > https://osmocom.org/projects/openggsn/wiki/OpenGGSN to cover also
> > sgsnemu for a basic use case of establishing one single tunnel.  That's
> > of course like a manual "HOWTO" and not yet anything that can be tested
> > automatically.
> >
> That would be good. Thanks!

I've spent some hours earlier today on this, I expect the document to be
ready at some point over the weekend.
diff mbox

Patch

diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 961fb3c..fc0fff5 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -58,6 +58,8 @@  struct pdp_ctx {
 	struct in_addr		ms_addr_ip4;
 	struct in_addr		sgsn_addr_ip4;
 
+	struct net_device       *dev;
+
 	atomic_t		tx_seq;
 	struct rcu_head		rcu_head;
 };
@@ -175,6 +177,40 @@  static bool gtp_check_src_ms(struct sk_buff *skb, struct pdp_ctx *pctx,
 	return false;
 }
 
+static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, unsigned int hdrlen,
+		  bool xnet)
+{
+	struct pcpu_sw_netstats *stats;
+
+	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
+		netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
+		return 1;
+	}
+
+	/* Get rid of the GTP + UDP headers. */
+	if (iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet))
+		return -1;
+
+	netdev_dbg(pctx->dev, "forwarding packet from GGSN to uplink\n");
+
+	/* Now that the UDP and the GTP header have been removed, set up the
+	 * new network header. This is required by the upper layer to
+	 * calculate the transport header.
+	 */
+	skb_reset_network_header(skb);
+
+	skb->dev = pctx->dev;
+
+	stats = this_cpu_ptr(pctx->dev->tstats);
+	u64_stats_update_begin(&stats->syncp);
+	stats->rx_packets++;
+	stats->rx_bytes += skb->len;
+	u64_stats_update_end(&stats->syncp);
+
+	netif_rx(skb);
+	return 0;
+}
+
 /* 1 means pass up to the stack, -1 means drop and 0 means decapsulated. */
 static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 			       bool xnet)
@@ -201,13 +237,7 @@  static int gtp0_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 		return 1;
 	}
 
-	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
-		netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
-		return 1;
-	}
-
-	/* Get rid of the GTP + UDP headers. */
-	return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
+	return gtp_rx(pctx, skb, hdrlen, xnet);
 }
 
 static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
@@ -250,13 +280,7 @@  static int gtp1u_udp_encap_recv(struct gtp_dev *gtp, struct sk_buff *skb,
 		return 1;
 	}
 
-	if (!gtp_check_src_ms(skb, pctx, hdrlen)) {
-		netdev_dbg(gtp->dev, "No PDP ctx for this MS\n");
-		return 1;
-	}
-
-	/* Get rid of the GTP + UDP headers. */
-	return iptunnel_pull_header(skb, hdrlen, skb->protocol, xnet);
+	return gtp_rx(pctx, skb, hdrlen, xnet);
 }
 
 static void gtp_encap_destroy(struct sock *sk)
@@ -290,10 +314,9 @@  static void gtp_encap_disable(struct gtp_dev *gtp)
  */
 static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 {
-	struct pcpu_sw_netstats *stats;
 	struct gtp_dev *gtp;
+	int ret = 0;
 	bool xnet;
-	int ret;
 
 	gtp = rcu_dereference_sk_user_data(sk);
 	if (!gtp)
@@ -319,33 +342,17 @@  static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
 	switch (ret) {
 	case 1:
 		netdev_dbg(gtp->dev, "pass up to the process\n");
-		return 1;
+		break;
 	case 0:
-		netdev_dbg(gtp->dev, "forwarding packet from GGSN to uplink\n");
 		break;
 	case -1:
 		netdev_dbg(gtp->dev, "GTP packet has been dropped\n");
 		kfree_skb(skb);
-		return 0;
+		ret = 0;
+		break;
 	}
 
-	/* Now that the UDP and the GTP header have been removed, set up the
-	 * new network header. This is required by the upper layer to
-	 * calculate the transport header.
-	 */
-	skb_reset_network_header(skb);
-
-	skb->dev = gtp->dev;
-
-	stats = this_cpu_ptr(gtp->dev->tstats);
-	u64_stats_update_begin(&stats->syncp);
-	stats->rx_packets++;
-	stats->rx_bytes += skb->len;
-	u64_stats_update_end(&stats->syncp);
-
-	netif_rx(skb);
-
-	return 0;
+	return ret;
 }
 
 static int gtp_dev_init(struct net_device *dev)
@@ -951,6 +958,7 @@  static int ipv4_pdp_add(struct gtp_dev *gtp, struct genl_info *info)
 	if (pctx == NULL)
 		return -ENOMEM;
 
+	pctx->dev = gtp->dev;
 	ipv4_pdp_fill(pctx, info);
 	atomic_set(&pctx->tx_seq, 0);