diff mbox series

[ovs-dev,PATCHv4] userspace: Add GTP-U support.

Message ID 1575578611-61481-1-git-send-email-u9012063@gmail.com
State Superseded
Headers show
Series [ovs-dev,PATCHv4] userspace: Add GTP-U support. | expand

Commit Message

William Tu Dec. 5, 2019, 8:43 p.m. UTC
GTP, GPRS Tunneling Protocol, is a group of IP-based communications
protocols used to carry general packet radio service (GPRS) within
GSM, UMTS and LTE networks.  GTP protocol has two parts: Signalling
(GTP-Control, GTP-C) and User data (GTP-User, GTP-U). GTP-C is used
for setting up GTP-U protocol, which is an IP-in-UDP tunneling
protocol. Usually GTP is used in connecting between base station for
radio, Serving Gateway (S-GW), and PDN Gateway (P-GW).

This patch implements GTP-U protocol for userspace datapath,
supporting only required header fields and G-PDU message type.
See spec in:
https://tools.ietf.org/html/draft-hmm-dmm-5g-uplane-analysis-00

Signed-off-by: Yi Yang <yangyi01@inspur.com>
Co-authored-by: Yi Yang <yangyi01@inspur.com>
Signed-off-by: William Tu <u9012063@gmail.com>
---
v3 -> v4:
  - applied Ben's doc revise
  - increment FLOW_WC_SEQ to 42
  - minor fixes
  - travis: https://travis-ci.org/williamtu/ovs-travis/builds/621289678

v2 -> v3:
  - pick up the code from v2, rebase to master
  - many fixes in code, docs, and add more tests
  - travis: https://travis-ci.org/williamtu/ovs-travis/builds/619799361

v1 -> v2:
  - Add new packet_type PT_GTPU_MSG to handle GTP-U signaling message,
    GTP-U signaling message should be steered into a control plane handler
    by explicit openflow entry in flow table.
  - Fix gtpu_flags and gptu_msgtype set issue.
  - Verify communication with kernel GTP implementation from Jiannan
    Ouyang.
  - Fix unit test issue and make sure all the unit tests can pass.
  - This patch series add GTP-U tunnel support in DPDK userspace,
    GTP-U is used for carrying user data within the GPRS core network and
    between the radio access network and the core network. The user data
    transported can be packets in any of IPv4, IPv6, or PPP formats.
---
 Documentation/faq/configuration.rst               |  13 +++
 Documentation/faq/releases.rst                    |   1 +
 NEWS                                              |   3 +
 datapath/linux/compat/include/linux/openvswitch.h |   2 +
 include/openvswitch/flow.h                        |   4 +-
 include/openvswitch/match.h                       |   6 ++
 include/openvswitch/meta-flow.h                   |  28 ++++++
 include/openvswitch/packets.h                     |   4 +-
 lib/dpif-netlink-rtnl.c                           |   5 +
 lib/dpif-netlink.c                                |   5 +
 lib/flow.c                                        |  22 +++--
 lib/flow.h                                        |   2 +-
 lib/match.c                                       |  30 +++++-
 lib/meta-flow.c                                   |  38 ++++++++
 lib/meta-flow.xml                                 |  79 ++++++++++++++-
 lib/netdev-native-tnl.c                           |  85 ++++++++++++++++
 lib/netdev-native-tnl.h                           |   8 ++
 lib/netdev-vport.c                                |  25 ++++-
 lib/nx-match.c                                    |   8 +-
 lib/odp-util.c                                    | 114 +++++++++++++++++++++-
 lib/odp-util.h                                    |   2 +-
 lib/ofp-match.c                                   |   2 +-
 lib/packets.h                                     |  59 +++++++++++
 lib/tnl-ports.c                                   |   3 +
 ofproto/ofproto-dpif-rid.h                        |   2 +-
 ofproto/ofproto-dpif-xlate.c                      |   6 +-
 ofproto/tunnel.c                                  |   3 +-
 tests/ofproto.at                                  |   4 +-
 tests/tunnel-push-pop.at                          |  22 +++++
 tests/tunnel.at                                   |  78 +++++++++++++++
 vswitchd/vswitch.xml                              |  24 +++++
 31 files changed, 658 insertions(+), 29 deletions(-)

Comments

Ben Pfaff Dec. 6, 2019, 4:53 a.m. UTC | #1
On Thu, Dec 05, 2019 at 12:43:31PM -0800, William Tu wrote:
> GTP, GPRS Tunneling Protocol, is a group of IP-based communications
> protocols used to carry general packet radio service (GPRS) within
> GSM, UMTS and LTE networks.  GTP protocol has two parts: Signalling
> (GTP-Control, GTP-C) and User data (GTP-User, GTP-U). GTP-C is used
> for setting up GTP-U protocol, which is an IP-in-UDP tunneling
> protocol. Usually GTP is used in connecting between base station for
> radio, Serving Gateway (S-GW), and PDN Gateway (P-GW).
> 
> This patch implements GTP-U protocol for userspace datapath,
> supporting only required header fields and G-PDU message type.
> See spec in:
> https://tools.ietf.org/html/draft-hmm-dmm-5g-uplane-analysis-00
> 
> Signed-off-by: Yi Yang <yangyi01@inspur.com>
> Co-authored-by: Yi Yang <yangyi01@inspur.com>
> Signed-off-by: William Tu <u9012063@gmail.com>

Thanks a lot!  I made another pass and noticed a few more things.

For some "flags" fields, where it's important to understand the flags,
we break out the flags for user comprehension.  For example, we do this
with TCP flags.  Do you have an idea whether the GTP-U flags are
important enough for this?  If so, then we'd need some additional
changes for parsing and formatting GTP-U flags.  If not, I suggest
changing formatting for them in meta-flow.h from "decimal" to
"hexadecimal" because it's easier to understand bit-mappings from hex to
binary in your head (my suggested patch does that).

I think that flow_get_metadata() should copy the new fields.

These new fields are marked writable.  Is it actually safe to let the
user change the flags field?  It seems to me that all of the bits here
are either fixed or indicate whether some following field is present, so
that changing it could break the interpretation of the packet.  Maybe
the flags should be read-only.

I think that format_flow_tunnel() in lib/match.c should format the new
fields.

The use of "? :" as in netdev_gtpu_build_header() is a GCC extension.  I
think that MSVC will reject it.

Doesn't tun_key_to_attr() need to support the new fields?

I'm pretty suspicious of the treatment of PT_GTPU_MSG packets.  I don't
know what can be done with them.  I bet they have not been tested.

I'm appending a few more minor suggestions.

Thanks again!

Ben

-8<--------------------------cut here-------------------------->8--

diff --git a/Documentation/faq/configuration.rst b/Documentation/faq/configuration.rst
index a9ac9354d0dc..4a98740c5d4d 100644
--- a/Documentation/faq/configuration.rst
+++ b/Documentation/faq/configuration.rst
@@ -227,9 +227,9 @@ Q: Does Open vSwitch support IPv6 GRE?
 
 Q: Does Open vSwitch support GTP-U?
 
-    A: Yes. GTP-U (GPRS Tunnelling Protocol User Plane (GTPv1-U))
-    is supported in userspace datapath. TEID is set by using tunnel
-    key field.
+    A: Yes. Starting with version 2.13, the Open vSwitch userspace
+    datapath supports GTP-U (GPRS Tunnelling Protocol User Plane
+    (GTPv1-U)). TEID is set by using tunnel key field.
 
     ::
 
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index d57a36468e19..b3457f231f9a 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -512,7 +512,7 @@ enum OVS_PACKED_ENUM mf_field_id {
      *
      * Type: u8.
      * Maskable: bitwise.
-     * Formatting: decimal.
+     * Formatting: hexadecimal.
      * Prerequisites: none.
      * Access: read/write.
      * NXM: none.
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 7ae554c87964..bbce5e1f55cb 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -738,7 +738,7 @@ netdev_gtpu_pop_header(struct dp_packet *packet)
 
     tnl->gtpu_flags = gtph->md.flags;
     tnl->gtpu_msgtype = gtph->md.msgtype;
-    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
+    tnl->tun_id = be32_to_be64(get_16aligned_be32(&gtph->teid));
 
     if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
         struct ip_header *ip;
diff --git a/lib/packets.h b/lib/packets.h
index 6a4d3fb87392..405ea9325c21 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1498,12 +1498,14 @@ struct gtpu_metadata {
     uint8_t flags;
     uint8_t msgtype;
 };
+BUILD_ASSERT_DECL(sizeof(struct gtpu_metadata) == 2);
 
 struct gtpuhdr {
     struct gtpu_metadata md;
     ovs_be16 len;
     ovs_16aligned_be32 teid;
 };
+BUILD_ASSERT_DECL(sizeof(struct gtpuhdr) == 8);
 
 /* VXLAN protocol header */
 struct vxlanhdr {
Aaron Conole Dec. 6, 2019, 3:48 p.m. UTC | #2
Ben Pfaff <blp@ovn.org> writes:

> On Thu, Dec 05, 2019 at 12:43:31PM -0800, William Tu wrote:
>> GTP, GPRS Tunneling Protocol, is a group of IP-based communications
>> protocols used to carry general packet radio service (GPRS) within
>> GSM, UMTS and LTE networks.  GTP protocol has two parts: Signalling
>> (GTP-Control, GTP-C) and User data (GTP-User, GTP-U). GTP-C is used
>> for setting up GTP-U protocol, which is an IP-in-UDP tunneling
>> protocol. Usually GTP is used in connecting between base station for
>> radio, Serving Gateway (S-GW), and PDN Gateway (P-GW).
>> 
>> This patch implements GTP-U protocol for userspace datapath,
>> supporting only required header fields and G-PDU message type.
>> See spec in:
>> https://tools.ietf.org/html/draft-hmm-dmm-5g-uplane-analysis-00
>> 
>> Signed-off-by: Yi Yang <yangyi01@inspur.com>
>> Co-authored-by: Yi Yang <yangyi01@inspur.com>
>> Signed-off-by: William Tu <u9012063@gmail.com>
>
> Thanks a lot!  I made another pass and noticed a few more things.
>
> For some "flags" fields, where it's important to understand the flags,
> we break out the flags for user comprehension.  For example, we do this
> with TCP flags.  Do you have an idea whether the GTP-U flags are
> important enough for this?  If so, then we'd need some additional
> changes for parsing and formatting GTP-U flags.  If not, I suggest
> changing formatting for them in meta-flow.h from "decimal" to
> "hexadecimal" because it's easier to understand bit-mappings from hex to
> binary in your head (my suggested patch does that).

CC'd Fouad for this part, too.

My understanding is that the GTP-U flags don't contain session state
information, but are really per-pdu 'special' settings.  For example,
there's no such idea as 'control' with gtp-u (that's all done via gtp-c)
so there isn't much to select on.  Some of the well-known extension
headers might have value as selectors, though (I'm thinking the RAN
container ID extensions, maybe the UDP Port for error path).

I agree with the hex data change.

> I think that flow_get_metadata() should copy the new fields.
>
> These new fields are marked writable.  Is it actually safe to let the
> user change the flags field?  It seems to me that all of the bits here
> are either fixed or indicate whether some following field is present, so
> that changing it could break the interpretation of the packet.  Maybe
> the flags should be read-only.

I can make an argument for the extension bit to be writable only if
there's a corresponding method to push or pop extensions.  I'm not sure
which extensions the SDN should ever push in (maybe UDP Port for error
path) vs. the actual RAN application.

> I think that format_flow_tunnel() in lib/match.c should format the new
> fields.
>
> The use of "? :" as in netdev_gtpu_build_header() is a GCC extension.  I
> think that MSVC will reject it.
>
> Doesn't tun_key_to_attr() need to support the new fields?
>
> I'm pretty suspicious of the treatment of PT_GTPU_MSG packets.  I don't
> know what can be done with them.  I bet they have not been tested.
>
> I'm appending a few more minor suggestions.
>
> Thanks again!
>
> Ben
>
> -8<--------------------------cut here-------------------------->8--
>
> diff --git a/Documentation/faq/configuration.rst b/Documentation/faq/configuration.rst
> index a9ac9354d0dc..4a98740c5d4d 100644
> --- a/Documentation/faq/configuration.rst
> +++ b/Documentation/faq/configuration.rst
> @@ -227,9 +227,9 @@ Q: Does Open vSwitch support IPv6 GRE?
>  
>  Q: Does Open vSwitch support GTP-U?
>  
> -    A: Yes. GTP-U (GPRS Tunnelling Protocol User Plane (GTPv1-U))
> -    is supported in userspace datapath. TEID is set by using tunnel
> -    key field.
> +    A: Yes. Starting with version 2.13, the Open vSwitch userspace
> +    datapath supports GTP-U (GPRS Tunnelling Protocol User Plane
> +    (GTPv1-U)). TEID is set by using tunnel key field.
>  
>      ::
>  
> diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
> index d57a36468e19..b3457f231f9a 100644
> --- a/include/openvswitch/meta-flow.h
> +++ b/include/openvswitch/meta-flow.h
> @@ -512,7 +512,7 @@ enum OVS_PACKED_ENUM mf_field_id {
>       *
>       * Type: u8.
>       * Maskable: bitwise.
> -     * Formatting: decimal.
> +     * Formatting: hexadecimal.
>       * Prerequisites: none.
>       * Access: read/write.
>       * NXM: none.
> diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
> index 7ae554c87964..bbce5e1f55cb 100644
> --- a/lib/netdev-native-tnl.c
> +++ b/lib/netdev-native-tnl.c
> @@ -738,7 +738,7 @@ netdev_gtpu_pop_header(struct dp_packet *packet)
>  
>      tnl->gtpu_flags = gtph->md.flags;
>      tnl->gtpu_msgtype = gtph->md.msgtype;
> -    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
> +    tnl->tun_id = be32_to_be64(get_16aligned_be32(&gtph->teid));
>  
>      if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
>          struct ip_header *ip;
> diff --git a/lib/packets.h b/lib/packets.h
> index 6a4d3fb87392..405ea9325c21 100644
> --- a/lib/packets.h
> +++ b/lib/packets.h
> @@ -1498,12 +1498,14 @@ struct gtpu_metadata {
>      uint8_t flags;
>      uint8_t msgtype;
>  };
> +BUILD_ASSERT_DECL(sizeof(struct gtpu_metadata) == 2);
>  
>  struct gtpuhdr {
>      struct gtpu_metadata md;
>      ovs_be16 len;
>      ovs_16aligned_be32 teid;
>  };
> +BUILD_ASSERT_DECL(sizeof(struct gtpuhdr) == 8);
>  
>  /* VXLAN protocol header */
>  struct vxlanhdr {
William Tu Dec. 6, 2019, 5:26 p.m. UTC | #3
On Thu, Dec 05, 2019 at 08:53:35PM -0800, Ben Pfaff wrote:
> On Thu, Dec 05, 2019 at 12:43:31PM -0800, William Tu wrote:
> > GTP, GPRS Tunneling Protocol, is a group of IP-based communications
> > protocols used to carry general packet radio service (GPRS) within
> > GSM, UMTS and LTE networks.  GTP protocol has two parts: Signalling
> > (GTP-Control, GTP-C) and User data (GTP-User, GTP-U). GTP-C is used
> > for setting up GTP-U protocol, which is an IP-in-UDP tunneling
> > protocol. Usually GTP is used in connecting between base station for
> > radio, Serving Gateway (S-GW), and PDN Gateway (P-GW).
> > 
> > This patch implements GTP-U protocol for userspace datapath,
> > supporting only required header fields and G-PDU message type.
> > See spec in:
> > https://tools.ietf.org/html/draft-hmm-dmm-5g-uplane-analysis-00
> > 
> > Signed-off-by: Yi Yang <yangyi01@inspur.com>
> > Co-authored-by: Yi Yang <yangyi01@inspur.com>
> > Signed-off-by: William Tu <u9012063@gmail.com>
> 
Hi Ben,

Thanks for the review.

> Thanks a lot!  I made another pass and noticed a few more things.
> 
> For some "flags" fields, where it's important to understand the flags,
> we break out the flags for user comprehension.  For example, we do this
> with TCP flags.  Do you have an idea whether the GTP-U flags are
> important enough for this?  If so, then we'd need some additional
> changes for parsing and formatting GTP-U flags.  If not, I suggest
> changing formatting for them in meta-flow.h from "decimal" to
> "hexadecimal" because it's easier to understand bit-mappings from hex to
> binary in your head (my suggested patch does that).

agree. hexadecimal makes more sense.

> 
> I think that flow_get_metadata() should copy the new fields.

Yes, now I understand FLOW_WC_SEQ is to remind you to add code there.

> 
> These new fields are marked writable.  Is it actually safe to let the
> user change the flags field?  It seems to me that all of the bits here
> are either fixed or indicate whether some following field is present, so
> that changing it could break the interpretation of the packet.  Maybe
> the flags should be read-only.

Yes, make sense.
I think both fields tun_gtpu_flags,tun_gtpu_msgtype can be read-only.

> 
> I think that format_flow_tunnel() in lib/match.c should format the new
> fields.

good catch, thanks
> 
> The use of "? :" as in netdev_gtpu_build_header() is a GCC extension.  I
> think that MSVC will reject it.
> 
OK, will fix it

> Doesn't tun_key_to_attr() need to support the new fields?

good catch, thanks
> 
> I'm pretty suspicious of the treatment of PT_GTPU_MSG packets.  I don't
> know what can be done with them.  I bet they have not been tested.
> 

I didn't test it.

Here we only support GTP-U msgtype=255 (G-PDU).
For other GTP-U messages, (ex: echo req/response), users should use
OpenFlow rules to match it and send to their application.


Regards,
William

> I'm appending a few more minor suggestions.
> 
> Thanks again!
> 
> Ben
> 
> -8<--------------------------cut here-------------------------->8--
> 
> diff --git a/Documentation/faq/configuration.rst b/Documentation/faq/configuration.rst
> index a9ac9354d0dc..4a98740c5d4d 100644
> --- a/Documentation/faq/configuration.rst
> +++ b/Documentation/faq/configuration.rst
> @@ -227,9 +227,9 @@ Q: Does Open vSwitch support IPv6 GRE?
>  
>  Q: Does Open vSwitch support GTP-U?
>  
> -    A: Yes. GTP-U (GPRS Tunnelling Protocol User Plane (GTPv1-U))
> -    is supported in userspace datapath. TEID is set by using tunnel
> -    key field.
> +    A: Yes. Starting with version 2.13, the Open vSwitch userspace
> +    datapath supports GTP-U (GPRS Tunnelling Protocol User Plane
> +    (GTPv1-U)). TEID is set by using tunnel key field.
>  
>      ::
>  
> diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
> index d57a36468e19..b3457f231f9a 100644
> --- a/include/openvswitch/meta-flow.h
> +++ b/include/openvswitch/meta-flow.h
> @@ -512,7 +512,7 @@ enum OVS_PACKED_ENUM mf_field_id {
>       *
>       * Type: u8.
>       * Maskable: bitwise.
> -     * Formatting: decimal.
> +     * Formatting: hexadecimal.
>       * Prerequisites: none.
>       * Access: read/write.
>       * NXM: none.
> diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
> index 7ae554c87964..bbce5e1f55cb 100644
> --- a/lib/netdev-native-tnl.c
> +++ b/lib/netdev-native-tnl.c
> @@ -738,7 +738,7 @@ netdev_gtpu_pop_header(struct dp_packet *packet)
>  
>      tnl->gtpu_flags = gtph->md.flags;
>      tnl->gtpu_msgtype = gtph->md.msgtype;
> -    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
> +    tnl->tun_id = be32_to_be64(get_16aligned_be32(&gtph->teid));
>  
>      if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
>          struct ip_header *ip;
> diff --git a/lib/packets.h b/lib/packets.h
> index 6a4d3fb87392..405ea9325c21 100644
> --- a/lib/packets.h
> +++ b/lib/packets.h
> @@ -1498,12 +1498,14 @@ struct gtpu_metadata {
>      uint8_t flags;
>      uint8_t msgtype;
>  };
> +BUILD_ASSERT_DECL(sizeof(struct gtpu_metadata) == 2);
>  
>  struct gtpuhdr {
>      struct gtpu_metadata md;
>      ovs_be16 len;
>      ovs_16aligned_be32 teid;
>  };
> +BUILD_ASSERT_DECL(sizeof(struct gtpuhdr) == 8);
>  
>  /* VXLAN protocol header */
>  struct vxlanhdr {
Roni Bar Yanai Dec. 8, 2019, 8:11 a.m. UTC | #4
Hi William,

GTP-U header size is not constant, you *must* take into account the flags, mainly
 the sequence. The use of sequence in GTP-U is optional but some devices do use
 it.  see from 3GPP definition:
"For PGW, SGW and eNodeB the usage of sequence numbers in G-PDUs is optional, but
 if GTP-U protocol entities in these nodes are relaying G-PDUs to other nodes, they 
 shall relay the sequence numbers as well. An RNC, SGSN or GGSN shall reorder out 
 of sequence T-PDUs when in sequence delivery is required. This is optional"

The assumption that GTP-U is only data is not correct, you have some signaling
for example END MARKER (like you wrote). This message is sent when there is a mobility
 between cells, and the message contains a TEID and indicates that last packet sent from 
the origin cell, to prevent packet re-order as handover might have packets
on the fly. So I would expected that END MARKER will do the exact same 
forwarding as the GTP-U data.

see inline

>-----Original Message-----
>From: dev <ovs-dev-bounces@openvswitch.org> On Behalf Of William Tu
>Sent: Thursday, December 5, 2019 10:44 PM
>To: dev@openvswitch.org
>Subject: [ovs-dev] [PATCHv4] userspace: Add GTP-U support.
>
>GTP, GPRS Tunneling Protocol, is a group of IP-based communications
>protocols used to carry general packet radio service (GPRS) within
>GSM, UMTS and LTE networks.  GTP protocol has two parts: Signalling
>(GTP-Control, GTP-C) and User data (GTP-User, GTP-U). GTP-C is used
>for setting up GTP-U protocol, which is an IP-in-UDP tunneling
>protocol. Usually GTP is used in connecting between base station for
>radio, Serving Gateway (S-GW), and PDN Gateway (P-GW).
>
>This patch implements GTP-U protocol for userspace datapath,
>supporting only required header fields and G-PDU message type.
>See spec in:
>https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftools.ietf.o
>rg%2Fhtml%2Fdraft-hmm-dmm-5g-uplane-analysis-
>00&amp;data=02%7C01%7Croniba%40mellanox.com%7Cb73529a0bea34637f9ed0
>8d779c3f1e4%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0%7C63711175479
>1292921&amp;sdata=tY0GruNTBV9NWZg9gO2Lo4PWQZ1swHkB1AwdEg3AJUE%3
>D&amp;reserved=0
>
>Signed-off-by: Yi Yang <yangyi01@inspur.com>
>Co-authored-by: Yi Yang <yangyi01@inspur.com>
>Signed-off-by: William Tu <u9012063@gmail.com>
>---
>v3 -> v4:
>  - applied Ben's doc revise
>  - increment FLOW_WC_SEQ to 42
>  - minor fixes
>  - travis:
>https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftravis-
>ci.org%2Fwilliamtu%2Fovs-
>travis%2Fbuilds%2F621289678&amp;data=02%7C01%7Croniba%40mellanox.com%
>7Cb73529a0bea34637f9ed08d779c3f1e4%7Ca652971c7d2e4d9ba6a4d149256f461b
>%7C0%7C0%7C637111754791292921&amp;sdata=eYRkIjcPUQqJwBQndsjCgtcPGG1
>l4QFUnAWW4vwPjpI%3D&amp;reserved=0
>
>v2 -> v3:
>  - pick up the code from v2, rebase to master
>  - many fixes in code, docs, and add more tests
>  - travis:
>https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftravis-
>ci.org%2Fwilliamtu%2Fovs-
>travis%2Fbuilds%2F619799361&amp;data=02%7C01%7Croniba%40mellanox.com%
>7Cb73529a0bea34637f9ed08d779c3f1e4%7Ca652971c7d2e4d9ba6a4d149256f461b
>%7C0%7C0%7C637111754791292921&amp;sdata=cu5bdmJ4ydInKt4chxOcGy33K5E
>9BMDzxgk5%2F7Cq%2FtI%3D&amp;reserved=0
>
>v1 -> v2:
>  - Add new packet_type PT_GTPU_MSG to handle GTP-U signaling message,
>    GTP-U signaling message should be steered into a control plane handler
>    by explicit openflow entry in flow table.
>  - Fix gtpu_flags and gptu_msgtype set issue.
>  - Verify communication with kernel GTP implementation from Jiannan
>    Ouyang.
>  - Fix unit test issue and make sure all the unit tests can pass.
>  - This patch series add GTP-U tunnel support in DPDK userspace,
>    GTP-U is used for carrying user data within the GPRS core network and
>    between the radio access network and the core network. The user data
>    transported can be packets in any of IPv4, IPv6, or PPP formats.
>---
> Documentation/faq/configuration.rst               |  13 +++
> Documentation/faq/releases.rst                    |   1 +
> NEWS                                              |   3 +
> datapath/linux/compat/include/linux/openvswitch.h |   2 +
> include/openvswitch/flow.h                        |   4 +-
> include/openvswitch/match.h                       |   6 ++
> include/openvswitch/meta-flow.h                   |  28 ++++++
> include/openvswitch/packets.h                     |   4 +-
> lib/dpif-netlink-rtnl.c                           |   5 +
> lib/dpif-netlink.c                                |   5 +
> lib/flow.c                                        |  22 +++--
> lib/flow.h                                        |   2 +-
> lib/match.c                                       |  30 +++++-
> lib/meta-flow.c                                   |  38 ++++++++
> lib/meta-flow.xml                                 |  79 ++++++++++++++-
> lib/netdev-native-tnl.c                           |  85 ++++++++++++++++
> lib/netdev-native-tnl.h                           |   8 ++
> lib/netdev-vport.c                                |  25 ++++-
> lib/nx-match.c                                    |   8 +-
> lib/odp-util.c                                    | 114 +++++++++++++++++++++-
> lib/odp-util.h                                    |   2 +-
> lib/ofp-match.c                                   |   2 +-
> lib/packets.h                                     |  59 +++++++++++
> lib/tnl-ports.c                                   |   3 +
> ofproto/ofproto-dpif-rid.h                        |   2 +-
> ofproto/ofproto-dpif-xlate.c                      |   6 +-
> ofproto/tunnel.c                                  |   3 +-
> tests/ofproto.at                                  |   4 +-
> tests/tunnel-push-pop.at                          |  22 +++++
> tests/tunnel.at                                   |  78 +++++++++++++++
> vswitchd/vswitch.xml                              |  24 +++++
> 31 files changed, 658 insertions(+), 29 deletions(-)
>
>diff --git a/Documentation/faq/configuration.rst
>b/Documentation/faq/configuration.rst
>index ff3b71a5d4ef..a9ac9354d0dc 100644
>--- a/Documentation/faq/configuration.rst
>+++ b/Documentation/faq/configuration.rst
>@@ -225,6 +225,19 @@ Q: Does Open vSwitch support IPv6 GRE?
>                 options:remote_ip=fc00:100::1 \
>                 options:packet_type=legacy_l2
>
>+Q: Does Open vSwitch support GTP-U?
>+
>+    A: Yes. GTP-U (GPRS Tunnelling Protocol User Plane (GTPv1-U))
>+    is supported in userspace datapath. TEID is set by using tunnel
>+    key field.
>+
>+    ::
>+
>+        $ ovs-vsctl add-br br0
>+        $ ovs-vsctl add-port br0 gtpu0 -- \
>+                set int gtpu0 type=gtpu options:key=<teid> \
>+                options:remote_ip=172.31.1.1
>+
> Q: How do I connect two bridges?
>
>     A: First, why do you want to do this?  Two connected bridges are not much
>diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst
>index 6702c58a2b6f..4fa28a378603 100644
>--- a/Documentation/faq/releases.rst
>+++ b/Documentation/faq/releases.rst
>@@ -130,6 +130,7 @@ Q: Are all features available with all datapaths?
>     Tunnel - Geneve-IPv6            4.4            2.6          2.6      NO
>     Tunnel - ERSPAN                 4.18           2.10         2.10     NO
>     Tunnel - ERSPAN-IPv6            4.18           2.10         2.10     NO
>+    Tunnel - GTP-U                  NO             NO           2.13     NO
>     QoS - Policing                  YES            1.1          2.6      NO
>     QoS - Shaping                   YES            1.1          NO       NO
>     sFlow                           YES            1.0          1.0      NO
>diff --git a/NEWS b/NEWS
>index e2254121f619..96d742f773a2 100644
>--- a/NEWS
>+++ b/NEWS
>@@ -26,6 +26,9 @@ Post-v2.12.0
>      * DPDK ring ports (dpdkr) are deprecated and will be removed in next
>        releases.
>      * Add support for DPDK 19.11.
>+   - GTP-U Tunnel Protocol
>+     * Add two new fields: tun_gtpu_flags, tun_gtpu_msgtype.
>+     * Only support for userspace datapath.
>
> v2.12.0 - 03 Sep 2019
> ---------------------
>diff --git a/datapath/linux/compat/include/linux/openvswitch.h
>b/datapath/linux/compat/include/linux/openvswitch.h
>index 778827f8b5a2..6cc90b5b29ef 100644
>--- a/datapath/linux/compat/include/linux/openvswitch.h
>+++ b/datapath/linux/compat/include/linux/openvswitch.h
>@@ -242,6 +242,7 @@ enum ovs_vport_type {
> 	OVS_VPORT_TYPE_ERSPAN = 107, /* ERSPAN tunnel. */
> 	OVS_VPORT_TYPE_IP6ERSPAN = 108, /* ERSPAN tunnel. */
> 	OVS_VPORT_TYPE_IP6GRE = 109,
>+	OVS_VPORT_TYPE_GTPU = 110,
> 	__OVS_VPORT_TYPE_MAX
> };
>
>@@ -401,6 +402,7 @@ enum ovs_tunnel_key_attr {
> 	OVS_TUNNEL_KEY_ATTR_IPV6_DST,		/* struct in6_addr dst IPv6
>address. */
> 	OVS_TUNNEL_KEY_ATTR_PAD,
> 	OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS,	/* struct erspan_metadata
>*/
>+	OVS_TUNNEL_KEY_ATTR_GTPU_OPTS,		/* struct
>gtpu_metadata */
> 	__OVS_TUNNEL_KEY_ATTR_MAX
> };
>
>diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
>index 57b6c925c7bc..3054015d93c7 100644
>--- a/include/openvswitch/flow.h
>+++ b/include/openvswitch/flow.h
>@@ -27,7 +27,7 @@ extern "C" {
> /* This sequence number should be incremented whenever anything involving
>flows
>  * or the wildcarding of flows changes.  This will cause build assertion
>  * failures in places which likely need to be updated. */
>-#define FLOW_WC_SEQ 41
>+#define FLOW_WC_SEQ 42
>
> /* Number of Open vSwitch extension 32-bit registers. */
> #define FLOW_N_REGS 16
>@@ -168,7 +168,7 @@ BUILD_ASSERT_DECL(sizeof(struct ovs_key_nsh) %
>sizeof(uint64_t) == 0);
> /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
> BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
>                   == sizeof(struct flow_tnl) + sizeof(struct ovs_key_nsh) + 300
>-                  && FLOW_WC_SEQ == 41);
>+                  && FLOW_WC_SEQ == 42);
>
> /* Incremental points at which flow classification may be performed in
>  * segments.
>diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
>index 05ecee7fc7cd..45a368c274b8 100644
>--- a/include/openvswitch/match.h
>+++ b/include/openvswitch/match.h
>@@ -121,6 +121,12 @@ void match_set_tun_erspan_dir_masked(struct match
>*match, uint8_t dir,
> void match_set_tun_erspan_hwid(struct match *match, uint8_t hwid);
> void match_set_tun_erspan_hwid_masked(struct match *match, uint8_t hwid,
>                                       uint8_t mask);
>+void match_set_tun_gtpu_flags(struct match *match, uint8_t flags);
>+void match_set_tun_gtpu_flags_masked(struct match *match, uint8_t flags,
>+                                     uint8_t mask);
>+void match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype);
>+void match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t
>msgtype,
>+                                       uint8_t mask);
> void match_set_in_port(struct match *, ofp_port_t ofp_port);
> void match_set_pkt_mark(struct match *, uint32_t pkt_mark);
> void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t
>mask);
>diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
>index b7fd6cb3f0fb..d57a36468e19 100644
>--- a/include/openvswitch/meta-flow.h
>+++ b/include/openvswitch/meta-flow.h
>@@ -506,6 +506,34 @@ enum OVS_PACKED_ENUM mf_field_id {
>      */
>     MFF_TUN_ERSPAN_HWID,
>
>+    /* "tun_gtpu_flags".
>+     *
>+     * GTP-U tunnel flags.
>+     *
>+     * Type: u8.
>+     * Maskable: bitwise.
>+     * Formatting: decimal.
>+     * Prerequisites: none.
>+     * Access: read/write.
>+     * NXM: none.
>+     * OXM: NXOXM_ET_GTPU_FLAGS(15) since v2.13.
>+     */
>+    MFF_TUN_GTPU_FLAGS,
>+
>+    /* "tun_gtpu_msgtype".
>+     *
>+     * GTP-U tunnel message type.
>+     *
>+     * Type: u8.
>+     * Maskable: bitwise.
>+     * Formatting: decimal.
>+     * Prerequisites: none.
>+     * Access: read/write.
>+     * NXM: none.
>+     * OXM: NXOXM_ET_GTPU_MSGTYPE(16) since v2.13.
>+     */
>+    MFF_TUN_GTPU_MSGTYPE,
>+
> #if TUN_METADATA_NUM_OPTS == 64
>     /* "tun_metadata<N>".
>      *
>diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
>index 925844edae6a..a65cb0d04e77 100644
>--- a/include/openvswitch/packets.h
>+++ b/include/openvswitch/packets.h
>@@ -43,7 +43,9 @@ struct flow_tnl {
>     uint32_t erspan_idx;
>     uint8_t erspan_dir;
>     uint8_t erspan_hwid;
>-    uint8_t pad1[6];     /* Pad to 64 bits. */
>+    uint8_t gtpu_flags;
>+    uint8_t gtpu_msgtype;
>+    uint8_t pad1[4];     /* Pad to 64 bits. */
>     struct tun_metadata metadata;
> };
>
>diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c
>index 582274c46774..fd157ce2d918 100644
>--- a/lib/dpif-netlink-rtnl.c
>+++ b/lib/dpif-netlink-rtnl.c
>@@ -111,6 +111,8 @@ vport_type_to_kind(enum ovs_vport_type type,
>         } else {
>             return NULL;
>         }
>+    case OVS_VPORT_TYPE_GTPU:
>+        return NULL;
>     case OVS_VPORT_TYPE_NETDEV:
>     case OVS_VPORT_TYPE_INTERNAL:
>     case OVS_VPORT_TYPE_LISP:
>@@ -277,6 +279,7 @@ dpif_netlink_rtnl_verify(const struct
>netdev_tunnel_config *tnl_cfg,
>     case OVS_VPORT_TYPE_INTERNAL:
>     case OVS_VPORT_TYPE_LISP:
>     case OVS_VPORT_TYPE_STT:
>+    case OVS_VPORT_TYPE_GTPU:
>     case OVS_VPORT_TYPE_UNSPEC:
>     case __OVS_VPORT_TYPE_MAX:
>     default:
>@@ -358,6 +361,7 @@ dpif_netlink_rtnl_create(const struct
>netdev_tunnel_config *tnl_cfg,
>     case OVS_VPORT_TYPE_INTERNAL:
>     case OVS_VPORT_TYPE_LISP:
>     case OVS_VPORT_TYPE_STT:
>+    case OVS_VPORT_TYPE_GTPU:
>     case OVS_VPORT_TYPE_UNSPEC:
>     case __OVS_VPORT_TYPE_MAX:
>     default:
>@@ -471,6 +475,7 @@ dpif_netlink_rtnl_port_destroy(const char *name, const
>char *type)
>     case OVS_VPORT_TYPE_INTERNAL:
>     case OVS_VPORT_TYPE_LISP:
>     case OVS_VPORT_TYPE_STT:
>+    case OVS_VPORT_TYPE_GTPU:
>     case OVS_VPORT_TYPE_UNSPEC:
>     case __OVS_VPORT_TYPE_MAX:
>     default:
>diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
>index e9a6887f7af2..bc740250e677 100644
>--- a/lib/dpif-netlink.c
>+++ b/lib/dpif-netlink.c
>@@ -703,6 +703,9 @@ get_vport_type(const struct dpif_netlink_vport *vport)
>     case OVS_VPORT_TYPE_IP6GRE:
>         return "ip6gre";
>
>+    case OVS_VPORT_TYPE_GTPU:
>+        return "gtpu";
>+
>     case OVS_VPORT_TYPE_UNSPEC:
>     case __OVS_VPORT_TYPE_MAX:
>         break;
>@@ -736,6 +739,8 @@ netdev_to_ovs_vport_type(const char *type)
>         return OVS_VPORT_TYPE_IP6GRE;
>     } else if (!strcmp(type, "gre")) {
>         return OVS_VPORT_TYPE_GRE;
>+    } else if (!strcmp(type, "gtpu")) {
>+        return OVS_VPORT_TYPE_GTPU;
>     } else {
>         return OVS_VPORT_TYPE_UNSPEC;
>     }
>diff --git a/lib/flow.c b/lib/flow.c
>index 45bb96b543be..9ddb24c15185 100644
>--- a/lib/flow.c
>+++ b/lib/flow.c
>@@ -129,7 +129,7 @@ struct mf_ctx {
>  * away.  Some GCC versions gave warnings on ALWAYS_INLINE, so these are
>  * defined as macros. */
>
>-#if (FLOW_WC_SEQ != 41)
>+#if (FLOW_WC_SEQ != 42)
> #define MINIFLOW_ASSERT(X) ovs_assert(X)
> BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime
>"
>                "assertions enabled. Consider updating FLOW_WC_SEQ after "
>@@ -731,7 +731,7 @@ void
> miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
> {
>     /* Add code to this function (or its callees) to extract new fields. */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     const struct pkt_metadata *md = &packet->md;
>     const void *data = dp_packet_data(packet);
>@@ -1188,7 +1188,7 @@ flow_get_metadata(const struct flow *flow, struct
>match *flow_metadata)
> {
>     int i;
>
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     match_init_catchall(flow_metadata);
>     if (flow->tunnel.tun_id != htonll(0)) {
>@@ -1768,7 +1768,7 @@ flow_wildcards_init_for_packet(struct flow_wildcards
>*wc,
>     memset(&wc->masks, 0x0, sizeof wc->masks);
>
>     /* Update this function whenever struct flow changes. */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     if (flow_tnl_dst_is_set(&flow->tunnel)) {
>         if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
>@@ -1789,6 +1789,8 @@ flow_wildcards_init_for_packet(struct flow_wildcards
>*wc,
>         WC_MASK_FIELD(wc, tunnel.erspan_idx);
>         WC_MASK_FIELD(wc, tunnel.erspan_dir);
>         WC_MASK_FIELD(wc, tunnel.erspan_hwid);
>+        WC_MASK_FIELD(wc, tunnel.gtpu_flags);
>+        WC_MASK_FIELD(wc, tunnel.gtpu_msgtype);
>
>         if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) {
>             if (flow->tunnel.metadata.present.map) {
>@@ -1919,7 +1921,7 @@ void
> flow_wc_map(const struct flow *flow, struct flowmap *map)
> {
>     /* Update this function whenever struct flow changes. */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     flowmap_init(map);
>
>@@ -2022,7 +2024,7 @@ void
> flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
> {
>     /* Update this function whenever struct flow changes. */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
>     memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
>@@ -2166,7 +2168,7 @@ flow_wildcards_set_xxreg_mask(struct flow_wildcards
>*wc, int idx,
> uint32_t
> miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis)
> {
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>     uint32_t hash = basis;
>
>     if (flow) {
>@@ -2213,7 +2215,7 @@ ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst);
> uint32_t
> flow_hash_5tuple(const struct flow *flow, uint32_t basis)
> {
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>     uint32_t hash = basis;
>
>     if (flow) {
>@@ -2891,7 +2893,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16
>mpls_eth_type,
>
>         if (clear_flow_L3) {
>             /* Clear all L3 and L4 fields and dp_hash. */
>-            BUILD_ASSERT(FLOW_WC_SEQ == 41);
>+            BUILD_ASSERT(FLOW_WC_SEQ == 42);
>             memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
>                    sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
>             flow->dp_hash = 0;
>@@ -3189,7 +3191,7 @@ flow_compose(struct dp_packet *p, const struct flow
>*flow,
>     /* Add code to this function (or its callees) for emitting new fields or
>      * protocols.  (This isn't essential, so it can be skipped for initial
>      * testing.) */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     uint32_t pseudo_hdr_csum;
>     size_t l4_len;
>diff --git a/lib/flow.h b/lib/flow.h
>index 75751763c81a..b32f0b27754a 100644
>--- a/lib/flow.h
>+++ b/lib/flow.h
>@@ -964,7 +964,7 @@ static inline void
> pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow)
> {
>     /* Update this function whenever struct flow changes. */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     md->recirc_id = flow->recirc_id;
>     md->dp_hash = flow->dp_hash;
>diff --git a/lib/match.c b/lib/match.c
>index ae568280a2bb..875bd7f66cd2 100644
>--- a/lib/match.c
>+++ b/lib/match.c
>@@ -375,6 +375,34 @@ match_set_tun_erspan_hwid(struct match *match,
>uint8_t hwid)
> }
>
> void
>+match_set_tun_gtpu_flags_masked(struct match *match, uint8_t flags,
>+                                uint8_t mask)
>+{
>+    match->wc.masks.tunnel.gtpu_flags = flags;
>+    match->flow.tunnel.gtpu_flags = flags & mask;
>+}
>+
>+void
>+match_set_tun_gtpu_flags(struct match *match, uint8_t flags)
>+{
>+    match_set_tun_gtpu_flags_masked(match, flags, UINT8_MAX);
>+}
>+
>+void
>+match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t msgtype,
>+                                  uint8_t mask)
>+{
>+    match->wc.masks.tunnel.gtpu_msgtype = msgtype;
>+    match->flow.tunnel.gtpu_msgtype = msgtype & mask;
>+}
>+
>+void
>+match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype)
>+{
>+    match_set_tun_gtpu_msgtype_masked(match, msgtype, UINT8_MAX);
>+}
>+
>+void
> match_set_in_port(struct match *match, ofp_port_t ofp_port)
> {
>     match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
>@@ -1390,7 +1418,7 @@ match_format(const struct match *match,
>     bool is_megaflow = false;
>     int i;
>
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     if (priority != OFP_DEFAULT_PRIORITY) {
>         ds_put_format(s, "%spriority=%s%d,",
>diff --git a/lib/meta-flow.c b/lib/meta-flow.c
>index e3274b97f335..d002344fc8c5 100644
>--- a/lib/meta-flow.c
>+++ b/lib/meta-flow.c
>@@ -391,6 +391,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct
>flow_wildcards *wc)
>     case MFF_NSH_C3:
>     case MFF_NSH_C4:
>         return !wc->masks.nsh.context[mf->id - MFF_NSH_C1];
>+    case MFF_TUN_GTPU_FLAGS:
>+        return !wc->masks.tunnel.gtpu_flags;
>+    case MFF_TUN_GTPU_MSGTYPE:
>+        return !wc->masks.tunnel.gtpu_msgtype;
>
>     case MFF_N_IDS:
>     default:
>@@ -530,6 +534,8 @@ mf_is_value_valid(const struct mf_field *mf, const union
>mf_value *value)
>     case MFF_TUN_ERSPAN_VER:
>     case MFF_TUN_ERSPAN_DIR:
>     case MFF_TUN_ERSPAN_HWID:
>+    case MFF_TUN_GTPU_FLAGS:
>+    case MFF_TUN_GTPU_MSGTYPE:
>     CASE_MFF_TUN_METADATA:
>     case MFF_METADATA:
>     case MFF_IN_PORT:
>@@ -711,6 +717,12 @@ mf_get_value(const struct mf_field *mf, const struct flow
>*flow,
>     case MFF_TUN_ERSPAN_HWID:
>         value->u8 = flow->tunnel.erspan_hwid;
>         break;
>+    case MFF_TUN_GTPU_FLAGS:
>+        value->u8 = flow->tunnel.gtpu_flags;
>+        break;
>+    case MFF_TUN_GTPU_MSGTYPE:
>+        value->u8 = flow->tunnel.gtpu_msgtype;
>+        break;
>     CASE_MFF_TUN_METADATA:
>         tun_metadata_read(&flow->tunnel, mf, value);
>         break;
>@@ -1042,6 +1054,12 @@ mf_set_value(const struct mf_field *mf,
>     case MFF_TUN_ERSPAN_HWID:
>         match_set_tun_erspan_hwid(match, value->u8);
>         break;
>+    case MFF_TUN_GTPU_FLAGS:
>+        match_set_tun_gtpu_flags(match, value->u8);
>+        break;
>+    case MFF_TUN_GTPU_MSGTYPE:
>+        match_set_tun_gtpu_msgtype(match, value->u8);
>+        break;
>     CASE_MFF_TUN_METADATA:
>         tun_metadata_set_match(mf, value, NULL, match, err_str);
>         break;
>@@ -1459,6 +1477,12 @@ mf_set_flow_value(const struct mf_field *mf,
>     case MFF_TUN_ERSPAN_HWID:
>         flow->tunnel.erspan_hwid = value->u8;
>         break;
>+    case MFF_TUN_GTPU_FLAGS:
>+        flow->tunnel.gtpu_flags = value->u8;
>+        break;
>+    case MFF_TUN_GTPU_MSGTYPE:
>+        flow->tunnel.gtpu_msgtype = value->u8;
>+        break;
>     CASE_MFF_TUN_METADATA:
>         tun_metadata_write(&flow->tunnel, mf, value);
>         break;
>@@ -1780,6 +1804,8 @@ mf_is_pipeline_field(const struct mf_field *mf)
>     case MFF_TUN_ERSPAN_IDX:
>     case MFF_TUN_ERSPAN_DIR:
>     case MFF_TUN_ERSPAN_HWID:
>+    case MFF_TUN_GTPU_FLAGS:
>+    case MFF_TUN_GTPU_MSGTYPE:
>     CASE_MFF_TUN_METADATA:
>     case MFF_METADATA:
>     case MFF_IN_PORT:
>@@ -1970,6 +1996,12 @@ mf_set_wild(const struct mf_field *mf, struct match
>*match, char **err_str)
>     case MFF_TUN_ERSPAN_HWID:
>         match_set_tun_erspan_hwid_masked(match, 0, 0);
>         break;
>+    case MFF_TUN_GTPU_FLAGS:
>+        match_set_tun_gtpu_flags_masked(match, 0, 0);
>+        break;
>+    case MFF_TUN_GTPU_MSGTYPE:
>+        match_set_tun_gtpu_msgtype_masked(match, 0, 0);
>+        break;
>     CASE_MFF_TUN_METADATA:
>         tun_metadata_set_match(mf, NULL, NULL, match, err_str);
>         break;
>@@ -2377,6 +2409,12 @@ mf_set(const struct mf_field *mf,
>     case MFF_TUN_ERSPAN_HWID:
>         match_set_tun_erspan_hwid_masked(match, value->u8, mask->u8);
>         break;
>+    case MFF_TUN_GTPU_FLAGS:
>+        match_set_tun_gtpu_flags_masked(match, value->u8, mask->u8);
>+        break;
>+    case MFF_TUN_GTPU_MSGTYPE:
>+        match_set_tun_gtpu_msgtype_masked(match, value->u8, mask->u8);
>+        break;
>     CASE_MFF_TUN_METADATA:
>         tun_metadata_set_match(mf, value, mask, match, err_str);
>         break;
>diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
>index 90b405c73750..ef62bf443679 100644
>--- a/lib/meta-flow.xml
>+++ b/lib/meta-flow.xml
>@@ -1456,7 +1456,8 @@ ovs-ofctl add-flow br-int
>'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
>         <li>LISP has a 24-bit instance ID.</li>
>         <li>GRE has an optional 32-bit key.</li>
>         <li>STT has a 64-bit key.</li>
>-    <li>ERSPAN has a 10-bit key (Session ID).</li>
>+        <li>ERSPAN has a 10-bit key (Session ID).</li>
>+        <li>GTPU has a 32-bit key (Tunnel Endpoint ID).</li>
>       </ul>
>
>       <p>
>@@ -1797,6 +1798,82 @@ ovs-ofctl add-flow br-int
>'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
>       A 6-bit unique identifier of an ERSPAN v2 engine within a system.
>     </field>
>
>+    <h2>GTP-U Metadata Fields</h2>
>+
>+    <p>
>+      These fields provide access to set-up GPRS Tunnelling Protocol
>+      for User Plane (GTPv1-U), based on 3GPP TS 29.281.  A GTP-U
>+      header has the following format:
>+    </p>
>+
>+    <diagram>
>+      <header>
>+        <bits name="flags" above="8" width="0.6"/>
>+        <bits name="msg type" above="8" width="0.6"/>
>+        <bits name="length" above="16" width="0.9"/>
>+        <bits name="TEID" above="32" width="1.3"/>
>+      </header>
>+      <dots/>
>+    </diagram>
>+
>+    <p>
>+      The flags and message type have the Open vSwitch GTP-U specific fields
>+      described below.  Open vSwitch makes the TEID (Tunnel Endpoint
>+      Identifier), which identifies a tunnel endpoint in the receiving GTP-U
>+      protocol entity, available via <ref field="tun_id"/>.
>+    </p>
>+
>+    <field id="MFF_TUN_GTPU_FLAGS" title="GTP-U Flags">
>+      <p>
>+        This field holds the 8-bit GTP-U flags, encoded as:
>+      </p>
>+
>+      <diagram>
>+        <header name="GTP-U Tunnel Flags">
>+          <bits name="version" above="3" below="1" width="0.5"/>
>+          <bits name="PT" above="1" width="0.3"/>
>+          <bits name="rsv" above="1" below="0" width="0.3"/>
>+          <bits name="E" above="1" width="0.3"/>
>+          <bits name="S" above="1" width="0.3"/>
>+          <bits name="PN" above="1" width="0.3"/>
>+        </header>
>+      </diagram>
>+
>+      <p>
>+        The flags are:
>+      </p>
>+      <dl>
>+        <dt>version</dt>
>+        <dd>Used to determine the version of the GTP-U protocol, which should
>+        be set to 1.</dd>
>+
>+        <dt>PT</dt>
>+        <dd>Protocol type, used as a protocol discriminator
>+        between GTP (1) and GTP' (0).</dd>
>+
>+        <dt>rsv</dt>
>+        <dd>Reserved.  Must be zero.</dd>
>+
>+        <dt>E</dt>
>+        <dd>If 1, indicates the presence of a meaningful value of the Next
>+        Extension Header field.</dd>
>+
>+        <dt>S</dt>
>+        <dd>If 1, indicates the presence of a meaningful value of the Sequence
>+        Number field.</dd>
>+
>+        <dt>PN</dt>
>+        <dd>If 1, indicates the presence of a meaningful value of the N-PDU
>+        Number field.</dd>
>+      </dl>
>+    </field>
>+
>+    <field id="MFF_TUN_GTPU_MSGTYPE" title="GTP-U Message Type">
>+      This field indicates whether it's a signalling message used for path
>+      management, or a user plane message which carries the original packet.
>+      The complete range of message types can be referred to [3GPP TS 29.281].
>+    </field>
>+
>     <h2>Geneve Fields</h2>
>
>     <p>
>diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
>index a78972888e33..7ae554c87964 100644
>--- a/lib/netdev-native-tnl.c
>+++ b/lib/netdev-native-tnl.c
>@@ -55,6 +55,9 @@ static struct vlog_rate_limit err_rl =
>VLOG_RATE_LIMIT_INIT(60, 5);
> #define GENEVE_BASE_HLEN   (sizeof(struct udp_header) +         \
>                             sizeof(struct genevehdr))
>
>+#define GTPU_HLEN   (sizeof(struct udp_header) +        \
>+                     sizeof(struct gtpuhdr))
This is the base header length, if S or N-PDU is on you have
to add 4-bytes
>+
> uint16_t tnl_udp_port_min = 32768;
> uint16_t tnl_udp_port_max = 61000;
>
>@@ -708,6 +711,88 @@ netdev_erspan_build_header(const struct netdev
>*netdev,
> }
>
> struct dp_packet *
>+netdev_gtpu_pop_header(struct dp_packet *packet)
>+{
>+    struct pkt_metadata *md = &packet->md;
>+    struct flow_tnl *tnl = &md->tunnel;
>+    struct gtpuhdr *gtph;
>+    unsigned int hlen;
>+
>+    ovs_assert(packet->l3_ofs > 0);
>+    ovs_assert(packet->l4_ofs > 0);
>+
>+    pkt_metadata_init_tnl(md);
>+    if (GTPU_HLEN > dp_packet_l4_size(packet)) {
>+        goto err;
>+    }
>+
>+    gtph = udp_extract_tnl_md(packet, tnl, &hlen);
>+    if (!gtph) {
>+        goto err;
>+    }
>+
>+    if (gtph->md.flags != GTPU_FLAGS_DEFAULT) {
>+        VLOG_WARN_RL(&err_rl, "GTP-U not supported");
>+        goto err;
>+    }
>+
>+    tnl->gtpu_flags = gtph->md.flags;
>+    tnl->gtpu_msgtype = gtph->md.msgtype;
>+    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
>+
>+    if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
>+        struct ip_header *ip;
>+
>+        ip = (struct ip_header *)(gtph + 1);
No always correct, should be additional 4 bytes on sequence, n-pdu
>+        if (IP_VER(ip->ip_ihl_ver) == 4) {
>+            packet->packet_type = htonl(PT_IPV4);
>+        } else if (IP_VER(ip->ip_ihl_ver) == 6) {
>+            packet->packet_type = htonl(PT_IPV6);
>+        } else {
>+            VLOG_WARN_RL(&err_rl, "GTP-U: Receive non-IP packet");
>+        }
>+    } else {
>+        /* non GPDU GTP-U messages, ex: echo request, end marker. */
>+        packet->packet_type = htonl(PT_GTPU_MSG);
>+    }
>+
>+    dp_packet_reset_packet(packet, hlen + GTPU_HLEN);
Why do you want to reset on control such as end marker?
>+
>+    return packet;
>+
>+err:
>+    dp_packet_delete(packet);
>+    return NULL;
>+}
>+
>+int
>+netdev_gtpu_build_header(const struct netdev *netdev,
>+                         struct ovs_action_push_tnl *data,
>+                         const struct netdev_tnl_build_header_params *params)
>+{
>+    struct netdev_vport *dev = netdev_vport_cast(netdev);
>+    struct netdev_tunnel_config *tnl_cfg;
>+    struct gtpuhdr *gtph;
>+
>+    ovs_mutex_lock(&dev->mutex);
>+    tnl_cfg = &dev->tnl_cfg;
>+    gtph = udp_build_header(tnl_cfg, data, params);
>+    ovs_mutex_unlock(&dev->mutex);
>+
>+    /* Set to default if not set in flow. */
>+    gtph->md.flags = params->flow->tunnel.gtpu_flags ? : GTPU_FLAGS_DEFAULT;
>+    gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ? :
>+                       GTPU_MSGTYPE_GPDU;
>+    put_16aligned_be32(&gtph->teid,
>+                       htonl(ntohll(params->flow->tunnel.tun_id)));
>+
>+    data->header_len += sizeof *gtph;
>+    data->tnl_type = OVS_VPORT_TYPE_GTPU;
Note that in some cases, application might need to replace TEID
, if you have tnl_push you need to preserve the origin sequence.

>+
>+    return 0;
>+}
>+
>+struct dp_packet *
> netdev_vxlan_pop_header(struct dp_packet *packet)
> {
>     struct pkt_metadata *md = &packet->md;
>diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h
>index 5dc00122d93e..7567abaebf95 100644
>--- a/lib/netdev-native-tnl.h
>+++ b/lib/netdev-native-tnl.h
>@@ -52,6 +52,14 @@ netdev_erspan_push_header(const struct netdev *netdev,
> struct dp_packet *
> netdev_erspan_pop_header(struct dp_packet *packet);
>
>+int
>+netdev_gtpu_build_header(const struct netdev *netdev,
>+                         struct ovs_action_push_tnl *data,
>+                         const struct netdev_tnl_build_header_params *p);
>+
>+struct dp_packet *
>+netdev_gtpu_pop_header(struct dp_packet *packet);
>+
> void
> netdev_tnl_push_udp_header(const struct netdev *netdev,
>                            struct dp_packet *packet,
>diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
>index b57d21ff8d41..8b8857017986 100644
>--- a/lib/netdev-vport.c
>+++ b/lib/netdev-vport.c
>@@ -111,7 +111,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
>
>     return (class->get_config == get_tunnel_config &&
>             (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
>-             !strcmp("lisp", type) || !strcmp("stt", type)) );
>+             !strcmp("lisp", type) || !strcmp("stt", type) ||
>+             !strcmp("gtpu", type)));
> }
>
> const char *
>@@ -216,6 +217,8 @@ netdev_vport_construct(struct netdev *netdev_)
>         dev->tnl_cfg.dst_port = port ? htons(port) : htons(LISP_DST_PORT);
>     } else if (!strcmp(type, "stt")) {
>         dev->tnl_cfg.dst_port = port ? htons(port) : htons(STT_DST_PORT);
>+    } else if (!strcmp(type, "gtpu")) {
>+        dev->tnl_cfg.dst_port = port ? htons(port) : htons(GTPU_DST_PORT);
>     }
>
>     dev->tnl_cfg.dont_fragment = true;
>@@ -433,6 +436,8 @@ tunnel_supported_layers(const char *type,
>     } else if (!strcmp(type, "vxlan")
>                && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
>         return TNL_L2 | TNL_L3;
>+    } else if (!strcmp(type, "gtpu")) {
>+        return TNL_L3;
>     } else {
>         return TNL_L2;
>     }
>@@ -589,6 +594,10 @@ set_tunnel_config(struct netdev *dev_, const struct
>smap *args, char **errp)
>         tnl_cfg.dst_port = htons(STT_DST_PORT);
>     }
>
>+    if (!strcmp(type, "gtpu")) {
>+        tnl_cfg.dst_port = htons(GTPU_DST_PORT);
>+    }
>+
>     needs_dst_port = netdev_vport_needs_dst_port(dev_);
>     tnl_cfg.dont_fragment = true;
>
>@@ -907,7 +916,8 @@ get_tunnel_config(const struct netdev *dev, struct smap
>*args)
>         if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
>             (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
>             (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
>-            (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
>+            (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
>+            (!strcmp("gtpu", type) && dst_port != GTPU_DST_PORT)) {
>             smap_add_format(args, "dst_port", "%d", dst_port);
>         }
>     }
>@@ -1223,6 +1233,17 @@ netdev_vport_tunnel_register(void)
>           },
>           {{NULL, NULL, 0, 0}}
>         },
>+        { "gtpu_sys",
>+          {
>+              TUNNEL_FUNCTIONS_COMMON,
>+              .type = "gtpu",
>+              .build_header = netdev_gtpu_build_header,
>+              .push_header = netdev_tnl_push_udp_header,
>+              .pop_header = netdev_gtpu_pop_header,
>+          },
>+          {{NULL, NULL, 0, 0}}
>+        },
>+
>     };
>     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
>
>diff --git a/lib/nx-match.c b/lib/nx-match.c
>index 0432ad4de6a7..058816c7b88f 100644
>--- a/lib/nx-match.c
>+++ b/lib/nx-match.c
>@@ -1051,7 +1051,7 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm,
>const struct match *match,
>     ovs_be32 spi_mask;
>     int match_len;
>
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     struct nxm_put_ctx ctx = { .output = b, .implied_ethernet = false };
>
>@@ -1191,6 +1191,12 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm,
>const struct match *match,
>     nxm_put_8m(&ctx, MFF_TUN_ERSPAN_HWID, oxm,
>                 flow->tunnel.erspan_hwid, match->wc.masks.tunnel.erspan_hwid);
>
>+    /* GTP-U */
>+    nxm_put_8m(&ctx, MFF_TUN_GTPU_FLAGS, oxm, flow->tunnel.gtpu_flags,
>+               match->wc.masks.tunnel.gtpu_flags);
>+    nxm_put_8m(&ctx, MFF_TUN_GTPU_MSGTYPE, oxm, flow-
>>tunnel.gtpu_msgtype,
>+               match->wc.masks.tunnel.gtpu_msgtype);
>+
>     /* Network Service Header */
>     nxm_put_8m(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags,
>             match->wc.masks.nsh.flags);
>diff --git a/lib/odp-util.c b/lib/odp-util.c
>index c3be2a59697f..9b835da4a934 100644
>--- a/lib/odp-util.c
>+++ b/lib/odp-util.c
>@@ -755,7 +755,17 @@ format_odp_tnl_push_header(struct ds *ds, struct
>ovs_action_push_tnl *data)
>         } else {
>             VLOG_WARN("%s Invalid ERSPAN version %d\n", __func__, ersh->ver);
>         }
>+    } else if (data->tnl_type == OVS_VPORT_TYPE_GTPU) {
>+        const struct gtpuhdr *gtph;
>+
>+        gtph = format_udp_tnl_push_header(ds, udp);
>+
>+        ds_put_format(ds, "gtpu(flags=0x%"PRIx8
>+                          ",msgtype=0x%"PRIx8",teid=0x%"PRIx32")",
>+                      gtph->md.flags, gtph->md.msgtype,
>+                      ntohl(get_16aligned_be32(&gtph->teid)));
>     }
>+
>     ds_put_format(ds, ")");
> }
>
>@@ -1496,6 +1506,8 @@ ovs_parse_tnl_push(const char *s, struct
>ovs_action_push_tnl *data)
>     void *l3, *l4;
>     int n = 0;
>     uint8_t hwid, dir;
>+    uint32_t teid;
>+    uint8_t gtpu_flags, gtpu_msgtype;
>
>     if (!ovs_scan_len(s, &n, "tnl_push(tnl_port(%"SCNi32"),", &data->tnl_port)) {
>         return -EINVAL;
>@@ -1725,6 +1737,18 @@ ovs_parse_tnl_push(const char *s, struct
>ovs_action_push_tnl *data)
>
>         header_len = sizeof *eth + ip_len + ERSPAN_GREHDR_LEN +
>                      sizeof *ersh + ERSPAN_V2_MDSIZE;
>+
>+    } else if (ovs_scan_len(s, &n, "gtpu(flags=0x%"SCNx8",msgtype=0x%"
>+                SCNx8",teid=0x%"SCNx32"))",
>+                &gtpu_flags, &gtpu_msgtype, &teid)) {
>+        struct gtpuhdr *gtph = (struct gtpuhdr *) (udp + 1);
>+
>+        gtph->md.flags = gtpu_flags;
>+        gtph->md.msgtype = gtpu_msgtype;
>+        put_16aligned_be32(&gtph->teid, htonl(teid));
>+        tnl_type = OVS_VPORT_TYPE_GTPU;
>+        header_len = sizeof *eth + ip_len +
>+                     sizeof *udp + sizeof *gtph;
>     } else {
>         return -EINVAL;
>     }
>@@ -2625,6 +2649,7 @@ static const struct attr_len_tbl
>ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX +
>     [OVS_TUNNEL_KEY_ATTR_IPV6_SRC]      = { .len = 16 },
>     [OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = 16 },
>     [OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS]   = { .len = ATTR_LEN_VARIABLE },
>+    [OVS_TUNNEL_KEY_ATTR_GTPU_OPTS]   = { .len = ATTR_LEN_VARIABLE },
> };
>
> const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
>@@ -3030,6 +3055,13 @@ odp_tun_key_from_attr__(const struct nlattr *attr,
>bool is_mask,
>             }
>             break;
>         }
>+        case OVS_TUNNEL_KEY_ATTR_GTPU_OPTS: {
>+            const struct gtpu_metadata *opts = nl_attr_get(a);
>+
>+            tun->gtpu_flags = opts->flags;
>+            tun->gtpu_msgtype = opts->msgtype;
>+            break;
>+        }
>
>         default:
>             /* Allow this to show up as unexpected, if there are unknown
>@@ -3640,6 +3672,22 @@ format_odp_tun_erspan_opt(const struct nlattr *attr,
>     ds_chomp(ds, ',');
> }
>
>+static void
>+format_odp_tun_gtpu_opt(const struct nlattr *attr,
>+                        const struct nlattr *mask_attr, struct ds *ds,
>+                        bool verbose)
>+{
>+    const struct gtpu_metadata *opts, *mask;
>+
>+    opts = nl_attr_get(attr);
>+    mask = mask_attr ? nl_attr_get(mask_attr) : NULL;
>+
>+    format_u8x(ds, "flags", opts->flags, mask ? &mask->flags : NULL, verbose);
>+    format_u8x(ds, "msgtype", opts->msgtype, mask ? &mask->msgtype : NULL,
>+               verbose);
>+    ds_chomp(ds, ',');
>+}
>+
> #define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
>
> static void
>@@ -3892,6 +3940,11 @@ format_odp_tun_attr(const struct nlattr *attr, const
>struct nlattr *mask_attr,
>             format_odp_tun_erspan_opt(a, ma, ds, verbose);
>             ds_put_cstr(ds, "),");
>             break;
>+        case OVS_TUNNEL_KEY_ATTR_GTPU_OPTS:
>+            ds_put_cstr(ds, "gtpu(");
>+            format_odp_tun_gtpu_opt(a, ma, ds, verbose);
>+            ds_put_cstr(ds, ")");
>+            break;
>         case __OVS_TUNNEL_KEY_ATTR_MAX:
>         default:
>             format_unknown_key(ds, a, ma);
>@@ -5100,6 +5153,50 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t
>*mask)
> }
>
> static int
>+scan_gtpu_metadata(const char *s,
>+                   struct gtpu_metadata *key,
>+                   struct gtpu_metadata *mask)
>+{
>+    const char *s_base = s;
>+    uint8_t flags, flags_ma;
>+    uint8_t msgtype, msgtype_ma;
>+    int len;
>+
>+    if (!strncmp(s, "flags=", 6)) {
>+        s += 6;
>+        len = scan_u8(s, &flags, mask ? &flags_ma : NULL);
>+        if (len == 0) {
>+            return 0;
>+        }
>+        s += len;
>+    }
>+
>+    if (s[0] == ',') {
>+        s++;
>+    }
>+
>+    if (!strncmp(s, "msgtype=", 8)) {
>+        s += 8;
>+        len = scan_u8(s, &msgtype, mask ? &msgtype_ma : NULL);
>+        if (len == 0) {
>+            return 0;
>+        }
>+        s += len;
>+    }
>+
>+    if (!strncmp(s, ")", 1)) {
>+        s += 1;
>+        key->flags = flags;
>+        key->msgtype = msgtype;
>+        if (mask) {
>+            mask->flags = flags_ma;
>+            mask->msgtype = msgtype_ma;
>+        }
>+    }
>+    return s - s_base;
>+}
>+
>+static int
> scan_erspan_metadata(const char *s,
>                      struct erspan_metadata *key,
>                      struct erspan_metadata *mask)
>@@ -5339,6 +5436,15 @@ erspan_to_attr(struct ofpbuf *a, const void *data_)
>                       sizeof *md);
> }
>
>+static void
>+gtpu_to_attr(struct ofpbuf *a, const void *data_)
>+{
>+    const struct gtpu_metadata *md = data_;
>+
>+    nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_GTPU_OPTS, md,
>+                      sizeof *md);
>+}
>+
> #define SCAN_PUT_ATTR(BUF, ATTR, DATA, FUNC)                      \
>     {                                                             \
>         unsigned long call_fn = (unsigned long)FUNC;              \
>@@ -5725,6 +5831,8 @@ parse_odp_key_mask_attr__(struct parse_odp_context
>*context, const char *s,
>         SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp,
>vxlan_gbp_to_attr);
>         SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve,
>                                geneve_to_attr);
>+        SCAN_FIELD_NESTED_FUNC("gtpu(", struct gtpu_metadata, gtpu_metadata,
>+                               gtpu_to_attr);
>         SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
>     } SCAN_END_NESTED();
>
>@@ -5992,7 +6100,7 @@ odp_flow_key_from_flow__(const struct
>odp_flow_key_parms *parms,
>     /* New "struct flow" fields that are visible to the datapath (including all
>      * data fields) should be translated into equivalent datapath flow fields
>      * here (you will have to add a OVS_KEY_ATTR_* for them). */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     struct ovs_key_ethernet *eth_key;
>     size_t encap[FLOW_MAX_VLAN_HEADERS] = {0};
>@@ -7080,7 +7188,7 @@ odp_flow_key_to_flow__(const struct nlattr *key,
>size_t key_len,
>     /* New "struct flow" fields that are visible to the datapath (including all
>      * data fields) should be translated from equivalent datapath flow fields
>      * here (you will have to add a OVS_KEY_ATTR_* for them).  */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     enum odp_key_fitness fitness = ODP_FIT_ERROR;
>     if (errorp) {
>@@ -8429,7 +8537,7 @@ commit_odp_actions(const struct flow *flow, struct flow
>*base,
>     /* If you add a field that OpenFlow actions can change, and that is visible
>      * to the datapath (including all data fields), then you should also add
>      * code here to commit changes to the field. */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     enum slow_path_reason slow1, slow2;
>     bool mpls_done = false;
>diff --git a/lib/odp-util.h b/lib/odp-util.h
>index f15e258e6b4a..8e6bc23a543c 100644
>--- a/lib/odp-util.h
>+++ b/lib/odp-util.h
>@@ -147,7 +147,7 @@ void odp_portno_name_format(const struct hmap
>*portno_names,
>  * add another field and forget to adjust this value.
>  */
> #define ODPUTIL_FLOW_KEY_BYTES 640
>-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
> /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
>  * key.  An array of "struct nlattr" might not, in theory, be sufficiently
>diff --git a/lib/ofp-match.c b/lib/ofp-match.c
>index 2ec28f8036c0..86a082dde141 100644
>--- a/lib/ofp-match.c
>+++ b/lib/ofp-match.c
>@@ -65,7 +65,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask)
> void
> ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
> {
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
>     /* Initialize most of wc. */
>     flow_wildcards_init_catchall(wc);
>diff --git a/lib/packets.h b/lib/packets.h
>index 5d7f82c45b6a..6a4d3fb87392 100644
>--- a/lib/packets.h
>+++ b/lib/packets.h
>@@ -1447,6 +1447,64 @@ static inline ovs_be32 get_erspan_ts(enum
>erspan_ts_gra gra)
>     return ts;
> }
>
>+/*
>+ * GTP-U protocol header and metadata
>+ * See:
>+ *   User Plane Protocol and Architectural Analysis on 3GPP 5G System
>+ *                 draft-hmm-dmm-5g-uplane-analysis-00
>+ *
>+ * 0                   1                   2                   3
>+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
>+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>+ * | Ver |P|R|E|S|N| Message Type|             Length              |
>+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>+ * |                Tunnel Endpoint Identifier                     |
>+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>+ * |      Sequence Number        |   N-PDU Number  |  Next-Ext-Hdr |
>+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
>+ *
>+ * GTP-U Flags:
>+ *   P: Protocol Type (Set to '1')
>+ *   R: Reserved Bit (Set to '0')
>+ *   E: Extension Header Flag (Set to '1' if extension header exists)
>+ *   S: Sequence Number Flag (Set to '1' if sequence number exists)
>+ *   N: N-PDU Number Flag (Set to '1' if N-PDU number exists)
>+ *
>+ * GTP-U Message Type:
>+ *   Indicates the type of GTP-U message.
>+ *
>+ * GTP-U Length:
>+ *   Indicates the length in octets of the payload.
>+ *
>+ * User payload is transmitted in G-PDU packets.
>+ */
>+
>+#define GTPU_VER_MASK   0xe0
>+#define GTPU_P_MASK     0x10
>+#define GTPU_E_MASK     0x04
>+
>+/* GTP-U UDP port. */
>+#define GTPU_DST_PORT   2152
>+
>+/* Default GTP-U flags: Ver = 1 and P = 1. */
>+#define GTPU_FLAGS_DEFAULT  0x30
>+
>+/* GTP-U message type for normal user plane PDU. */
>+#define GTPU_MSGTYPE_REQ    1   /* Echo Request. */
>+#define GTPU_MSGTYPE_REPL   2   /* Echo Reply. */
>+#define GTPU_MSGTYPE_GPDU   255 /* User Payload. */
>+
>+struct gtpu_metadata {
>+    uint8_t flags;
>+    uint8_t msgtype;
>+};
>+
>+struct gtpuhdr {
>+    struct gtpu_metadata md;
>+    ovs_be16 len;
>+    ovs_16aligned_be32 teid;
>+};
>+
> /* VXLAN protocol header */
> struct vxlanhdr {
>     union {
>@@ -1543,6 +1601,7 @@ enum packet_type {
>     PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS),
>     PT_MPLS_MC = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS_MCAST),
>     PT_NSH  = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_NSH),
>+    PT_GTPU_MSG = PACKET_TYPE(OFPHTN_UDP_TCP_PORT, GTPU_DST_PORT),
>     PT_UNKNOWN = PACKET_TYPE(0xffff, 0xffff),   /* Unknown packet type. */
> };
>
>diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
>index 17353046cc6e..446b40763035 100644
>--- a/lib/tnl-ports.c
>+++ b/lib/tnl-ports.c
>@@ -178,6 +178,9 @@ tnl_type_to_nw_proto(const char type[])
>     if (!strcmp(type, "vxlan")) {
>         return IPPROTO_UDP;
>     }
>+    if (!strcmp(type, "gtpu")) {
>+        return IPPROTO_UDP;
>+    }
>     return 0;
> }
>
>diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h
>index 147ef9c33348..e5d02caf28a3 100644
>--- a/ofproto/ofproto-dpif-rid.h
>+++ b/ofproto/ofproto-dpif-rid.h
>@@ -99,7 +99,7 @@ struct rule;
> /* Metadata for restoring pipeline context after recirculation.  Helpers
>  * are inlined below to keep them together with the definition for easier
>  * updates. */
>-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>
> struct frozen_metadata {
>     /* Metadata in struct flow. */
>diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
>index daf7fec80c9e..841bb13df1f9 100644
>--- a/ofproto/ofproto-dpif-xlate.c
>+++ b/ofproto/ofproto-dpif-xlate.c
>@@ -3542,6 +3542,7 @@ propagate_tunnel_data_to_flow(struct xlate_ctx *ctx,
>struct eth_addr dmac,
>         break;
>     case OVS_VPORT_TYPE_VXLAN:
>     case OVS_VPORT_TYPE_GENEVE:
>+    case OVS_VPORT_TYPE_GTPU:
>         nw_proto = IPPROTO_UDP;
>         break;
>     case OVS_VPORT_TYPE_LISP:
>@@ -4093,7 +4094,7 @@ compose_output_action__(struct xlate_ctx *ctx,
>ofp_port_t ofp_port,
>
>     /* If 'struct flow' gets additional metadata, we'll need to zero it out
>      * before traversing a patch port. */
>-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
>+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
>     memset(&flow_tnl, 0, sizeof flow_tnl);
>
>     if (!check_output_prerequisites(ctx, xport, flow, check_stp)) {
>@@ -7581,7 +7582,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out
>*xout)
>     }
>
>     if (flow->packet_type != htonl(PT_ETH) && in_port &&
>-        in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0) {
>+        in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0 &&
>+        flow->packet_type != htonl(PT_GTPU_MSG)) {
>         /* Add dummy Ethernet header to non-L2 packet if it's coming from a
>          * L3 port. So all packets will be L2 packets for lookup.
>          * The dl_type has already been set from the packet_type. */
>diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
>index 03f0ab76562a..6a8bf40f17b1 100644
>--- a/ofproto/tunnel.c
>+++ b/ofproto/tunnel.c
>@@ -585,7 +585,8 @@ tnl_find(const struct flow *flow)
>OVS_REQ_RDLOCK(rwlock)
>                     match.ip_src_flow = ip_src == IP_SRC_FLOW;
>
>                     /* Look for a legacy L2 or L3 tunnel port first. */
>-                    if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
>+                    if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE ||
>+                        flow->packet_type == htonl(PT_GTPU_MSG)) {
>                         match.pt_mode = NETDEV_PT_LEGACY_L3;
>                     } else {
>                         match.pt_mode = NETDEV_PT_LEGACY_L2;
>diff --git a/tests/ofproto.at b/tests/ofproto.at
>index 2436434ca9b7..48260c880dcd 100644
>--- a/tests/ofproto.at
>+++ b/tests/ofproto.at
>@@ -2406,7 +2406,7 @@ head_table () {
>       instructions:
>meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
>       Write-Actions and Apply-Actions features:
>         actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl
>set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
>-        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst
>tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver
>tun_erspan_dir tun_erspan_hwid tun_metadata0 dnl
>+        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst
>tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver
>tun_erspan_dir tun_erspan_hwid tun_gtpu_flags tun_gtpu_msgtype
>tun_metadata0 dnl
> tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5
>tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10
>tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14
>tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18
>tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22
>tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26
>tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30
>tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34
>tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38
>tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42
>tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46
>tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50
>tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54
>tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58
>tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62
>tun_metadata63 dnl
> metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3
>reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1
>xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst
>vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src
>ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa
>arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type
>icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nd_reserved
>nd_options_type nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
>     matching:
>@@ -2426,6 +2426,8 @@ metadata in_port in_port_oxm pkt_mark ct_mark
>ct_label reg0 reg1 reg2 reg3 reg4
>       tun_erspan_ver: arbitrary mask
>       tun_erspan_dir: arbitrary mask
>       tun_erspan_hwid: arbitrary mask
>+      tun_gtpu_flags: arbitrary mask
>+      tun_gtpu_msgtype: arbitrary mask
>       tun_metadata0: arbitrary mask
>       tun_metadata1: arbitrary mask
>       tun_metadata2: arbitrary mask
>diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
>index f7172433ee63..64e77056d5d1 100644
>--- a/tests/tunnel-push-pop.at
>+++ b/tests/tunnel-push-pop.at
>@@ -216,6 +216,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2
>type=vxlan \
>                        options:remote_ip=1.1.2.92 options:key=456
>options:packet_type=legacy_l3 ofport_request=7\
>                     -- add-port int-br t7 -- set Interface t7 type=vxlan \
>                        options:remote_ip=1.1.2.92 options:key=345 options:exts=gpe
>ofport_request=8\
>+                    -- add-port int-br t8 -- set Interface t8 type=gtpu \
>+                       options:remote_ip=1.1.2.92 options:key=123 ofport_request=9\
>                        ], [0])
>
> AT_CHECK([ovs-appctl dpif/show], [0], [dnl
>@@ -232,6 +234,7 @@ dummy@ovs-dummy: hit:0 missed:0
>     t5 6/6081: (geneve: egress_pkt_mark=1234, out_key=flow,
>remote_ip=1.1.2.93)
>     t6 7/3: (gre: key=456, packet_type=legacy_l3, remote_ip=1.1.2.92)
>     t7 8/4789: (vxlan: key=345, remote_ip=1.1.2.92)
>+    t8 9/2152: (gtpu: key=123, remote_ip=1.1.2.92)
> ])
>
> dnl First setup dummy interface IP address, then add the route
>@@ -342,6 +345,7 @@ AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
> Listening ports:
> genev_sys_6081 (6081) ref_cnt=2
> gre_sys (3) ref_cnt=2
>+gtpu_sys_2152 (2152) ref_cnt=1
> vxlan_sys_4789 (4789) ref_cnt=3
> ])
>
>@@ -369,6 +373,13 @@ AT_CHECK([tail -1 stdout], [0],
>   [Datapath actions: tnl_pop(6081)
> ])
>
>+dnl Check GTP-U tunnel pop
>+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy
>'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv
>4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=215
>2)'],
>+[0], [stdout])
>+AT_CHECK([tail -1 stdout], [0],
>+  [Datapath actions: tnl_pop(2152)
>+])
>+
> dnl Check VXLAN tunnel push
> AT_CHECK([ovs-ofctl add-flow int-br action=2])
> AT_CHECK([ovs-appctl ofproto/trace ovs-dummy
>'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv
>4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
>@@ -426,6 +437,15 @@ AT_CHECK([tail -1 stdout], [0],
>   [Datapath actions:
>clone(tnl_push(tnl_port(6081),header(size=58,type=5,eth(dst=f8:bc:12:44:34:b6,s
>rc=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos
>=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x7b,optio
>ns({class=0xffff,type=0x80,len=4,0xa}))),out_port(100)),1)
> ])
>
>+dnl Check GTP-U tunnel push
>+AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:0x30-
>>tun_gtpu_flags,set_field:0xff->tun_gtpu_msgtype,9"])
>+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy
>'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv
>4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'],
>+[0], [stdout])
>+AT_CHECK([tail -1 stdout], [0],
>+  [Datapath actions:
>pop_eth,clone(tnl_push(tnl_port(2152),header(size=50,type=110,eth(dst=f8:bc:1
>2:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,pr
>oto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=2152,csum=0x0),gtpu(flags=0x30,
>msgtype=0xff,teid=0x7b)),out_port(100)),1)
>+])
>+AT_CHECK([ovs-ofctl del-flows int-br])
>+
> dnl Check decapsulation of GRE packet
> AT_CHECK([ovs-appctl netdev-dummy/receive p0
>'aa55aa550000001b213cab6408004500007e79464000402fba550101025c01010258200
>06558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000
>011e00000200004227e75400030af3195500000000f26501000000000010111213141516
>1718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
> AT_CHECK([ovs-appctl netdev-dummy/receive p0
>'aa55aa550000001b213cab6408004500007e79464000402fba550101025c01010258200
>06558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000
>011e00000200004227e75400030af3195500000000f26501000000000010111213141516
>1718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
>@@ -494,6 +514,7 @@ AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
> Listening ports:
> genev_sys_6081 (6081) ref_cnt=1
> gre_sys (3) ref_cnt=1
>+gtpu_sys_2152 (2152) ref_cnt=1
> vxlan_sys_4789 (4789) ref_cnt=2
> vxlan_sys_4790 (4790) ref_cnt=1
> ])
>@@ -503,6 +524,7 @@ AT_CHECK([ovs-vsctl del-port int-br t1 \
>                     -- del-port int-br t4 \
>                     -- del-port int-br t6 \
>                     -- del-port int-br t7 \
>+                    -- del-port int-br t8 \
>                        ], [0])
>
> dnl Check tunnel lookup entries after deleting all remaining tunnel ports
>diff --git a/tests/tunnel.at b/tests/tunnel.at
>index faffb4149c3b..328a7078663c 100644
>--- a/tests/tunnel.at
>+++ b/tests/tunnel.at
>@@ -1029,3 +1029,81 @@ AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 |
>sort], [0], [dnl
>
> OVS_VSWITCHD_STOP
> AT_CLEANUP
>+
>+AT_SETUP([tunnel - GTP-U basic])
>+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gtpu \
>+                    options:remote_ip=1.1.1.1 \
>+                    options:key=123 ofport_request=1])
>+
>+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
>+    br0 65534/100: (dummy-internal)
>+    p1 1/2152: (gtpu: key=123, remote_ip=1.1.1.1)
>+])
>+
>+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
>+Listening ports:
>+gtpu_sys_2152 (2152) ref_cnt=1
>+])
>+
>+OVS_VSWITCHD_STOP
>+AT_CLEANUP
>+
>+AT_SETUP([tunnel - GTP-U and GRE])
>+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \
>+                    ofport_request=1 \
>+                    -- add-port br0 p2 -- set Interface p2 type=dummy \
>+                    ofport_request=2])
>+
>+# Add these ports separately to ensure that they get the datapath port
>+# number expected below.
>+ovs-vsctl -- add-port br0 p3 \
>+          -- set Interface p3 type=gtpu \
>+                              ofport_request=3 \
>+                              options:remote_ip=1.1.1.1 \
>+                              options:key=3 \
>+                              options:packet_type=legacy_l3
>+ovs-vsctl -- add-port br0 p4 \
>+          -- set Interface p4 type=gtpu \
>+                              ofport_request=4 \
>+                              options:remote_ip=1.1.1.2 \
>+                              options:key=4 \
>+                              options:packet_type=legacy_l3
>+ovs-vsctl -- add-port br0 l3gre \
>+          -- set Interface l3gre type=gre \
>+                              ofport_request=5 \
>+                              options:remote_ip=1.1.1.2 \
>+                              options:packet_type=legacy_l3
>+OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
>+
>+dnl AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
>+AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
>+    l3gre 5/3: (gre: packet_type=legacy_l3, remote_ip=1.1.1.2)
>+    p1 1/1: (dummy)
>+    p2 2/2: (dummy)
>+    p3 3/2152: (gtpu: key=3, remote_ip=1.1.1.1)
>+    p4 4/2152: (gtpu: key=4, remote_ip=1.1.1.2)
>+])
>+
>+AT_DATA([flows.txt], [dnl
>+in_port=1,actions=3
>+in_port=2,actions=4
>+in_port=3,tun_gtpu_flags=0x30,tun_gtpu_msgtype=0xff,actions=5
>+in_port=4,tun_gtpu_flags=0x30,tun_gtpu_msgtype=0xff,actions=2
>+])
>+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
>+
>+dnl Encap: in_port=1,actions=3
>+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy
>'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv
>4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,co
>de=0)'], [0], [stdout])
>+AT_CHECK([tail -1 stdout], [0],
>+  [Datapath actions:
>set(tunnel(tun_id=0x3,dst=1.1.1.1,ttl=64,tp_dst=2152,flags(df|key))),pop_eth,21
>52
>+])
>+
>+dnl receive packet from GTP-U port, match it, and output to layer3 GRE
>+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy
>'recirc_id(0),tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,ttl=64,gtpu(flags=0x30,msg
>type=0xff),flags(df|key)),in_port(2152),packet_type(ns=3,id=2152),skb_mark(0),i
>pv4(frag=no)'], [0], [stdout])
>+AT_CHECK([tail -2 stdout], [0],
>+  [Megaflow:
>recirc_id=0,packet_type=(3,0x868),ip,tun_id=0x3,tun_src=1.1.1.1,tun_dst=2.2.2.2,
>tun_tos=0,tun_flags=+df-csum+key,in_port=3
>+Datapath actions: set(tunnel(dst=1.1.1.2,ttl=64,flags(df))),3
>+])
>+
>+OVS_VSWITCHD_STOP
>+AT_CLEANUP
>diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
>index b311ef27826a..c08f3e9c16a5 100644
>--- a/vswitchd/vswitch.xml
>+++ b/vswitchd/vswitch.xml
>@@ -2615,6 +2615,30 @@
>           <dd>
>             A pair of virtual devices that act as a patch cable.
>           </dd>
>+
>+          <dt><code>gtpu</code></dt>
>+          <dd>
>+            <p>
>+            GPRS Tunneling Protocol (GTP) is a group of IP-based communications
>+            protocols used to carry general packet radio service (GPRS) within
>+            GSM, UMTS and LTE networks. GTP-U is used for carrying user data
>+            within the GPRS core network and between the radio access network
>+            and the core network. The user data transported can be packets in
>+            any of IPv4, IPv6, or PPP formats.
>+            </p>
>+
>+            <p>
>+            The protocol is documented at
>+
>https://eur03.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.3gpp.
>org%2FDynaReport%2F29281.htm&amp;data=02%7C01%7Croniba%40mellanox.co
>m%7Cb73529a0bea34637f9ed08d779c3f1e4%7Ca652971c7d2e4d9ba6a4d149256f46
>1b%7C0%7C0%7C637111754791292921&amp;sdata=2PnO5ROFHcZVrkGZNXNOCpy
>gRaaGmMkt2KMhamFfVBg%3D&amp;reserved=0
>+            </p>
>+
>+            <p>
>+            Open vSwitch uses UDP destination port 2152. The source port used
>+            for GTP traffic varies on a per-flow basis and is in the ephemeral
>+            port range.
>+            </p>
>+          </dd>
>+
>         </dl>
>       </column>
>     </group>
>--
>2.7.4
>
>_______________________________________________
>dev mailing list
>dev@openvswitch.org
>https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.open
>vswitch.org%2Fmailman%2Flistinfo%2Fovs-
>dev&amp;data=02%7C01%7Croniba%40mellanox.com%7Cb73529a0bea34637f9ed
>08d779c3f1e4%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0%7C6371117547
>91292921&amp;sdata=UTvsU18TDtWxC0IajC%2B7hwaxXCOPtrJjNJ0LOZIYzkc%3D&
>amp;reserved=0
William Tu Dec. 11, 2019, 12:03 a.m. UTC | #5
On Sun, Dec 08, 2019 at 08:11:19AM +0000, Roni Bar Yanai wrote:
> 
Hi Roni,

Thanks for your feedback!

> GTP-U header size is not constant, you *must* take into account the flags, mainly
>  the sequence. The use of sequence in GTP-U is optional but some devices do use
>  it.  see from 3GPP definition:
> "For PGW, SGW and eNodeB the usage of sequence numbers in G-PDUs is optional, but
>  if GTP-U protocol entities in these nodes are relaying G-PDUs to other nodes, they 
>  shall relay the sequence numbers as well. An RNC, SGSN or GGSN shall reorder out 
>  of sequence T-PDUs when in sequence delivery is required. This is optional"

I see, thanks.
I was reading slide 9 and 10 from
https://www.slideshare.net/aliirfan04/gtp-overview
and I thought sequence number is not needed.

I will add sequence number support.

> 
> The assumption that GTP-U is only data is not correct, you have some signaling
> for example END MARKER (like you wrote). This message is sent when there is a mobility
>  between cells, and the message contains a TEID and indicates that last packet sent from 
> the origin cell, to prevent packet re-order as handover might have packets
> on the fly. So I would expected that END MARKER will do the exact same 
> forwarding as the GTP-U data.

OK, thanks.

> 
> see inline
> 
> >-----Original Message-----
> >From: dev <ovs-dev-bounces@openvswitch.org> On Behalf Of William Tu
> >Sent: Thursday, December 5, 2019 10:44 PM
> >To: dev@openvswitch.org
> >Subject: [ovs-dev] [PATCHv4] userspace: Add GTP-U support.
> >
> >GTP, GPRS Tunneling Protocol, is a group of IP-based communications
> >protocols used to carry general packet radio service (GPRS) within
> >GSM, UMTS and LTE networks.  GTP protocol has two parts: Signalling
> >(GTP-Control, GTP-C) and User data (GTP-User, GTP-U). GTP-C is used
> >for setting up GTP-U protocol, which is an IP-in-UDP tunneling
> >protocol. Usually GTP is used in connecting between base station for
> >radio, Serving Gateway (S-GW), and PDN Gateway (P-GW).
> >
> >This patch implements GTP-U protocol for userspace datapath,
> >supporting only required header fields and G-PDU message type.
> >See spec in:
> >https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftools.ietf.o
> >rg%2Fhtml%2Fdraft-hmm-dmm-5g-uplane-analysis-
> >00&amp;data=02%7C01%7Croniba%40mellanox.com%7Cb73529a0bea34637f9ed0
> >8d779c3f1e4%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0%7C63711175479
> >1292921&amp;sdata=tY0GruNTBV9NWZg9gO2Lo4PWQZ1swHkB1AwdEg3AJUE%3
> >D&amp;reserved=0
> >
> >Signed-off-by: Yi Yang <yangyi01@inspur.com>
> >Co-authored-by: Yi Yang <yangyi01@inspur.com>
> >Signed-off-by: William Tu <u9012063@gmail.com>
> >---
> >v3 -> v4:
> >  - applied Ben's doc revise
> >  - increment FLOW_WC_SEQ to 42
> >  - minor fixes
> >  - travis:
> >https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftravis-
> >ci.org%2Fwilliamtu%2Fovs-
> >travis%2Fbuilds%2F621289678&amp;data=02%7C01%7Croniba%40mellanox.com%
> >7Cb73529a0bea34637f9ed08d779c3f1e4%7Ca652971c7d2e4d9ba6a4d149256f461b
> >%7C0%7C0%7C637111754791292921&amp;sdata=eYRkIjcPUQqJwBQndsjCgtcPGG1
> >l4QFUnAWW4vwPjpI%3D&amp;reserved=0
> >
> >v2 -> v3:
> >  - pick up the code from v2, rebase to master
> >  - many fixes in code, docs, and add more tests
> >  - travis:
> >https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftravis-
> >ci.org%2Fwilliamtu%2Fovs-
> >travis%2Fbuilds%2F619799361&amp;data=02%7C01%7Croniba%40mellanox.com%
> >7Cb73529a0bea34637f9ed08d779c3f1e4%7Ca652971c7d2e4d9ba6a4d149256f461b
> >%7C0%7C0%7C637111754791292921&amp;sdata=cu5bdmJ4ydInKt4chxOcGy33K5E
> >9BMDzxgk5%2F7Cq%2FtI%3D&amp;reserved=0
> >
> >v1 -> v2:
> >  - Add new packet_type PT_GTPU_MSG to handle GTP-U signaling message,
> >    GTP-U signaling message should be steered into a control plane handler
> >    by explicit openflow entry in flow table.
> >  - Fix gtpu_flags and gptu_msgtype set issue.
> >  - Verify communication with kernel GTP implementation from Jiannan
> >    Ouyang.
> >  - Fix unit test issue and make sure all the unit tests can pass.
> >  - This patch series add GTP-U tunnel support in DPDK userspace,
> >    GTP-U is used for carrying user data within the GPRS core network and
> >    between the radio access network and the core network. The user data
> >    transported can be packets in any of IPv4, IPv6, or PPP formats.
> >---
> > Documentation/faq/configuration.rst               |  13 +++
> > Documentation/faq/releases.rst                    |   1 +
> > NEWS                                              |   3 +
> > datapath/linux/compat/include/linux/openvswitch.h |   2 +
> > include/openvswitch/flow.h                        |   4 +-
> > include/openvswitch/match.h                       |   6 ++
> > include/openvswitch/meta-flow.h                   |  28 ++++++
> > include/openvswitch/packets.h                     |   4 +-
> > lib/dpif-netlink-rtnl.c                           |   5 +
> > lib/dpif-netlink.c                                |   5 +
> > lib/flow.c                                        |  22 +++--
> > lib/flow.h                                        |   2 +-
> > lib/match.c                                       |  30 +++++-
> > lib/meta-flow.c                                   |  38 ++++++++
> > lib/meta-flow.xml                                 |  79 ++++++++++++++-
> > lib/netdev-native-tnl.c                           |  85 ++++++++++++++++
> > lib/netdev-native-tnl.h                           |   8 ++
> > lib/netdev-vport.c                                |  25 ++++-
> > lib/nx-match.c                                    |   8 +-
> > lib/odp-util.c                                    | 114 +++++++++++++++++++++-
> > lib/odp-util.h                                    |   2 +-
> > lib/ofp-match.c                                   |   2 +-
> > lib/packets.h                                     |  59 +++++++++++
> > lib/tnl-ports.c                                   |   3 +
> > ofproto/ofproto-dpif-rid.h                        |   2 +-
> > ofproto/ofproto-dpif-xlate.c                      |   6 +-
> > ofproto/tunnel.c                                  |   3 +-
> > tests/ofproto.at                                  |   4 +-
> > tests/tunnel-push-pop.at                          |  22 +++++
> > tests/tunnel.at                                   |  78 +++++++++++++++
> > vswitchd/vswitch.xml                              |  24 +++++
> > 31 files changed, 658 insertions(+), 29 deletions(-)
> >
> >diff --git a/Documentation/faq/configuration.rst
> >b/Documentation/faq/configuration.rst
> >index ff3b71a5d4ef..a9ac9354d0dc 100644
> >--- a/Documentation/faq/configuration.rst
> >+++ b/Documentation/faq/configuration.rst
> >@@ -225,6 +225,19 @@ Q: Does Open vSwitch support IPv6 GRE?
> >                 options:remote_ip=fc00:100::1 \
> >                 options:packet_type=legacy_l2
> >
> >+Q: Does Open vSwitch support GTP-U?
> >+
> >+    A: Yes. GTP-U (GPRS Tunnelling Protocol User Plane (GTPv1-U))
> >+    is supported in userspace datapath. TEID is set by using tunnel
> >+    key field.
> >+
> >+    ::
> >+
> >+        $ ovs-vsctl add-br br0
> >+        $ ovs-vsctl add-port br0 gtpu0 -- \
> >+                set int gtpu0 type=gtpu options:key=<teid> \
> >+                options:remote_ip=172.31.1.1
> >+
> > Q: How do I connect two bridges?
> >
> >     A: First, why do you want to do this?  Two connected bridges are not much
> >diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst
> >index 6702c58a2b6f..4fa28a378603 100644
> >--- a/Documentation/faq/releases.rst
> >+++ b/Documentation/faq/releases.rst
> >@@ -130,6 +130,7 @@ Q: Are all features available with all datapaths?
> >     Tunnel - Geneve-IPv6            4.4            2.6          2.6      NO
> >     Tunnel - ERSPAN                 4.18           2.10         2.10     NO
> >     Tunnel - ERSPAN-IPv6            4.18           2.10         2.10     NO
> >+    Tunnel - GTP-U                  NO             NO           2.13     NO
> >     QoS - Policing                  YES            1.1          2.6      NO
> >     QoS - Shaping                   YES            1.1          NO       NO
> >     sFlow                           YES            1.0          1.0      NO
> >diff --git a/NEWS b/NEWS
> >index e2254121f619..96d742f773a2 100644
> >--- a/NEWS
> >+++ b/NEWS
> >@@ -26,6 +26,9 @@ Post-v2.12.0
> >      * DPDK ring ports (dpdkr) are deprecated and will be removed in next
> >        releases.
> >      * Add support for DPDK 19.11.
> >+   - GTP-U Tunnel Protocol
> >+     * Add two new fields: tun_gtpu_flags, tun_gtpu_msgtype.
> >+     * Only support for userspace datapath.
> >
> > v2.12.0 - 03 Sep 2019
> > ---------------------
> >diff --git a/datapath/linux/compat/include/linux/openvswitch.h
> >b/datapath/linux/compat/include/linux/openvswitch.h
> >index 778827f8b5a2..6cc90b5b29ef 100644
> >--- a/datapath/linux/compat/include/linux/openvswitch.h
> >+++ b/datapath/linux/compat/include/linux/openvswitch.h
> >@@ -242,6 +242,7 @@ enum ovs_vport_type {
> > 	OVS_VPORT_TYPE_ERSPAN = 107, /* ERSPAN tunnel. */
> > 	OVS_VPORT_TYPE_IP6ERSPAN = 108, /* ERSPAN tunnel. */
> > 	OVS_VPORT_TYPE_IP6GRE = 109,
> >+	OVS_VPORT_TYPE_GTPU = 110,
> > 	__OVS_VPORT_TYPE_MAX
> > };
> >
> >@@ -401,6 +402,7 @@ enum ovs_tunnel_key_attr {
> > 	OVS_TUNNEL_KEY_ATTR_IPV6_DST,		/* struct in6_addr dst IPv6
> >address. */
> > 	OVS_TUNNEL_KEY_ATTR_PAD,
> > 	OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS,	/* struct erspan_metadata
> >*/
> >+	OVS_TUNNEL_KEY_ATTR_GTPU_OPTS,		/* struct
> >gtpu_metadata */
> > 	__OVS_TUNNEL_KEY_ATTR_MAX
> > };
> >
> >diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
> >index 57b6c925c7bc..3054015d93c7 100644
> >--- a/include/openvswitch/flow.h
> >+++ b/include/openvswitch/flow.h
> >@@ -27,7 +27,7 @@ extern "C" {
> > /* This sequence number should be incremented whenever anything involving
> >flows
> >  * or the wildcarding of flows changes.  This will cause build assertion
> >  * failures in places which likely need to be updated. */
> >-#define FLOW_WC_SEQ 41
> >+#define FLOW_WC_SEQ 42
> >
> > /* Number of Open vSwitch extension 32-bit registers. */
> > #define FLOW_N_REGS 16
> >@@ -168,7 +168,7 @@ BUILD_ASSERT_DECL(sizeof(struct ovs_key_nsh) %
> >sizeof(uint64_t) == 0);
> > /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
> > BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
> >                   == sizeof(struct flow_tnl) + sizeof(struct ovs_key_nsh) + 300
> >-                  && FLOW_WC_SEQ == 41);
> >+                  && FLOW_WC_SEQ == 42);
> >
> > /* Incremental points at which flow classification may be performed in
> >  * segments.
> >diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
> >index 05ecee7fc7cd..45a368c274b8 100644
> >--- a/include/openvswitch/match.h
> >+++ b/include/openvswitch/match.h
> >@@ -121,6 +121,12 @@ void match_set_tun_erspan_dir_masked(struct match
> >*match, uint8_t dir,
> > void match_set_tun_erspan_hwid(struct match *match, uint8_t hwid);
> > void match_set_tun_erspan_hwid_masked(struct match *match, uint8_t hwid,
> >                                       uint8_t mask);
> >+void match_set_tun_gtpu_flags(struct match *match, uint8_t flags);
> >+void match_set_tun_gtpu_flags_masked(struct match *match, uint8_t flags,
> >+                                     uint8_t mask);
> >+void match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype);
> >+void match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t
> >msgtype,
> >+                                       uint8_t mask);
> > void match_set_in_port(struct match *, ofp_port_t ofp_port);
> > void match_set_pkt_mark(struct match *, uint32_t pkt_mark);
> > void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t
> >mask);
> >diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
> >index b7fd6cb3f0fb..d57a36468e19 100644
> >--- a/include/openvswitch/meta-flow.h
> >+++ b/include/openvswitch/meta-flow.h
> >@@ -506,6 +506,34 @@ enum OVS_PACKED_ENUM mf_field_id {
> >      */
> >     MFF_TUN_ERSPAN_HWID,
> >
> >+    /* "tun_gtpu_flags".
> >+     *
> >+     * GTP-U tunnel flags.
> >+     *
> >+     * Type: u8.
> >+     * Maskable: bitwise.
> >+     * Formatting: decimal.
> >+     * Prerequisites: none.
> >+     * Access: read/write.
> >+     * NXM: none.
> >+     * OXM: NXOXM_ET_GTPU_FLAGS(15) since v2.13.
> >+     */
> >+    MFF_TUN_GTPU_FLAGS,
> >+
> >+    /* "tun_gtpu_msgtype".
> >+     *
> >+     * GTP-U tunnel message type.
> >+     *
> >+     * Type: u8.
> >+     * Maskable: bitwise.
> >+     * Formatting: decimal.
> >+     * Prerequisites: none.
> >+     * Access: read/write.
> >+     * NXM: none.
> >+     * OXM: NXOXM_ET_GTPU_MSGTYPE(16) since v2.13.
> >+     */
> >+    MFF_TUN_GTPU_MSGTYPE,
> >+
> > #if TUN_METADATA_NUM_OPTS == 64
> >     /* "tun_metadata<N>".
> >      *
> >diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
> >index 925844edae6a..a65cb0d04e77 100644
> >--- a/include/openvswitch/packets.h
> >+++ b/include/openvswitch/packets.h
> >@@ -43,7 +43,9 @@ struct flow_tnl {
> >     uint32_t erspan_idx;
> >     uint8_t erspan_dir;
> >     uint8_t erspan_hwid;
> >-    uint8_t pad1[6];     /* Pad to 64 bits. */
> >+    uint8_t gtpu_flags;
> >+    uint8_t gtpu_msgtype;
> >+    uint8_t pad1[4];     /* Pad to 64 bits. */
> >     struct tun_metadata metadata;
> > };
> >
> >diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c
> >index 582274c46774..fd157ce2d918 100644
> >--- a/lib/dpif-netlink-rtnl.c
> >+++ b/lib/dpif-netlink-rtnl.c
> >@@ -111,6 +111,8 @@ vport_type_to_kind(enum ovs_vport_type type,
> >         } else {
> >             return NULL;
> >         }
> >+    case OVS_VPORT_TYPE_GTPU:
> >+        return NULL;
> >     case OVS_VPORT_TYPE_NETDEV:
> >     case OVS_VPORT_TYPE_INTERNAL:
> >     case OVS_VPORT_TYPE_LISP:
> >@@ -277,6 +279,7 @@ dpif_netlink_rtnl_verify(const struct
> >netdev_tunnel_config *tnl_cfg,
> >     case OVS_VPORT_TYPE_INTERNAL:
> >     case OVS_VPORT_TYPE_LISP:
> >     case OVS_VPORT_TYPE_STT:
> >+    case OVS_VPORT_TYPE_GTPU:
> >     case OVS_VPORT_TYPE_UNSPEC:
> >     case __OVS_VPORT_TYPE_MAX:
> >     default:
> >@@ -358,6 +361,7 @@ dpif_netlink_rtnl_create(const struct
> >netdev_tunnel_config *tnl_cfg,
> >     case OVS_VPORT_TYPE_INTERNAL:
> >     case OVS_VPORT_TYPE_LISP:
> >     case OVS_VPORT_TYPE_STT:
> >+    case OVS_VPORT_TYPE_GTPU:
> >     case OVS_VPORT_TYPE_UNSPEC:
> >     case __OVS_VPORT_TYPE_MAX:
> >     default:
> >@@ -471,6 +475,7 @@ dpif_netlink_rtnl_port_destroy(const char *name, const
> >char *type)
> >     case OVS_VPORT_TYPE_INTERNAL:
> >     case OVS_VPORT_TYPE_LISP:
> >     case OVS_VPORT_TYPE_STT:
> >+    case OVS_VPORT_TYPE_GTPU:
> >     case OVS_VPORT_TYPE_UNSPEC:
> >     case __OVS_VPORT_TYPE_MAX:
> >     default:
> >diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
> >index e9a6887f7af2..bc740250e677 100644
> >--- a/lib/dpif-netlink.c
> >+++ b/lib/dpif-netlink.c
> >@@ -703,6 +703,9 @@ get_vport_type(const struct dpif_netlink_vport *vport)
> >     case OVS_VPORT_TYPE_IP6GRE:
> >         return "ip6gre";
> >
> >+    case OVS_VPORT_TYPE_GTPU:
> >+        return "gtpu";
> >+
> >     case OVS_VPORT_TYPE_UNSPEC:
> >     case __OVS_VPORT_TYPE_MAX:
> >         break;
> >@@ -736,6 +739,8 @@ netdev_to_ovs_vport_type(const char *type)
> >         return OVS_VPORT_TYPE_IP6GRE;
> >     } else if (!strcmp(type, "gre")) {
> >         return OVS_VPORT_TYPE_GRE;
> >+    } else if (!strcmp(type, "gtpu")) {
> >+        return OVS_VPORT_TYPE_GTPU;
> >     } else {
> >         return OVS_VPORT_TYPE_UNSPEC;
> >     }
> >diff --git a/lib/flow.c b/lib/flow.c
> >index 45bb96b543be..9ddb24c15185 100644
> >--- a/lib/flow.c
> >+++ b/lib/flow.c
> >@@ -129,7 +129,7 @@ struct mf_ctx {
> >  * away.  Some GCC versions gave warnings on ALWAYS_INLINE, so these are
> >  * defined as macros. */
> >
> >-#if (FLOW_WC_SEQ != 41)
> >+#if (FLOW_WC_SEQ != 42)
> > #define MINIFLOW_ASSERT(X) ovs_assert(X)
> > BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime
> >"
> >                "assertions enabled. Consider updating FLOW_WC_SEQ after "
> >@@ -731,7 +731,7 @@ void
> > miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
> > {
> >     /* Add code to this function (or its callees) to extract new fields. */
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >
> >     const struct pkt_metadata *md = &packet->md;
> >     const void *data = dp_packet_data(packet);
> >@@ -1188,7 +1188,7 @@ flow_get_metadata(const struct flow *flow, struct
> >match *flow_metadata)
> > {
> >     int i;
> >
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >
> >     match_init_catchall(flow_metadata);
> >     if (flow->tunnel.tun_id != htonll(0)) {
> >@@ -1768,7 +1768,7 @@ flow_wildcards_init_for_packet(struct flow_wildcards
> >*wc,
> >     memset(&wc->masks, 0x0, sizeof wc->masks);
> >
> >     /* Update this function whenever struct flow changes. */
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >
> >     if (flow_tnl_dst_is_set(&flow->tunnel)) {
> >         if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
> >@@ -1789,6 +1789,8 @@ flow_wildcards_init_for_packet(struct flow_wildcards
> >*wc,
> >         WC_MASK_FIELD(wc, tunnel.erspan_idx);
> >         WC_MASK_FIELD(wc, tunnel.erspan_dir);
> >         WC_MASK_FIELD(wc, tunnel.erspan_hwid);
> >+        WC_MASK_FIELD(wc, tunnel.gtpu_flags);
> >+        WC_MASK_FIELD(wc, tunnel.gtpu_msgtype);
> >
> >         if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) {
> >             if (flow->tunnel.metadata.present.map) {
> >@@ -1919,7 +1921,7 @@ void
> > flow_wc_map(const struct flow *flow, struct flowmap *map)
> > {
> >     /* Update this function whenever struct flow changes. */
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >
> >     flowmap_init(map);
> >
> >@@ -2022,7 +2024,7 @@ void
> > flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
> > {
> >     /* Update this function whenever struct flow changes. */
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >
> >     memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
> >     memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
> >@@ -2166,7 +2168,7 @@ flow_wildcards_set_xxreg_mask(struct flow_wildcards
> >*wc, int idx,
> > uint32_t
> > miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis)
> > {
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >     uint32_t hash = basis;
> >
> >     if (flow) {
> >@@ -2213,7 +2215,7 @@ ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst);
> > uint32_t
> > flow_hash_5tuple(const struct flow *flow, uint32_t basis)
> > {
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >     uint32_t hash = basis;
> >
> >     if (flow) {
> >@@ -2891,7 +2893,7 @@ flow_push_mpls(struct flow *flow, int n, ovs_be16
> >mpls_eth_type,
> >
> >         if (clear_flow_L3) {
> >             /* Clear all L3 and L4 fields and dp_hash. */
> >-            BUILD_ASSERT(FLOW_WC_SEQ == 41);
> >+            BUILD_ASSERT(FLOW_WC_SEQ == 42);
> >             memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
> >                    sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
> >             flow->dp_hash = 0;
> >@@ -3189,7 +3191,7 @@ flow_compose(struct dp_packet *p, const struct flow
> >*flow,
> >     /* Add code to this function (or its callees) for emitting new fields or
> >      * protocols.  (This isn't essential, so it can be skipped for initial
> >      * testing.) */
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >
> >     uint32_t pseudo_hdr_csum;
> >     size_t l4_len;
> >diff --git a/lib/flow.h b/lib/flow.h
> >index 75751763c81a..b32f0b27754a 100644
> >--- a/lib/flow.h
> >+++ b/lib/flow.h
> >@@ -964,7 +964,7 @@ static inline void
> > pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow)
> > {
> >     /* Update this function whenever struct flow changes. */
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >
> >     md->recirc_id = flow->recirc_id;
> >     md->dp_hash = flow->dp_hash;
> >diff --git a/lib/match.c b/lib/match.c
> >index ae568280a2bb..875bd7f66cd2 100644
> >--- a/lib/match.c
> >+++ b/lib/match.c
> >@@ -375,6 +375,34 @@ match_set_tun_erspan_hwid(struct match *match,
> >uint8_t hwid)
> > }
> >
> > void
> >+match_set_tun_gtpu_flags_masked(struct match *match, uint8_t flags,
> >+                                uint8_t mask)
> >+{
> >+    match->wc.masks.tunnel.gtpu_flags = flags;
> >+    match->flow.tunnel.gtpu_flags = flags & mask;
> >+}
> >+
> >+void
> >+match_set_tun_gtpu_flags(struct match *match, uint8_t flags)
> >+{
> >+    match_set_tun_gtpu_flags_masked(match, flags, UINT8_MAX);
> >+}
> >+
> >+void
> >+match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t msgtype,
> >+                                  uint8_t mask)
> >+{
> >+    match->wc.masks.tunnel.gtpu_msgtype = msgtype;
> >+    match->flow.tunnel.gtpu_msgtype = msgtype & mask;
> >+}
> >+
> >+void
> >+match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype)
> >+{
> >+    match_set_tun_gtpu_msgtype_masked(match, msgtype, UINT8_MAX);
> >+}
> >+
> >+void
> > match_set_in_port(struct match *match, ofp_port_t ofp_port)
> > {
> >     match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
> >@@ -1390,7 +1418,7 @@ match_format(const struct match *match,
> >     bool is_megaflow = false;
> >     int i;
> >
> >-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
> >+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
> >
> >     if (priority != OFP_DEFAULT_PRIORITY) {
> >         ds_put_format(s, "%spriority=%s%d,",
> >diff --git a/lib/meta-flow.c b/lib/meta-flow.c
> >index e3274b97f335..d002344fc8c5 100644
> >--- a/lib/meta-flow.c
> >+++ b/lib/meta-flow.c
> >@@ -391,6 +391,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct
> >flow_wildcards *wc)
> >     case MFF_NSH_C3:
> >     case MFF_NSH_C4:
> >         return !wc->masks.nsh.context[mf->id - MFF_NSH_C1];
> >+    case MFF_TUN_GTPU_FLAGS:
> >+        return !wc->masks.tunnel.gtpu_flags;
> >+    case MFF_TUN_GTPU_MSGTYPE:
> >+        return !wc->masks.tunnel.gtpu_msgtype;
> >
> >     case MFF_N_IDS:
> >     default:
> >@@ -530,6 +534,8 @@ mf_is_value_valid(const struct mf_field *mf, const union
> >mf_value *value)
> >     case MFF_TUN_ERSPAN_VER:
> >     case MFF_TUN_ERSPAN_DIR:
> >     case MFF_TUN_ERSPAN_HWID:
> >+    case MFF_TUN_GTPU_FLAGS:
> >+    case MFF_TUN_GTPU_MSGTYPE:
> >     CASE_MFF_TUN_METADATA:
> >     case MFF_METADATA:
> >     case MFF_IN_PORT:
> >@@ -711,6 +717,12 @@ mf_get_value(const struct mf_field *mf, const struct flow
> >*flow,
> >     case MFF_TUN_ERSPAN_HWID:
> >         value->u8 = flow->tunnel.erspan_hwid;
> >         break;
> >+    case MFF_TUN_GTPU_FLAGS:
> >+        value->u8 = flow->tunnel.gtpu_flags;
> >+        break;
> >+    case MFF_TUN_GTPU_MSGTYPE:
> >+        value->u8 = flow->tunnel.gtpu_msgtype;
> >+        break;
> >     CASE_MFF_TUN_METADATA:
> >         tun_metadata_read(&flow->tunnel, mf, value);
> >         break;
> >@@ -1042,6 +1054,12 @@ mf_set_value(const struct mf_field *mf,
> >     case MFF_TUN_ERSPAN_HWID:
> >         match_set_tun_erspan_hwid(match, value->u8);
> >         break;
> >+    case MFF_TUN_GTPU_FLAGS:
> >+        match_set_tun_gtpu_flags(match, value->u8);
> >+        break;
> >+    case MFF_TUN_GTPU_MSGTYPE:
> >+        match_set_tun_gtpu_msgtype(match, value->u8);
> >+        break;
> >     CASE_MFF_TUN_METADATA:
> >         tun_metadata_set_match(mf, value, NULL, match, err_str);
> >         break;
> >@@ -1459,6 +1477,12 @@ mf_set_flow_value(const struct mf_field *mf,
> >     case MFF_TUN_ERSPAN_HWID:
> >         flow->tunnel.erspan_hwid = value->u8;
> >         break;
> >+    case MFF_TUN_GTPU_FLAGS:
> >+        flow->tunnel.gtpu_flags = value->u8;
> >+        break;
> >+    case MFF_TUN_GTPU_MSGTYPE:
> >+        flow->tunnel.gtpu_msgtype = value->u8;
> >+        break;
> >     CASE_MFF_TUN_METADATA:
> >         tun_metadata_write(&flow->tunnel, mf, value);
> >         break;
> >@@ -1780,6 +1804,8 @@ mf_is_pipeline_field(const struct mf_field *mf)
> >     case MFF_TUN_ERSPAN_IDX:
> >     case MFF_TUN_ERSPAN_DIR:
> >     case MFF_TUN_ERSPAN_HWID:
> >+    case MFF_TUN_GTPU_FLAGS:
> >+    case MFF_TUN_GTPU_MSGTYPE:
> >     CASE_MFF_TUN_METADATA:
> >     case MFF_METADATA:
> >     case MFF_IN_PORT:
> >@@ -1970,6 +1996,12 @@ mf_set_wild(const struct mf_field *mf, struct match
> >*match, char **err_str)
> >     case MFF_TUN_ERSPAN_HWID:
> >         match_set_tun_erspan_hwid_masked(match, 0, 0);
> >         break;
> >+    case MFF_TUN_GTPU_FLAGS:
> >+        match_set_tun_gtpu_flags_masked(match, 0, 0);
> >+        break;
> >+    case MFF_TUN_GTPU_MSGTYPE:
> >+        match_set_tun_gtpu_msgtype_masked(match, 0, 0);
> >+        break;
> >     CASE_MFF_TUN_METADATA:
> >         tun_metadata_set_match(mf, NULL, NULL, match, err_str);
> >         break;
> >@@ -2377,6 +2409,12 @@ mf_set(const struct mf_field *mf,
> >     case MFF_TUN_ERSPAN_HWID:
> >         match_set_tun_erspan_hwid_masked(match, value->u8, mask->u8);
> >         break;
> >+    case MFF_TUN_GTPU_FLAGS:
> >+        match_set_tun_gtpu_flags_masked(match, value->u8, mask->u8);
> >+        break;
> >+    case MFF_TUN_GTPU_MSGTYPE:
> >+        match_set_tun_gtpu_msgtype_masked(match, value->u8, mask->u8);
> >+        break;
> >     CASE_MFF_TUN_METADATA:
> >         tun_metadata_set_match(mf, value, mask, match, err_str);
> >         break;
> >diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
> >index 90b405c73750..ef62bf443679 100644
> >--- a/lib/meta-flow.xml
> >+++ b/lib/meta-flow.xml
> >@@ -1456,7 +1456,8 @@ ovs-ofctl add-flow br-int
> >'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
> >         <li>LISP has a 24-bit instance ID.</li>
> >         <li>GRE has an optional 32-bit key.</li>
> >         <li>STT has a 64-bit key.</li>
> >-    <li>ERSPAN has a 10-bit key (Session ID).</li>
> >+        <li>ERSPAN has a 10-bit key (Session ID).</li>
> >+        <li>GTPU has a 32-bit key (Tunnel Endpoint ID).</li>
> >       </ul>
> >
> >       <p>
> >@@ -1797,6 +1798,82 @@ ovs-ofctl add-flow br-int
> >'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
> >       A 6-bit unique identifier of an ERSPAN v2 engine within a system.
> >     </field>
> >
> >+    <h2>GTP-U Metadata Fields</h2>
> >+
> >+    <p>
> >+      These fields provide access to set-up GPRS Tunnelling Protocol
> >+      for User Plane (GTPv1-U), based on 3GPP TS 29.281.  A GTP-U
> >+      header has the following format:
> >+    </p>
> >+
> >+    <diagram>
> >+      <header>
> >+        <bits name="flags" above="8" width="0.6"/>
> >+        <bits name="msg type" above="8" width="0.6"/>
> >+        <bits name="length" above="16" width="0.9"/>
> >+        <bits name="TEID" above="32" width="1.3"/>
> >+      </header>
> >+      <dots/>
> >+    </diagram>
> >+
> >+    <p>
> >+      The flags and message type have the Open vSwitch GTP-U specific fields
> >+      described below.  Open vSwitch makes the TEID (Tunnel Endpoint
> >+      Identifier), which identifies a tunnel endpoint in the receiving GTP-U
> >+      protocol entity, available via <ref field="tun_id"/>.
> >+    </p>
> >+
> >+    <field id="MFF_TUN_GTPU_FLAGS" title="GTP-U Flags">
> >+      <p>
> >+        This field holds the 8-bit GTP-U flags, encoded as:
> >+      </p>
> >+
> >+      <diagram>
> >+        <header name="GTP-U Tunnel Flags">
> >+          <bits name="version" above="3" below="1" width="0.5"/>
> >+          <bits name="PT" above="1" width="0.3"/>
> >+          <bits name="rsv" above="1" below="0" width="0.3"/>
> >+          <bits name="E" above="1" width="0.3"/>
> >+          <bits name="S" above="1" width="0.3"/>
> >+          <bits name="PN" above="1" width="0.3"/>
> >+        </header>
> >+      </diagram>
> >+
> >+      <p>
> >+        The flags are:
> >+      </p>
> >+      <dl>
> >+        <dt>version</dt>
> >+        <dd>Used to determine the version of the GTP-U protocol, which should
> >+        be set to 1.</dd>
> >+
> >+        <dt>PT</dt>
> >+        <dd>Protocol type, used as a protocol discriminator
> >+        between GTP (1) and GTP' (0).</dd>
> >+
> >+        <dt>rsv</dt>
> >+        <dd>Reserved.  Must be zero.</dd>
> >+
> >+        <dt>E</dt>
> >+        <dd>If 1, indicates the presence of a meaningful value of the Next
> >+        Extension Header field.</dd>
> >+
> >+        <dt>S</dt>
> >+        <dd>If 1, indicates the presence of a meaningful value of the Sequence
> >+        Number field.</dd>
> >+
> >+        <dt>PN</dt>
> >+        <dd>If 1, indicates the presence of a meaningful value of the N-PDU
> >+        Number field.</dd>
> >+      </dl>
> >+    </field>
> >+
> >+    <field id="MFF_TUN_GTPU_MSGTYPE" title="GTP-U Message Type">
> >+      This field indicates whether it's a signalling message used for path
> >+      management, or a user plane message which carries the original packet.
> >+      The complete range of message types can be referred to [3GPP TS 29.281].
> >+    </field>
> >+
> >     <h2>Geneve Fields</h2>
> >
> >     <p>
> >diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
> >index a78972888e33..7ae554c87964 100644
> >--- a/lib/netdev-native-tnl.c
> >+++ b/lib/netdev-native-tnl.c
> >@@ -55,6 +55,9 @@ static struct vlog_rate_limit err_rl =
> >VLOG_RATE_LIMIT_INIT(60, 5);
> > #define GENEVE_BASE_HLEN   (sizeof(struct udp_header) +         \
> >                             sizeof(struct genevehdr))
> >
> >+#define GTPU_HLEN   (sizeof(struct udp_header) +        \
> >+                     sizeof(struct gtpuhdr))
> This is the base header length, if S or N-PDU is on you have
> to add 4-bytes

OK, will add sequence.

> >+
> > uint16_t tnl_udp_port_min = 32768;
> > uint16_t tnl_udp_port_max = 61000;
> >
> >@@ -708,6 +711,88 @@ netdev_erspan_build_header(const struct netdev
> >*netdev,
> > }
> >
> > struct dp_packet *
> >+netdev_gtpu_pop_header(struct dp_packet *packet)
> >+{
> >+    struct pkt_metadata *md = &packet->md;
> >+    struct flow_tnl *tnl = &md->tunnel;
> >+    struct gtpuhdr *gtph;
> >+    unsigned int hlen;
> >+
> >+    ovs_assert(packet->l3_ofs > 0);
> >+    ovs_assert(packet->l4_ofs > 0);
> >+
> >+    pkt_metadata_init_tnl(md);
> >+    if (GTPU_HLEN > dp_packet_l4_size(packet)) {
> >+        goto err;
> >+    }
> >+
> >+    gtph = udp_extract_tnl_md(packet, tnl, &hlen);
> >+    if (!gtph) {
> >+        goto err;
> >+    }
> >+
> >+    if (gtph->md.flags != GTPU_FLAGS_DEFAULT) {
> >+        VLOG_WARN_RL(&err_rl, "GTP-U not supported");
> >+        goto err;
> >+    }
> >+
> >+    tnl->gtpu_flags = gtph->md.flags;
> >+    tnl->gtpu_msgtype = gtph->md.msgtype;
> >+    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
> >+
> >+    if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
> >+        struct ip_header *ip;
> >+
> >+        ip = (struct ip_header *)(gtph + 1);
> No always correct, should be additional 4 bytes on sequence, n-pdu
> >+        if (IP_VER(ip->ip_ihl_ver) == 4) {
> >+            packet->packet_type = htonl(PT_IPV4);
> >+        } else if (IP_VER(ip->ip_ihl_ver) == 6) {
> >+            packet->packet_type = htonl(PT_IPV6);
> >+        } else {
> >+            VLOG_WARN_RL(&err_rl, "GTP-U: Receive non-IP packet");
> >+        }
> >+    } else {
> >+        /* non GPDU GTP-U messages, ex: echo request, end marker. */
> >+        packet->packet_type = htonl(PT_GTPU_MSG);
> >+    }
> >+
> >+    dp_packet_reset_packet(packet, hlen + GTPU_HLEN);
> Why do you want to reset on control such as end marker?
> >+
> >+    return packet;
> >+
> >+err:
> >+    dp_packet_delete(packet);
> >+    return NULL;
> >+}
> >+
> >+int
> >+netdev_gtpu_build_header(const struct netdev *netdev,
> >+                         struct ovs_action_push_tnl *data,
> >+                         const struct netdev_tnl_build_header_params *params)
> >+{
> >+    struct netdev_vport *dev = netdev_vport_cast(netdev);
> >+    struct netdev_tunnel_config *tnl_cfg;
> >+    struct gtpuhdr *gtph;
> >+
> >+    ovs_mutex_lock(&dev->mutex);
> >+    tnl_cfg = &dev->tnl_cfg;
> >+    gtph = udp_build_header(tnl_cfg, data, params);
> >+    ovs_mutex_unlock(&dev->mutex);
> >+
> >+    /* Set to default if not set in flow. */
> >+    gtph->md.flags = params->flow->tunnel.gtpu_flags ? : GTPU_FLAGS_DEFAULT;
> >+    gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ? :
> >+                       GTPU_MSGTYPE_GPDU;
> >+    put_16aligned_be32(&gtph->teid,
> >+                       htonl(ntohll(params->flow->tunnel.tun_id)));
> >+
> >+    data->header_len += sizeof *gtph;
> >+    data->tnl_type = OVS_VPORT_TYPE_GTPU;
> Note that in some cases, application might need to replace TEID
> , if you have tnl_push you need to preserve the origin sequence.

OK, thanks.

> 
> >+
> >+    return 0;
> >+}
> >+
> >+struct dp_packet *
> > netdev_vxlan_pop_header(struct dp_packet *packet)
> > {
> >     struct pkt_metadata *md = &packet->md;
> >diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h
> >index 5dc00122d93e..7567abaebf95 100644
> >--- a/lib/netdev-native-tnl.h
> >+++ b/lib/netdev-native-tnl.h
> >@@ -52,6 +52,14 @@ netdev_erspan_push_header(const struct netdev *netdev,
> > struct dp_packet *
> > netdev_erspan_pop_header(struct dp_packet *packet);
> >
> >+int
> >+netdev_gtpu_build_header(const struct netdev *netdev,
> >+                         struct ovs_action_push_tnl *data,
> >+                         const struct netdev_tnl_build_header_params *p);
> >+
> >+struct dp_packet *
> >+netdev_gtpu_pop_header(struct dp_packet *packet);
> >+
> > void
> > netdev_tnl_push_udp_header(const struct netdev *netdev,
> >                            struct dp_packet *packet,
> >diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
> >index b57d21ff8d41..8b8857017986 100644
> >--- a/lib/netdev-vport.c
> >+++ b/lib/netdev-vport.c
> >@@ -111,7 +111,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
> >
> >     return (class->get_config == get_tunnel_config &&
> >             (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
> >-             !strcmp("lisp", type) || !strcmp("stt", type)) );
> >+             !strcmp("lisp", type) || !strcmp("stt", type) ||
> >+             !strcmp("gtpu", type)));
> > }
> >
> > const char *
> >@@ -216,6 +217,8 @@ netdev_vport_construct(struct netdev *netdev_)
> >         dev->tnl_cfg.dst_port = port ? htons(port) : htons(LISP_DST_PORT);
> >     } else if (!strcmp(type, "stt")) {
> >         dev->tnl_cfg.dst_port = port ? htons(port) : htons(STT_DST_PORT);
> >+    } else if (!strcmp(type, "gtpu")) {
> >+        dev->tnl_cfg.dst_port = port ? htons(port) : htons(GTPU_DST_PORT);
> >     }
> >
> >     dev->tnl_cfg.dont_fragment = true;
> >@@ -433,6 +436,8 @@ tunnel_supported_layers(const char *type,
> >     } else if (!strcmp(type, "vxlan")
> >                && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
> >         return TNL_L2 | TNL_L3;
> >+    } else if (!strcmp(type, "gtpu")) {
> >+        return TNL_L3;
> >     } else {
> >         return TNL_L2;
> >     }
> >@@ -589,6 +594,10 @@ set_tunnel_config(struct netdev *dev_, const struct
> >smap *args, char **errp)
> >         tnl_cfg.dst_port = htons(STT_DST_PORT);
> >     }
> >
> >+    if (!strcmp(type, "gtpu")) {
> >+        tnl_cfg.dst_port = htons(GTPU_DST_PORT);
> >+    }
> >+
> >     needs_dst_port = netdev_vport_needs_dst_port(dev_);
> >     tnl_cfg.dont_fragment = true;
> >
> >@@ -907,7 +916,8 @@ get_tunnel_config(const struct netdev *dev, struct smap
> >*args)
> >         if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
> >             (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
> >             (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
> >-            (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
> >+            (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
> >+            (!strcmp("gtpu", type) && dst_port != GTPU_DST_PORT)) {
> >             smap_add_format(args, "dst_port", "%d", dst_port);
> >         }
> >     }
> >@@ -1223,6 +1233,17 @@ netdev_vport_tunnel_register(void)
> >           },
> >           {{NULL, NULL, 0, 0}}
> >         },
> >+        { "gtpu_sys",
> >+          {
> >+              TUNNEL_FUNCTIONS_COMMON,
> >+              .type = "gtpu",
> >+              .build_header = netdev_gtpu_build_header,
> >+              .push_header = netdev_tnl_push_udp_header,
> >+              .pop_header = netdev_gtpu_pop_header,
> >+          },
> >+          {{NULL, NULL, 0, 0}}
> >+        },
> >+
> >     };
> >     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
> >
> >diff --git a/lib/nx-match.c b/lib/nx-match.c
William Tu Dec. 11, 2019, 6:19 p.m. UTC | #6
On Sun, Dec 08, 2019 at 08:11:19AM +0000, Roni Bar Yanai wrote:
> Hi William,
> 
> GTP-U header size is not constant, you *must* take into account the flags, mainly
>  the sequence. The use of sequence in GTP-U is optional but some devices do use
>  it.  see from 3GPP definition:
> "For PGW, SGW and eNodeB the usage of sequence numbers in G-PDUs is optional, but
>  if GTP-U protocol entities in these nodes are relaying G-PDUs to other nodes, they 
>  shall relay the sequence numbers as well. An RNC, SGSN or GGSN shall reorder out 
>  of sequence T-PDUs when in sequence delivery is required. This is optional"
> 
> The assumption that GTP-U is only data is not correct, you have some signaling
> for example END MARKER (like you wrote). This message is sent when there is a mobility
>  between cells, and the message contains a TEID and indicates that last packet sent from 
> the origin cell, to prevent packet re-order as handover might have packets
> on the fly. So I would expected that END MARKER will do the exact same 
> forwarding as the GTP-U data.
> 
> see inline
> 
> >diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
> >index a78972888e33..7ae554c87964 100644
> >--- a/lib/netdev-native-tnl.c
> >+++ b/lib/netdev-native-tnl.c
> >@@ -55,6 +55,9 @@ static struct vlog_rate_limit err_rl =
> >VLOG_RATE_LIMIT_INIT(60, 5);
> > #define GENEVE_BASE_HLEN   (sizeof(struct udp_header) +         \
> >                             sizeof(struct genevehdr))
> >
> >+#define GTPU_HLEN   (sizeof(struct udp_header) +        \
> >+                     sizeof(struct gtpuhdr))
> This is the base header length, if S or N-PDU is on you have
> to add 4-bytes
> >+
> > uint16_t tnl_udp_port_min = 32768;
> > uint16_t tnl_udp_port_max = 61000;
> >
> >@@ -708,6 +711,88 @@ netdev_erspan_build_header(const struct netdev
> >*netdev,
> > }
> >
> > struct dp_packet *
> >+netdev_gtpu_pop_header(struct dp_packet *packet)
> >+{
> >+    struct pkt_metadata *md = &packet->md;
> >+    struct flow_tnl *tnl = &md->tunnel;
> >+    struct gtpuhdr *gtph;
> >+    unsigned int hlen;
> >+
> >+    ovs_assert(packet->l3_ofs > 0);
> >+    ovs_assert(packet->l4_ofs > 0);
> >+
> >+    pkt_metadata_init_tnl(md);
> >+    if (GTPU_HLEN > dp_packet_l4_size(packet)) {
> >+        goto err;
> >+    }
> >+
> >+    gtph = udp_extract_tnl_md(packet, tnl, &hlen);
> >+    if (!gtph) {
> >+        goto err;
> >+    }
> >+
> >+    if (gtph->md.flags != GTPU_FLAGS_DEFAULT) {
> >+        VLOG_WARN_RL(&err_rl, "GTP-U not supported");
> >+        goto err;
> >+    }
> >+
> >+    tnl->gtpu_flags = gtph->md.flags;
> >+    tnl->gtpu_msgtype = gtph->md.msgtype;
> >+    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
> >+
> >+    if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
> >+        struct ip_header *ip;
> >+
> >+        ip = (struct ip_header *)(gtph + 1);
> No always correct, should be additional 4 bytes on sequence, n-pdu
> >+        if (IP_VER(ip->ip_ihl_ver) == 4) {
> >+            packet->packet_type = htonl(PT_IPV4);
> >+        } else if (IP_VER(ip->ip_ihl_ver) == 6) {
> >+            packet->packet_type = htonl(PT_IPV6);
> >+        } else {
> >+            VLOG_WARN_RL(&err_rl, "GTP-U: Receive non-IP packet");
> >+        }
> >+    } else {
> >+        /* non GPDU GTP-U messages, ex: echo request, end marker. */
> >+        packet->packet_type = htonl(PT_GTPU_MSG);
> >+    }
> >+
> >+    dp_packet_reset_packet(packet, hlen + GTPU_HLEN);
> Why do you want to reset on control such as end marker?

Right, for non-GPDU, we shouldn't reset.
I will keep the original packet, no pop.

> >+
> >+    return packet;
> >+
> >+err:
> >+    dp_packet_delete(packet);
> >+    return NULL;
> >+}
> >+
> >+int
> >+netdev_gtpu_build_header(const struct netdev *netdev,
> >+                         struct ovs_action_push_tnl *data,
> >+                         const struct netdev_tnl_build_header_params *params)
> >+{
> >+    struct netdev_vport *dev = netdev_vport_cast(netdev);
> >+    struct netdev_tunnel_config *tnl_cfg;
> >+    struct gtpuhdr *gtph;
> >+
> >+    ovs_mutex_lock(&dev->mutex);
> >+    tnl_cfg = &dev->tnl_cfg;
> >+    gtph = udp_build_header(tnl_cfg, data, params);
> >+    ovs_mutex_unlock(&dev->mutex);
> >+
> >+    /* Set to default if not set in flow. */
> >+    gtph->md.flags = params->flow->tunnel.gtpu_flags ? : GTPU_FLAGS_DEFAULT;
> >+    gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ? :
> >+                       GTPU_MSGTYPE_GPDU;
> >+    put_16aligned_be32(&gtph->teid,
> >+                       htonl(ntohll(params->flow->tunnel.tun_id)));
> >+
> >+    data->header_len += sizeof *gtph;
> >+    data->tnl_type = OVS_VPORT_TYPE_GTPU;
> Note that in some cases, application might need to replace TEID
> , if you have tnl_push you need to preserve the origin sequence.
What use case are you talking about here?
Are you saying that OVS receive a GTP-U packet, decap it,
preserve its sequence number, and send to anther GTPU tunnel?

For replacing TEID, change the tun_id will do the work.

Thanks for your feedback!
William
Roni Bar Yanai Dec. 12, 2019, 7:28 a.m. UTC | #7
Hi William,

I think the question here is what is the main target functionality?
3-5/G VNF switching?
Note that in many cases there is RSS issue here because GTP between
 two  functions will have the exact same outer header, there is no
 port entropy. All the packets between two peers will get to the same
 core (per direction, according to RSS symmetric cfg).

See inline.

>-----Original Message-----
>From: William Tu <u9012063@gmail.com>
>Sent: Wednesday, December 11, 2019 8:20 PM
>To: Roni Bar Yanai <roniba@mellanox.com>
>Cc: dev@openvswitch.org
>Subject: Re: [ovs-dev] [PATCHv4] userspace: Add GTP-U support.
>
>On Sun, Dec 08, 2019 at 08:11:19AM +0000, Roni Bar Yanai wrote:
>> Hi William,
>>
>> GTP-U header size is not constant, you *must* take into account the
>> flags, mainly  the sequence. The use of sequence in GTP-U is optional
>> but some devices do use  it.  see from 3GPP definition:
>> "For PGW, SGW and eNodeB the usage of sequence numbers in G-PDUs is
>> optional, but  if GTP-U protocol entities in these nodes are relaying
>> G-PDUs to other nodes, they  shall relay the sequence numbers as well.
>> An RNC, SGSN or GGSN shall reorder out  of sequence T-PDUs when in
>sequence delivery is required. This is optional"
>>
>> The assumption that GTP-U is only data is not correct, you have some
>> signaling for example END MARKER (like you wrote). This message is
>> sent when there is a mobility  between cells, and the message contains
>> a TEID and indicates that last packet sent from the origin cell, to
>> prevent packet re-order as handover might have packets on the fly. So
>> I would expected that END MARKER will do the exact same forwarding as the
>GTP-U data.
>>
>> see inline
>>
>> >diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c index
>> >a78972888e33..7ae554c87964 100644
>> >--- a/lib/netdev-native-tnl.c
>> >+++ b/lib/netdev-native-tnl.c
>> >@@ -55,6 +55,9 @@ static struct vlog_rate_limit err_rl =
>> >VLOG_RATE_LIMIT_INIT(60, 5);
>> > #define GENEVE_BASE_HLEN   (sizeof(struct udp_header) +         \
>> >                             sizeof(struct genevehdr))
>> >
>> >+#define GTPU_HLEN   (sizeof(struct udp_header) +        \
>> >+                     sizeof(struct gtpuhdr))
>> This is the base header length, if S or N-PDU is on you have to add
>> 4-bytes
>> >+
>> > uint16_t tnl_udp_port_min = 32768;
>> > uint16_t tnl_udp_port_max = 61000;
>> >
>> >@@ -708,6 +711,88 @@ netdev_erspan_build_header(const struct netdev
>> >*netdev,  }
>> >
>> > struct dp_packet *
>> >+netdev_gtpu_pop_header(struct dp_packet *packet) {
>> >+    struct pkt_metadata *md = &packet->md;
>> >+    struct flow_tnl *tnl = &md->tunnel;
>> >+    struct gtpuhdr *gtph;
>> >+    unsigned int hlen;
>> >+
>> >+    ovs_assert(packet->l3_ofs > 0);
>> >+    ovs_assert(packet->l4_ofs > 0);
>> >+
>> >+    pkt_metadata_init_tnl(md);
>> >+    if (GTPU_HLEN > dp_packet_l4_size(packet)) {
>> >+        goto err;
>> >+    }
>> >+
>> >+    gtph = udp_extract_tnl_md(packet, tnl, &hlen);
>> >+    if (!gtph) {
>> >+        goto err;
>> >+    }
>> >+
>> >+    if (gtph->md.flags != GTPU_FLAGS_DEFAULT) {
>> >+        VLOG_WARN_RL(&err_rl, "GTP-U not supported");
>> >+        goto err;
>> >+    }
>> >+
>> >+    tnl->gtpu_flags = gtph->md.flags;
>> >+    tnl->gtpu_msgtype = gtph->md.msgtype;
>> >+    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
>> >+
>> >+    if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
>> >+        struct ip_header *ip;
>> >+
>> >+        ip = (struct ip_header *)(gtph + 1);
>> No always correct, should be additional 4 bytes on sequence, n-pdu
>> >+        if (IP_VER(ip->ip_ihl_ver) == 4) {
>> >+            packet->packet_type = htonl(PT_IPV4);
>> >+        } else if (IP_VER(ip->ip_ihl_ver) == 6) {
>> >+            packet->packet_type = htonl(PT_IPV6);
>> >+        } else {
>> >+            VLOG_WARN_RL(&err_rl, "GTP-U: Receive non-IP packet");
>> >+        }
>> >+    } else {
>> >+        /* non GPDU GTP-U messages, ex: echo request, end marker. */
>> >+        packet->packet_type = htonl(PT_GTPU_MSG);
>> >+    }
>> >+
>> >+    dp_packet_reset_packet(packet, hlen + GTPU_HLEN);
>> Why do you want to reset on control such as end marker?
>
>Right, for non-GPDU, we shouldn't reset.
>I will keep the original packet, no pop.
>
>> >+
>> >+    return packet;
>> >+
>> >+err:
>> >+    dp_packet_delete(packet);
>> >+    return NULL;
>> >+}
>> >+
>> >+int
>> >+netdev_gtpu_build_header(const struct netdev *netdev,
>> >+                         struct ovs_action_push_tnl *data,
>> >+                         const struct netdev_tnl_build_header_params
>> >+*params) {
>> >+    struct netdev_vport *dev = netdev_vport_cast(netdev);
>> >+    struct netdev_tunnel_config *tnl_cfg;
>> >+    struct gtpuhdr *gtph;
>> >+
>> >+    ovs_mutex_lock(&dev->mutex);
>> >+    tnl_cfg = &dev->tnl_cfg;
>> >+    gtph = udp_build_header(tnl_cfg, data, params);
>> >+    ovs_mutex_unlock(&dev->mutex);
>> >+
>> >+    /* Set to default if not set in flow. */
>> >+    gtph->md.flags = params->flow->tunnel.gtpu_flags ? :
>GTPU_FLAGS_DEFAULT;
>> >+    gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ? :
>> >+                       GTPU_MSGTYPE_GPDU;
>> >+    put_16aligned_be32(&gtph->teid,
>> >+                       htonl(ntohll(params->flow->tunnel.tun_id)));
>> >+
>> >+    data->header_len += sizeof *gtph;
>> >+    data->tnl_type = OVS_VPORT_TYPE_GTPU;
>> Note that in some cases, application might need to replace TEID , if
>> you have tnl_push you need to preserve the origin sequence.
>What use case are you talking about here?
>Are you saying that OVS receive a GTP-U packet, decap it, preserve its sequence
>number, and send to anther GTPU tunnel?
>
>For replacing TEID, change the tun_id will do the work.

What is the use case you target here?
I guess for switching this part can be skipped it is more VNF functionality.
There are points on data path like p-gw that tunnel TEID changes because the
allocation is done by the two peers of the connection and it depends on their
available tunnels. So packet will arrive on one tunnel and leave for next function
with a different tunnel. If this is the use case and GTP header has a sequence, the
sequence should remain the same.
>
>Thanks for your feedback!
>William
William Tu Dec. 12, 2019, 5:24 p.m. UTC | #8
On Thu, Dec 12, 2019 at 07:28:27AM +0000, Roni Bar Yanai wrote:
> Hi William,
> 
> I think the question here is what is the main target functionality?
> 3-5/G VNF switching?

I actually don't have a target use case. The original patch was from
Facebook and Intel, see:
https://lists.linuxfoundation.org/pipermail/ovs-dev/2018-May/346957.html
https://lists.linuxfoundation.org/pipermail/ovs-dev/2018-May/347029.html

Like other OVS tunnel supports, the patch allows OVS to
- create GTP-U tunnel dev
- decap/encap GTP-U 
- match on flags and msgtype

I don't have experience in deploying GTP-U, but I'd love to
know/learn from your use cases.
 
> Note that in many cases there is RSS issue here because GTP between
>  two  functions will have the exact same outer header, there is no
>  port entropy. All the packets between two peers will get to the same
>  core (per direction, according to RSS symmetric cfg).

I see. This looks like a performance issue.
Maybe we can improve it later.

> 
> See inline.
> 
> >-----Original Message-----
> >From: William Tu <u9012063@gmail.com>
> >Sent: Wednesday, December 11, 2019 8:20 PM
> >To: Roni Bar Yanai <roniba@mellanox.com>
> >Cc: dev@openvswitch.org
> >Subject: Re: [ovs-dev] [PATCHv4] userspace: Add GTP-U support.
> >
> >On Sun, Dec 08, 2019 at 08:11:19AM +0000, Roni Bar Yanai wrote:
> >> Hi William,
> >>
> >> GTP-U header size is not constant, you *must* take into account the
> >> flags, mainly  the sequence. The use of sequence in GTP-U is optional
> >> but some devices do use  it.  see from 3GPP definition:
> >> "For PGW, SGW and eNodeB the usage of sequence numbers in G-PDUs is
> >> optional, but  if GTP-U protocol entities in these nodes are relaying
> >> G-PDUs to other nodes, they  shall relay the sequence numbers as well.
> >> An RNC, SGSN or GGSN shall reorder out  of sequence T-PDUs when in
> >sequence delivery is required. This is optional"
> >>
> >> The assumption that GTP-U is only data is not correct, you have some
> >> signaling for example END MARKER (like you wrote). This message is
> >> sent when there is a mobility  between cells, and the message contains
> >> a TEID and indicates that last packet sent from the origin cell, to
> >> prevent packet re-order as handover might have packets on the fly. So
> >> I would expected that END MARKER will do the exact same forwarding as the
> >GTP-U data.
> >>
> >> see inline
> >>
> >> >diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c index
> >> >a78972888e33..7ae554c87964 100644
> >> >--- a/lib/netdev-native-tnl.c
> >> >+++ b/lib/netdev-native-tnl.c
> >> >@@ -55,6 +55,9 @@ static struct vlog_rate_limit err_rl =
> >> >VLOG_RATE_LIMIT_INIT(60, 5);
> >> > #define GENEVE_BASE_HLEN   (sizeof(struct udp_header) +         \
> >> >                             sizeof(struct genevehdr))
> >> >
> >> >+#define GTPU_HLEN   (sizeof(struct udp_header) +        \
> >> >+                     sizeof(struct gtpuhdr))
> >> This is the base header length, if S or N-PDU is on you have to add
> >> 4-bytes
> >> >+
> >> > uint16_t tnl_udp_port_min = 32768;
> >> > uint16_t tnl_udp_port_max = 61000;
> >> >
> >> >@@ -708,6 +711,88 @@ netdev_erspan_build_header(const struct netdev
> >> >*netdev,  }
> >> >
> >> > struct dp_packet *
> >> >+netdev_gtpu_pop_header(struct dp_packet *packet) {
> >> >+    struct pkt_metadata *md = &packet->md;
> >> >+    struct flow_tnl *tnl = &md->tunnel;
> >> >+    struct gtpuhdr *gtph;
> >> >+    unsigned int hlen;
> >> >+
> >> >+    ovs_assert(packet->l3_ofs > 0);
> >> >+    ovs_assert(packet->l4_ofs > 0);
> >> >+
> >> >+    pkt_metadata_init_tnl(md);
> >> >+    if (GTPU_HLEN > dp_packet_l4_size(packet)) {
> >> >+        goto err;
> >> >+    }
> >> >+
> >> >+    gtph = udp_extract_tnl_md(packet, tnl, &hlen);
> >> >+    if (!gtph) {
> >> >+        goto err;
> >> >+    }
> >> >+
> >> >+    if (gtph->md.flags != GTPU_FLAGS_DEFAULT) {
> >> >+        VLOG_WARN_RL(&err_rl, "GTP-U not supported");
> >> >+        goto err;
> >> >+    }
> >> >+
> >> >+    tnl->gtpu_flags = gtph->md.flags;
> >> >+    tnl->gtpu_msgtype = gtph->md.msgtype;
> >> >+    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
> >> >+
> >> >+    if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
> >> >+        struct ip_header *ip;
> >> >+
> >> >+        ip = (struct ip_header *)(gtph + 1);
> >> No always correct, should be additional 4 bytes on sequence, n-pdu
> >> >+        if (IP_VER(ip->ip_ihl_ver) == 4) {
> >> >+            packet->packet_type = htonl(PT_IPV4);
> >> >+        } else if (IP_VER(ip->ip_ihl_ver) == 6) {
> >> >+            packet->packet_type = htonl(PT_IPV6);
> >> >+        } else {
> >> >+            VLOG_WARN_RL(&err_rl, "GTP-U: Receive non-IP packet");
> >> >+        }
> >> >+    } else {
> >> >+        /* non GPDU GTP-U messages, ex: echo request, end marker. */
> >> >+        packet->packet_type = htonl(PT_GTPU_MSG);
> >> >+    }
> >> >+
> >> >+    dp_packet_reset_packet(packet, hlen + GTPU_HLEN);
> >> Why do you want to reset on control such as end marker?
> >
> >Right, for non-GPDU, we shouldn't reset.
> >I will keep the original packet, no pop.
> >
> >> >+
> >> >+    return packet;
> >> >+
> >> >+err:
> >> >+    dp_packet_delete(packet);
> >> >+    return NULL;
> >> >+}
> >> >+
> >> >+int
> >> >+netdev_gtpu_build_header(const struct netdev *netdev,
> >> >+                         struct ovs_action_push_tnl *data,
> >> >+                         const struct netdev_tnl_build_header_params
> >> >+*params) {
> >> >+    struct netdev_vport *dev = netdev_vport_cast(netdev);
> >> >+    struct netdev_tunnel_config *tnl_cfg;
> >> >+    struct gtpuhdr *gtph;
> >> >+
> >> >+    ovs_mutex_lock(&dev->mutex);
> >> >+    tnl_cfg = &dev->tnl_cfg;
> >> >+    gtph = udp_build_header(tnl_cfg, data, params);
> >> >+    ovs_mutex_unlock(&dev->mutex);
> >> >+
> >> >+    /* Set to default if not set in flow. */
> >> >+    gtph->md.flags = params->flow->tunnel.gtpu_flags ? :
> >GTPU_FLAGS_DEFAULT;
> >> >+    gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ? :
> >> >+                       GTPU_MSGTYPE_GPDU;
> >> >+    put_16aligned_be32(&gtph->teid,
> >> >+                       htonl(ntohll(params->flow->tunnel.tun_id)));
> >> >+
> >> >+    data->header_len += sizeof *gtph;
> >> >+    data->tnl_type = OVS_VPORT_TYPE_GTPU;
> >> Note that in some cases, application might need to replace TEID , if
> >> you have tnl_push you need to preserve the origin sequence.
> >What use case are you talking about here?
> >Are you saying that OVS receive a GTP-U packet, decap it, preserve its sequence
> >number, and send to anther GTPU tunnel?
> >
> >For replacing TEID, change the tun_id will do the work.
> 
> What is the use case you target here?
> I guess for switching this part can be skipped it is more VNF functionality.
> There are points on data path like p-gw that tunnel TEID changes because the
> allocation is done by the two peers of the connection and it depends on their
> available tunnels. So packet will arrive on one tunnel and leave for next function
> with a different tunnel. If this is the use case and GTP header has a sequence, the
> sequence should remain the same.

I see. So in this case, I image OVS works like this

0) create gtpu0 (teid=10) and gtpu1 (teid=11) tunnel ports
   and GTP-U packets arrives at an port
1) OVS decaps the rx packet, forwards to gtpu0 dev
2) controller adds an openflow rule
   in_port=gtpu0 actions=output:gtpu1
3) gtpu1 gets inner packet, and encap it with outer header
   with new teid, and tx 

However, the sequence number will not remain the same.
William
Roni Bar Yanai Jan. 6, 2020, 12:50 p.m. UTC | #9
Hi William,
See inline

>-----Original Message-----
>From: William Tu <u9012063@gmail.com>
>Sent: Thursday, December 12, 2019 7:25 PM
>To: Roni Bar Yanai <roniba@mellanox.com>
>Cc: dev@openvswitch.org
>Subject: Re: [ovs-dev] [PATCHv4] userspace: Add GTP-U support.
>
>On Thu, Dec 12, 2019 at 07:28:27AM +0000, Roni Bar Yanai wrote:
>> Hi William,
>>
>> I think the question here is what is the main target functionality?
>> 3-5/G VNF switching?
>
>I actually don't have a target use case. The original patch was from Facebook and
>Intel, see:
>https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.linuxf
>oundation.org%2Fpipermail%2Fovs-dev%2F2018-
>May%2F346957.html&amp;data=02%7C01%7Croniba%40mellanox.com%7C5ea521
>3063934e92f8c808d77f283106%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0
>%7C637117682910041411&amp;sdata=QyL1wgHhtLZfIe%2Bc%2B7F6eYM6R7E1%2F
>qc5%2FpGH2U4M3VY%3D&amp;reserved=0
>https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.linuxf
>oundation.org%2Fpipermail%2Fovs-dev%2F2018-
>May%2F347029.html&amp;data=02%7C01%7Croniba%40mellanox.com%7C5ea521
>3063934e92f8c808d77f283106%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0
>%7C637117682910041411&amp;sdata=%2FKf47zwbTz5Rrf8u6SMKIjhd8hHKetnGS6
>7%2FHy5X%2FVw%3D&amp;reserved=0
>
>Like other OVS tunnel supports, the patch allows OVS to
>- create GTP-U tunnel dev
>- decap/encap GTP-U
>- match on flags and msgtype
>
>I don't have experience in deploying GTP-U, but I'd love to know/learn from your
>use cases.
I can think of the following:
*  4/5G VNF, In this case you might have
      - VNF get the packet with the GTP, no decap then TX with encap of GTP
      - Termination point, decap on RX and might also do NAT, On TX UN-NAT and encap
      - VNF that process control, doing match on msg type
* OVS as an implementation of UPF data (just a crazy idea):
       - UPF configures through N4 with matches that can have combination of teid (with ips)
         and  internal 5-tuple (or part of it). Actions of UPF can include decap. NAT and QoS.
         This can be translated to open-flow rules easily.
         However, unmatched packets should go to some VNF that have access to the SMF, and
         as a result will create new open-flow rules.
>
>> Note that in many cases there is RSS issue here because GTP between
>> two  functions will have the exact same outer header, there is no
>> port entropy. All the packets between two peers will get to the same
>> core (per direction, according to RSS symmetric cfg).
>
>I see. This looks like a performance issue.
>Maybe we can improve it later.
>
>>
>> See inline.
>>
>> >-----Original Message-----
>> >From: William Tu <u9012063@gmail.com>
>> >Sent: Wednesday, December 11, 2019 8:20 PM
>> >To: Roni Bar Yanai <roniba@mellanox.com>
>> >Cc: dev@openvswitch.org
>> >Subject: Re: [ovs-dev] [PATCHv4] userspace: Add GTP-U support.
>> >
>> >On Sun, Dec 08, 2019 at 08:11:19AM +0000, Roni Bar Yanai wrote:
>> >> Hi William,
>> >>
>> >> GTP-U header size is not constant, you *must* take into account the
>> >> flags, mainly  the sequence. The use of sequence in GTP-U is
>> >> optional but some devices do use  it.  see from 3GPP definition:
>> >> "For PGW, SGW and eNodeB the usage of sequence numbers in G-PDUs is
>> >> optional, but  if GTP-U protocol entities in these nodes are
>> >> relaying G-PDUs to other nodes, they  shall relay the sequence numbers as
>well.
>> >> An RNC, SGSN or GGSN shall reorder out  of sequence T-PDUs when in
>> >sequence delivery is required. This is optional"
>> >>
>> >> The assumption that GTP-U is only data is not correct, you have
>> >> some signaling for example END MARKER (like you wrote). This
>> >> message is sent when there is a mobility  between cells, and the
>> >> message contains a TEID and indicates that last packet sent from
>> >> the origin cell, to prevent packet re-order as handover might have
>> >> packets on the fly. So I would expected that END MARKER will do the
>> >> exact same forwarding as the
>> >GTP-U data.
>> >>
>> >> see inline
>> >>
>> >> >diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
>> >> >index
>> >> >a78972888e33..7ae554c87964 100644
>> >> >--- a/lib/netdev-native-tnl.c
>> >> >+++ b/lib/netdev-native-tnl.c
>> >> >@@ -55,6 +55,9 @@ static struct vlog_rate_limit err_rl =
>> >> >VLOG_RATE_LIMIT_INIT(60, 5);
>> >> > #define GENEVE_BASE_HLEN   (sizeof(struct udp_header) +         \
>> >> >                             sizeof(struct genevehdr))
>> >> >
>> >> >+#define GTPU_HLEN   (sizeof(struct udp_header) +        \
>> >> >+                     sizeof(struct gtpuhdr))
>> >> This is the base header length, if S or N-PDU is on you have to add
>> >> 4-bytes
>> >> >+
>> >> > uint16_t tnl_udp_port_min = 32768; uint16_t tnl_udp_port_max =
>> >> > 61000;
>> >> >
>> >> >@@ -708,6 +711,88 @@ netdev_erspan_build_header(const struct
>> >> >netdev *netdev,  }
>> >> >
>> >> > struct dp_packet *
>> >> >+netdev_gtpu_pop_header(struct dp_packet *packet) {
>> >> >+    struct pkt_metadata *md = &packet->md;
>> >> >+    struct flow_tnl *tnl = &md->tunnel;
>> >> >+    struct gtpuhdr *gtph;
>> >> >+    unsigned int hlen;
>> >> >+
>> >> >+    ovs_assert(packet->l3_ofs > 0);
>> >> >+    ovs_assert(packet->l4_ofs > 0);
>> >> >+
>> >> >+    pkt_metadata_init_tnl(md);
>> >> >+    if (GTPU_HLEN > dp_packet_l4_size(packet)) {
>> >> >+        goto err;
>> >> >+    }
>> >> >+
>> >> >+    gtph = udp_extract_tnl_md(packet, tnl, &hlen);
>> >> >+    if (!gtph) {
>> >> >+        goto err;
>> >> >+    }
>> >> >+
>> >> >+    if (gtph->md.flags != GTPU_FLAGS_DEFAULT) {
>> >> >+        VLOG_WARN_RL(&err_rl, "GTP-U not supported");
>> >> >+        goto err;
>> >> >+    }
>> >> >+
>> >> >+    tnl->gtpu_flags = gtph->md.flags;
>> >> >+    tnl->gtpu_msgtype = gtph->md.msgtype;
>> >> >+    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
>> >> >+
>> >> >+    if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
>> >> >+        struct ip_header *ip;
>> >> >+
>> >> >+        ip = (struct ip_header *)(gtph + 1);
>> >> No always correct, should be additional 4 bytes on sequence, n-pdu
>> >> >+        if (IP_VER(ip->ip_ihl_ver) == 4) {
>> >> >+            packet->packet_type = htonl(PT_IPV4);
>> >> >+        } else if (IP_VER(ip->ip_ihl_ver) == 6) {
>> >> >+            packet->packet_type = htonl(PT_IPV6);
>> >> >+        } else {
>> >> >+            VLOG_WARN_RL(&err_rl, "GTP-U: Receive non-IP packet");
>> >> >+        }
>> >> >+    } else {
>> >> >+        /* non GPDU GTP-U messages, ex: echo request, end marker. */
>> >> >+        packet->packet_type = htonl(PT_GTPU_MSG);
>> >> >+    }
>> >> >+
>> >> >+    dp_packet_reset_packet(packet, hlen + GTPU_HLEN);
>> >> Why do you want to reset on control such as end marker?
>> >
>> >Right, for non-GPDU, we shouldn't reset.
>> >I will keep the original packet, no pop.
>> >
>> >> >+
>> >> >+    return packet;
>> >> >+
>> >> >+err:
>> >> >+    dp_packet_delete(packet);
>> >> >+    return NULL;
>> >> >+}
>> >> >+
>> >> >+int
>> >> >+netdev_gtpu_build_header(const struct netdev *netdev,
>> >> >+                         struct ovs_action_push_tnl *data,
>> >> >+                         const struct
>> >> >+netdev_tnl_build_header_params
>> >> >+*params) {
>> >> >+    struct netdev_vport *dev = netdev_vport_cast(netdev);
>> >> >+    struct netdev_tunnel_config *tnl_cfg;
>> >> >+    struct gtpuhdr *gtph;
>> >> >+
>> >> >+    ovs_mutex_lock(&dev->mutex);
>> >> >+    tnl_cfg = &dev->tnl_cfg;
>> >> >+    gtph = udp_build_header(tnl_cfg, data, params);
>> >> >+    ovs_mutex_unlock(&dev->mutex);
>> >> >+
>> >> >+    /* Set to default if not set in flow. */
>> >> >+    gtph->md.flags = params->flow->tunnel.gtpu_flags ? :
>> >GTPU_FLAGS_DEFAULT;
>> >> >+    gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ? :
>> >> >+                       GTPU_MSGTYPE_GPDU;
>> >> >+    put_16aligned_be32(&gtph->teid,
>> >> >+
>> >> >+ htonl(ntohll(params->flow->tunnel.tun_id)));
>> >> >+
>> >> >+    data->header_len += sizeof *gtph;
>> >> >+    data->tnl_type = OVS_VPORT_TYPE_GTPU;
>> >> Note that in some cases, application might need to replace TEID ,
>> >> if you have tnl_push you need to preserve the origin sequence.
>> >What use case are you talking about here?
>> >Are you saying that OVS receive a GTP-U packet, decap it, preserve
>> >its sequence number, and send to anther GTPU tunnel?
>> >
>> >For replacing TEID, change the tun_id will do the work.
>>
>> What is the use case you target here?
>> I guess for switching this part can be skipped it is more VNF functionality.
>> There are points on data path like p-gw that tunnel TEID changes
>> because the allocation is done by the two peers of the connection and
>> it depends on their available tunnels. So packet will arrive on one
>> tunnel and leave for next function with a different tunnel. If this is
>> the use case and GTP header has a sequence, the sequence should remain the
>same.
>
>I see. So in this case, I image OVS works like this
>
>0) create gtpu0 (teid=10) and gtpu1 (teid=11) tunnel ports
>   and GTP-U packets arrives at an port
>1) OVS decaps the rx packet, forwards to gtpu0 dev
>2) controller adds an openflow rule
>   in_port=gtpu0 actions=output:gtpu1
>3) gtpu1 gets inner packet, and encap it with outer header
>   with new teid, and tx
>
>However, the sequence number will not remain the same.
>William
diff mbox series

Patch

diff --git a/Documentation/faq/configuration.rst b/Documentation/faq/configuration.rst
index ff3b71a5d4ef..a9ac9354d0dc 100644
--- a/Documentation/faq/configuration.rst
+++ b/Documentation/faq/configuration.rst
@@ -225,6 +225,19 @@  Q: Does Open vSwitch support IPv6 GRE?
                 options:remote_ip=fc00:100::1 \
                 options:packet_type=legacy_l2
 
+Q: Does Open vSwitch support GTP-U?
+
+    A: Yes. GTP-U (GPRS Tunnelling Protocol User Plane (GTPv1-U))
+    is supported in userspace datapath. TEID is set by using tunnel
+    key field.
+
+    ::
+
+        $ ovs-vsctl add-br br0
+        $ ovs-vsctl add-port br0 gtpu0 -- \
+                set int gtpu0 type=gtpu options:key=<teid> \
+                options:remote_ip=172.31.1.1
+
 Q: How do I connect two bridges?
 
     A: First, why do you want to do this?  Two connected bridges are not much
diff --git a/Documentation/faq/releases.rst b/Documentation/faq/releases.rst
index 6702c58a2b6f..4fa28a378603 100644
--- a/Documentation/faq/releases.rst
+++ b/Documentation/faq/releases.rst
@@ -130,6 +130,7 @@  Q: Are all features available with all datapaths?
     Tunnel - Geneve-IPv6            4.4            2.6          2.6      NO
     Tunnel - ERSPAN                 4.18           2.10         2.10     NO
     Tunnel - ERSPAN-IPv6            4.18           2.10         2.10     NO
+    Tunnel - GTP-U                  NO             NO           2.13     NO
     QoS - Policing                  YES            1.1          2.6      NO
     QoS - Shaping                   YES            1.1          NO       NO
     sFlow                           YES            1.0          1.0      NO
diff --git a/NEWS b/NEWS
index e2254121f619..96d742f773a2 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,9 @@  Post-v2.12.0
      * DPDK ring ports (dpdkr) are deprecated and will be removed in next
        releases.
      * Add support for DPDK 19.11.
+   - GTP-U Tunnel Protocol
+     * Add two new fields: tun_gtpu_flags, tun_gtpu_msgtype.
+     * Only support for userspace datapath.
 
 v2.12.0 - 03 Sep 2019
 ---------------------
diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h
index 778827f8b5a2..6cc90b5b29ef 100644
--- a/datapath/linux/compat/include/linux/openvswitch.h
+++ b/datapath/linux/compat/include/linux/openvswitch.h
@@ -242,6 +242,7 @@  enum ovs_vport_type {
 	OVS_VPORT_TYPE_ERSPAN = 107, /* ERSPAN tunnel. */
 	OVS_VPORT_TYPE_IP6ERSPAN = 108, /* ERSPAN tunnel. */
 	OVS_VPORT_TYPE_IP6GRE = 109,
+	OVS_VPORT_TYPE_GTPU = 110,
 	__OVS_VPORT_TYPE_MAX
 };
 
@@ -401,6 +402,7 @@  enum ovs_tunnel_key_attr {
 	OVS_TUNNEL_KEY_ATTR_IPV6_DST,		/* struct in6_addr dst IPv6 address. */
 	OVS_TUNNEL_KEY_ATTR_PAD,
 	OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS,	/* struct erspan_metadata */
+	OVS_TUNNEL_KEY_ATTR_GTPU_OPTS,		/* struct gtpu_metadata */
 	__OVS_TUNNEL_KEY_ATTR_MAX
 };
 
diff --git a/include/openvswitch/flow.h b/include/openvswitch/flow.h
index 57b6c925c7bc..3054015d93c7 100644
--- a/include/openvswitch/flow.h
+++ b/include/openvswitch/flow.h
@@ -27,7 +27,7 @@  extern "C" {
 /* This sequence number should be incremented whenever anything involving flows
  * or the wildcarding of flows changes.  This will cause build assertion
  * failures in places which likely need to be updated. */
-#define FLOW_WC_SEQ 41
+#define FLOW_WC_SEQ 42
 
 /* Number of Open vSwitch extension 32-bit registers. */
 #define FLOW_N_REGS 16
@@ -168,7 +168,7 @@  BUILD_ASSERT_DECL(sizeof(struct ovs_key_nsh) % sizeof(uint64_t) == 0);
 /* Remember to update FLOW_WC_SEQ when changing 'struct flow'. */
 BUILD_ASSERT_DECL(offsetof(struct flow, igmp_group_ip4) + sizeof(uint32_t)
                   == sizeof(struct flow_tnl) + sizeof(struct ovs_key_nsh) + 300
-                  && FLOW_WC_SEQ == 41);
+                  && FLOW_WC_SEQ == 42);
 
 /* Incremental points at which flow classification may be performed in
  * segments.
diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h
index 05ecee7fc7cd..45a368c274b8 100644
--- a/include/openvswitch/match.h
+++ b/include/openvswitch/match.h
@@ -121,6 +121,12 @@  void match_set_tun_erspan_dir_masked(struct match *match, uint8_t dir,
 void match_set_tun_erspan_hwid(struct match *match, uint8_t hwid);
 void match_set_tun_erspan_hwid_masked(struct match *match, uint8_t hwid,
                                       uint8_t mask);
+void match_set_tun_gtpu_flags(struct match *match, uint8_t flags);
+void match_set_tun_gtpu_flags_masked(struct match *match, uint8_t flags,
+                                     uint8_t mask);
+void match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype);
+void match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t msgtype,
+                                       uint8_t mask);
 void match_set_in_port(struct match *, ofp_port_t ofp_port);
 void match_set_pkt_mark(struct match *, uint32_t pkt_mark);
 void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t mask);
diff --git a/include/openvswitch/meta-flow.h b/include/openvswitch/meta-flow.h
index b7fd6cb3f0fb..d57a36468e19 100644
--- a/include/openvswitch/meta-flow.h
+++ b/include/openvswitch/meta-flow.h
@@ -506,6 +506,34 @@  enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_TUN_ERSPAN_HWID,
 
+    /* "tun_gtpu_flags".
+     *
+     * GTP-U tunnel flags.
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: decimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: none.
+     * OXM: NXOXM_ET_GTPU_FLAGS(15) since v2.13.
+     */
+    MFF_TUN_GTPU_FLAGS,
+
+    /* "tun_gtpu_msgtype".
+     *
+     * GTP-U tunnel message type.
+     *
+     * Type: u8.
+     * Maskable: bitwise.
+     * Formatting: decimal.
+     * Prerequisites: none.
+     * Access: read/write.
+     * NXM: none.
+     * OXM: NXOXM_ET_GTPU_MSGTYPE(16) since v2.13.
+     */
+    MFF_TUN_GTPU_MSGTYPE,
+
 #if TUN_METADATA_NUM_OPTS == 64
     /* "tun_metadata<N>".
      *
diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h
index 925844edae6a..a65cb0d04e77 100644
--- a/include/openvswitch/packets.h
+++ b/include/openvswitch/packets.h
@@ -43,7 +43,9 @@  struct flow_tnl {
     uint32_t erspan_idx;
     uint8_t erspan_dir;
     uint8_t erspan_hwid;
-    uint8_t pad1[6];     /* Pad to 64 bits. */
+    uint8_t gtpu_flags;
+    uint8_t gtpu_msgtype;
+    uint8_t pad1[4];     /* Pad to 64 bits. */
     struct tun_metadata metadata;
 };
 
diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c
index 582274c46774..fd157ce2d918 100644
--- a/lib/dpif-netlink-rtnl.c
+++ b/lib/dpif-netlink-rtnl.c
@@ -111,6 +111,8 @@  vport_type_to_kind(enum ovs_vport_type type,
         } else {
             return NULL;
         }
+    case OVS_VPORT_TYPE_GTPU:
+        return NULL;
     case OVS_VPORT_TYPE_NETDEV:
     case OVS_VPORT_TYPE_INTERNAL:
     case OVS_VPORT_TYPE_LISP:
@@ -277,6 +279,7 @@  dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg,
     case OVS_VPORT_TYPE_INTERNAL:
     case OVS_VPORT_TYPE_LISP:
     case OVS_VPORT_TYPE_STT:
+    case OVS_VPORT_TYPE_GTPU:
     case OVS_VPORT_TYPE_UNSPEC:
     case __OVS_VPORT_TYPE_MAX:
     default:
@@ -358,6 +361,7 @@  dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg,
     case OVS_VPORT_TYPE_INTERNAL:
     case OVS_VPORT_TYPE_LISP:
     case OVS_VPORT_TYPE_STT:
+    case OVS_VPORT_TYPE_GTPU:
     case OVS_VPORT_TYPE_UNSPEC:
     case __OVS_VPORT_TYPE_MAX:
     default:
@@ -471,6 +475,7 @@  dpif_netlink_rtnl_port_destroy(const char *name, const char *type)
     case OVS_VPORT_TYPE_INTERNAL:
     case OVS_VPORT_TYPE_LISP:
     case OVS_VPORT_TYPE_STT:
+    case OVS_VPORT_TYPE_GTPU:
     case OVS_VPORT_TYPE_UNSPEC:
     case __OVS_VPORT_TYPE_MAX:
     default:
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index e9a6887f7af2..bc740250e677 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -703,6 +703,9 @@  get_vport_type(const struct dpif_netlink_vport *vport)
     case OVS_VPORT_TYPE_IP6GRE:
         return "ip6gre";
 
+    case OVS_VPORT_TYPE_GTPU:
+        return "gtpu";
+
     case OVS_VPORT_TYPE_UNSPEC:
     case __OVS_VPORT_TYPE_MAX:
         break;
@@ -736,6 +739,8 @@  netdev_to_ovs_vport_type(const char *type)
         return OVS_VPORT_TYPE_IP6GRE;
     } else if (!strcmp(type, "gre")) {
         return OVS_VPORT_TYPE_GRE;
+    } else if (!strcmp(type, "gtpu")) {
+        return OVS_VPORT_TYPE_GTPU;
     } else {
         return OVS_VPORT_TYPE_UNSPEC;
     }
diff --git a/lib/flow.c b/lib/flow.c
index 45bb96b543be..9ddb24c15185 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -129,7 +129,7 @@  struct mf_ctx {
  * away.  Some GCC versions gave warnings on ALWAYS_INLINE, so these are
  * defined as macros. */
 
-#if (FLOW_WC_SEQ != 41)
+#if (FLOW_WC_SEQ != 42)
 #define MINIFLOW_ASSERT(X) ovs_assert(X)
 BUILD_MESSAGE("FLOW_WC_SEQ changed: miniflow_extract() will have runtime "
                "assertions enabled. Consider updating FLOW_WC_SEQ after "
@@ -731,7 +731,7 @@  void
 miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
 {
     /* Add code to this function (or its callees) to extract new fields. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     const struct pkt_metadata *md = &packet->md;
     const void *data = dp_packet_data(packet);
@@ -1188,7 +1188,7 @@  flow_get_metadata(const struct flow *flow, struct match *flow_metadata)
 {
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     match_init_catchall(flow_metadata);
     if (flow->tunnel.tun_id != htonll(0)) {
@@ -1768,7 +1768,7 @@  flow_wildcards_init_for_packet(struct flow_wildcards *wc,
     memset(&wc->masks, 0x0, sizeof wc->masks);
 
     /* Update this function whenever struct flow changes. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     if (flow_tnl_dst_is_set(&flow->tunnel)) {
         if (flow->tunnel.flags & FLOW_TNL_F_KEY) {
@@ -1789,6 +1789,8 @@  flow_wildcards_init_for_packet(struct flow_wildcards *wc,
         WC_MASK_FIELD(wc, tunnel.erspan_idx);
         WC_MASK_FIELD(wc, tunnel.erspan_dir);
         WC_MASK_FIELD(wc, tunnel.erspan_hwid);
+        WC_MASK_FIELD(wc, tunnel.gtpu_flags);
+        WC_MASK_FIELD(wc, tunnel.gtpu_msgtype);
 
         if (!(flow->tunnel.flags & FLOW_TNL_F_UDPIF)) {
             if (flow->tunnel.metadata.present.map) {
@@ -1919,7 +1921,7 @@  void
 flow_wc_map(const struct flow *flow, struct flowmap *map)
 {
     /* Update this function whenever struct flow changes. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     flowmap_init(map);
 
@@ -2022,7 +2024,7 @@  void
 flow_wildcards_clear_non_packet_fields(struct flow_wildcards *wc)
 {
     /* Update this function whenever struct flow changes. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     memset(&wc->masks.metadata, 0, sizeof wc->masks.metadata);
     memset(&wc->masks.regs, 0, sizeof wc->masks.regs);
@@ -2166,7 +2168,7 @@  flow_wildcards_set_xxreg_mask(struct flow_wildcards *wc, int idx,
 uint32_t
 miniflow_hash_5tuple(const struct miniflow *flow, uint32_t basis)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
     uint32_t hash = basis;
 
     if (flow) {
@@ -2213,7 +2215,7 @@  ASSERT_SEQUENTIAL(ipv6_src, ipv6_dst);
 uint32_t
 flow_hash_5tuple(const struct flow *flow, uint32_t basis)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
     uint32_t hash = basis;
 
     if (flow) {
@@ -2891,7 +2893,7 @@  flow_push_mpls(struct flow *flow, int n, ovs_be16 mpls_eth_type,
 
         if (clear_flow_L3) {
             /* Clear all L3 and L4 fields and dp_hash. */
-            BUILD_ASSERT(FLOW_WC_SEQ == 41);
+            BUILD_ASSERT(FLOW_WC_SEQ == 42);
             memset((char *) flow + FLOW_SEGMENT_2_ENDS_AT, 0,
                    sizeof(struct flow) - FLOW_SEGMENT_2_ENDS_AT);
             flow->dp_hash = 0;
@@ -3189,7 +3191,7 @@  flow_compose(struct dp_packet *p, const struct flow *flow,
     /* Add code to this function (or its callees) for emitting new fields or
      * protocols.  (This isn't essential, so it can be skipped for initial
      * testing.) */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     uint32_t pseudo_hdr_csum;
     size_t l4_len;
diff --git a/lib/flow.h b/lib/flow.h
index 75751763c81a..b32f0b27754a 100644
--- a/lib/flow.h
+++ b/lib/flow.h
@@ -964,7 +964,7 @@  static inline void
 pkt_metadata_from_flow(struct pkt_metadata *md, const struct flow *flow)
 {
     /* Update this function whenever struct flow changes. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     md->recirc_id = flow->recirc_id;
     md->dp_hash = flow->dp_hash;
diff --git a/lib/match.c b/lib/match.c
index ae568280a2bb..875bd7f66cd2 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -375,6 +375,34 @@  match_set_tun_erspan_hwid(struct match *match, uint8_t hwid)
 }
 
 void
+match_set_tun_gtpu_flags_masked(struct match *match, uint8_t flags,
+                                uint8_t mask)
+{
+    match->wc.masks.tunnel.gtpu_flags = flags;
+    match->flow.tunnel.gtpu_flags = flags & mask;
+}
+
+void
+match_set_tun_gtpu_flags(struct match *match, uint8_t flags)
+{
+    match_set_tun_gtpu_flags_masked(match, flags, UINT8_MAX);
+}
+
+void
+match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t msgtype,
+                                  uint8_t mask)
+{
+    match->wc.masks.tunnel.gtpu_msgtype = msgtype;
+    match->flow.tunnel.gtpu_msgtype = msgtype & mask;
+}
+
+void
+match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype)
+{
+    match_set_tun_gtpu_msgtype_masked(match, msgtype, UINT8_MAX);
+}
+
+void
 match_set_in_port(struct match *match, ofp_port_t ofp_port)
 {
     match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
@@ -1390,7 +1418,7 @@  match_format(const struct match *match,
     bool is_megaflow = false;
     int i;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     if (priority != OFP_DEFAULT_PRIORITY) {
         ds_put_format(s, "%spriority=%s%d,",
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index e3274b97f335..d002344fc8c5 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -391,6 +391,10 @@  mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_NSH_C3:
     case MFF_NSH_C4:
         return !wc->masks.nsh.context[mf->id - MFF_NSH_C1];
+    case MFF_TUN_GTPU_FLAGS:
+        return !wc->masks.tunnel.gtpu_flags;
+    case MFF_TUN_GTPU_MSGTYPE:
+        return !wc->masks.tunnel.gtpu_msgtype;
 
     case MFF_N_IDS:
     default:
@@ -530,6 +534,8 @@  mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_TUN_ERSPAN_VER:
     case MFF_TUN_ERSPAN_DIR:
     case MFF_TUN_ERSPAN_HWID:
+    case MFF_TUN_GTPU_FLAGS:
+    case MFF_TUN_GTPU_MSGTYPE:
     CASE_MFF_TUN_METADATA:
     case MFF_METADATA:
     case MFF_IN_PORT:
@@ -711,6 +717,12 @@  mf_get_value(const struct mf_field *mf, const struct flow *flow,
     case MFF_TUN_ERSPAN_HWID:
         value->u8 = flow->tunnel.erspan_hwid;
         break;
+    case MFF_TUN_GTPU_FLAGS:
+        value->u8 = flow->tunnel.gtpu_flags;
+        break;
+    case MFF_TUN_GTPU_MSGTYPE:
+        value->u8 = flow->tunnel.gtpu_msgtype;
+        break;
     CASE_MFF_TUN_METADATA:
         tun_metadata_read(&flow->tunnel, mf, value);
         break;
@@ -1042,6 +1054,12 @@  mf_set_value(const struct mf_field *mf,
     case MFF_TUN_ERSPAN_HWID:
         match_set_tun_erspan_hwid(match, value->u8);
         break;
+    case MFF_TUN_GTPU_FLAGS:
+        match_set_tun_gtpu_flags(match, value->u8);
+        break;
+    case MFF_TUN_GTPU_MSGTYPE:
+        match_set_tun_gtpu_msgtype(match, value->u8);
+        break;
     CASE_MFF_TUN_METADATA:
         tun_metadata_set_match(mf, value, NULL, match, err_str);
         break;
@@ -1459,6 +1477,12 @@  mf_set_flow_value(const struct mf_field *mf,
     case MFF_TUN_ERSPAN_HWID:
         flow->tunnel.erspan_hwid = value->u8;
         break;
+    case MFF_TUN_GTPU_FLAGS:
+        flow->tunnel.gtpu_flags = value->u8;
+        break;
+    case MFF_TUN_GTPU_MSGTYPE:
+        flow->tunnel.gtpu_msgtype = value->u8;
+        break;
     CASE_MFF_TUN_METADATA:
         tun_metadata_write(&flow->tunnel, mf, value);
         break;
@@ -1780,6 +1804,8 @@  mf_is_pipeline_field(const struct mf_field *mf)
     case MFF_TUN_ERSPAN_IDX:
     case MFF_TUN_ERSPAN_DIR:
     case MFF_TUN_ERSPAN_HWID:
+    case MFF_TUN_GTPU_FLAGS:
+    case MFF_TUN_GTPU_MSGTYPE:
     CASE_MFF_TUN_METADATA:
     case MFF_METADATA:
     case MFF_IN_PORT:
@@ -1970,6 +1996,12 @@  mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
     case MFF_TUN_ERSPAN_HWID:
         match_set_tun_erspan_hwid_masked(match, 0, 0);
         break;
+    case MFF_TUN_GTPU_FLAGS:
+        match_set_tun_gtpu_flags_masked(match, 0, 0);
+        break;
+    case MFF_TUN_GTPU_MSGTYPE:
+        match_set_tun_gtpu_msgtype_masked(match, 0, 0);
+        break;
     CASE_MFF_TUN_METADATA:
         tun_metadata_set_match(mf, NULL, NULL, match, err_str);
         break;
@@ -2377,6 +2409,12 @@  mf_set(const struct mf_field *mf,
     case MFF_TUN_ERSPAN_HWID:
         match_set_tun_erspan_hwid_masked(match, value->u8, mask->u8);
         break;
+    case MFF_TUN_GTPU_FLAGS:
+        match_set_tun_gtpu_flags_masked(match, value->u8, mask->u8);
+        break;
+    case MFF_TUN_GTPU_MSGTYPE:
+        match_set_tun_gtpu_msgtype_masked(match, value->u8, mask->u8);
+        break;
     CASE_MFF_TUN_METADATA:
         tun_metadata_set_match(mf, value, mask, match, err_str);
         break;
diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml
index 90b405c73750..ef62bf443679 100644
--- a/lib/meta-flow.xml
+++ b/lib/meta-flow.xml
@@ -1456,7 +1456,8 @@  ovs-ofctl add-flow br-int 'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
         <li>LISP has a 24-bit instance ID.</li>
         <li>GRE has an optional 32-bit key.</li>
         <li>STT has a 64-bit key.</li>
-    <li>ERSPAN has a 10-bit key (Session ID).</li>
+        <li>ERSPAN has a 10-bit key (Session ID).</li>
+        <li>GTPU has a 32-bit key (Tunnel Endpoint ID).</li>
       </ul>
 
       <p>
@@ -1797,6 +1798,82 @@  ovs-ofctl add-flow br-int 'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
       A 6-bit unique identifier of an ERSPAN v2 engine within a system.
     </field>
 
+    <h2>GTP-U Metadata Fields</h2>
+
+    <p>
+      These fields provide access to set-up GPRS Tunnelling Protocol
+      for User Plane (GTPv1-U), based on 3GPP TS 29.281.  A GTP-U
+      header has the following format:
+    </p>
+
+    <diagram>
+      <header>
+        <bits name="flags" above="8" width="0.6"/>
+        <bits name="msg type" above="8" width="0.6"/>
+        <bits name="length" above="16" width="0.9"/>
+        <bits name="TEID" above="32" width="1.3"/>
+      </header>
+      <dots/>
+    </diagram>
+
+    <p>
+      The flags and message type have the Open vSwitch GTP-U specific fields
+      described below.  Open vSwitch makes the TEID (Tunnel Endpoint
+      Identifier), which identifies a tunnel endpoint in the receiving GTP-U
+      protocol entity, available via <ref field="tun_id"/>.
+    </p>
+
+    <field id="MFF_TUN_GTPU_FLAGS" title="GTP-U Flags">
+      <p>
+        This field holds the 8-bit GTP-U flags, encoded as:
+      </p>
+
+      <diagram>
+        <header name="GTP-U Tunnel Flags">
+          <bits name="version" above="3" below="1" width="0.5"/>
+          <bits name="PT" above="1" width="0.3"/>
+          <bits name="rsv" above="1" below="0" width="0.3"/>
+          <bits name="E" above="1" width="0.3"/>
+          <bits name="S" above="1" width="0.3"/>
+          <bits name="PN" above="1" width="0.3"/>
+        </header>
+      </diagram>
+
+      <p>
+        The flags are:
+      </p>
+      <dl>
+        <dt>version</dt>
+        <dd>Used to determine the version of the GTP-U protocol, which should
+        be set to 1.</dd>
+
+        <dt>PT</dt>
+        <dd>Protocol type, used as a protocol discriminator
+        between GTP (1) and GTP' (0).</dd>
+
+        <dt>rsv</dt>
+        <dd>Reserved.  Must be zero.</dd>
+
+        <dt>E</dt>
+        <dd>If 1, indicates the presence of a meaningful value of the Next
+        Extension Header field.</dd>
+
+        <dt>S</dt>
+        <dd>If 1, indicates the presence of a meaningful value of the Sequence
+        Number field.</dd>
+
+        <dt>PN</dt>
+        <dd>If 1, indicates the presence of a meaningful value of the N-PDU
+        Number field.</dd>
+      </dl>
+    </field>
+
+    <field id="MFF_TUN_GTPU_MSGTYPE" title="GTP-U Message Type">
+      This field indicates whether it's a signalling message used for path
+      management, or a user plane message which carries the original packet.
+      The complete range of message types can be referred to [3GPP TS 29.281].
+    </field>
+
     <h2>Geneve Fields</h2>
 
     <p>
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index a78972888e33..7ae554c87964 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -55,6 +55,9 @@  static struct vlog_rate_limit err_rl = VLOG_RATE_LIMIT_INIT(60, 5);
 #define GENEVE_BASE_HLEN   (sizeof(struct udp_header) +         \
                             sizeof(struct genevehdr))
 
+#define GTPU_HLEN   (sizeof(struct udp_header) +        \
+                     sizeof(struct gtpuhdr))
+
 uint16_t tnl_udp_port_min = 32768;
 uint16_t tnl_udp_port_max = 61000;
 
@@ -708,6 +711,88 @@  netdev_erspan_build_header(const struct netdev *netdev,
 }
 
 struct dp_packet *
+netdev_gtpu_pop_header(struct dp_packet *packet)
+{
+    struct pkt_metadata *md = &packet->md;
+    struct flow_tnl *tnl = &md->tunnel;
+    struct gtpuhdr *gtph;
+    unsigned int hlen;
+
+    ovs_assert(packet->l3_ofs > 0);
+    ovs_assert(packet->l4_ofs > 0);
+
+    pkt_metadata_init_tnl(md);
+    if (GTPU_HLEN > dp_packet_l4_size(packet)) {
+        goto err;
+    }
+
+    gtph = udp_extract_tnl_md(packet, tnl, &hlen);
+    if (!gtph) {
+        goto err;
+    }
+
+    if (gtph->md.flags != GTPU_FLAGS_DEFAULT) {
+        VLOG_WARN_RL(&err_rl, "GTP-U not supported");
+        goto err;
+    }
+
+    tnl->gtpu_flags = gtph->md.flags;
+    tnl->gtpu_msgtype = gtph->md.msgtype;
+    tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gtph->teid)));
+
+    if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
+        struct ip_header *ip;
+
+        ip = (struct ip_header *)(gtph + 1);
+        if (IP_VER(ip->ip_ihl_ver) == 4) {
+            packet->packet_type = htonl(PT_IPV4);
+        } else if (IP_VER(ip->ip_ihl_ver) == 6) {
+            packet->packet_type = htonl(PT_IPV6);
+        } else {
+            VLOG_WARN_RL(&err_rl, "GTP-U: Receive non-IP packet");
+        }
+    } else {
+        /* non GPDU GTP-U messages, ex: echo request, end marker. */
+        packet->packet_type = htonl(PT_GTPU_MSG);
+    }
+
+    dp_packet_reset_packet(packet, hlen + GTPU_HLEN);
+
+    return packet;
+
+err:
+    dp_packet_delete(packet);
+    return NULL;
+}
+
+int
+netdev_gtpu_build_header(const struct netdev *netdev,
+                         struct ovs_action_push_tnl *data,
+                         const struct netdev_tnl_build_header_params *params)
+{
+    struct netdev_vport *dev = netdev_vport_cast(netdev);
+    struct netdev_tunnel_config *tnl_cfg;
+    struct gtpuhdr *gtph;
+
+    ovs_mutex_lock(&dev->mutex);
+    tnl_cfg = &dev->tnl_cfg;
+    gtph = udp_build_header(tnl_cfg, data, params);
+    ovs_mutex_unlock(&dev->mutex);
+
+    /* Set to default if not set in flow. */
+    gtph->md.flags = params->flow->tunnel.gtpu_flags ? : GTPU_FLAGS_DEFAULT;
+    gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ? :
+                       GTPU_MSGTYPE_GPDU;
+    put_16aligned_be32(&gtph->teid,
+                       htonl(ntohll(params->flow->tunnel.tun_id)));
+
+    data->header_len += sizeof *gtph;
+    data->tnl_type = OVS_VPORT_TYPE_GTPU;
+
+    return 0;
+}
+
+struct dp_packet *
 netdev_vxlan_pop_header(struct dp_packet *packet)
 {
     struct pkt_metadata *md = &packet->md;
diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h
index 5dc00122d93e..7567abaebf95 100644
--- a/lib/netdev-native-tnl.h
+++ b/lib/netdev-native-tnl.h
@@ -52,6 +52,14 @@  netdev_erspan_push_header(const struct netdev *netdev,
 struct dp_packet *
 netdev_erspan_pop_header(struct dp_packet *packet);
 
+int
+netdev_gtpu_build_header(const struct netdev *netdev,
+                         struct ovs_action_push_tnl *data,
+                         const struct netdev_tnl_build_header_params *p);
+
+struct dp_packet *
+netdev_gtpu_pop_header(struct dp_packet *packet);
+
 void
 netdev_tnl_push_udp_header(const struct netdev *netdev,
                            struct dp_packet *packet,
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index b57d21ff8d41..8b8857017986 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -111,7 +111,8 @@  netdev_vport_needs_dst_port(const struct netdev *dev)
 
     return (class->get_config == get_tunnel_config &&
             (!strcmp("geneve", type) || !strcmp("vxlan", type) ||
-             !strcmp("lisp", type) || !strcmp("stt", type)) );
+             !strcmp("lisp", type) || !strcmp("stt", type) ||
+             !strcmp("gtpu", type)));
 }
 
 const char *
@@ -216,6 +217,8 @@  netdev_vport_construct(struct netdev *netdev_)
         dev->tnl_cfg.dst_port = port ? htons(port) : htons(LISP_DST_PORT);
     } else if (!strcmp(type, "stt")) {
         dev->tnl_cfg.dst_port = port ? htons(port) : htons(STT_DST_PORT);
+    } else if (!strcmp(type, "gtpu")) {
+        dev->tnl_cfg.dst_port = port ? htons(port) : htons(GTPU_DST_PORT);
     }
 
     dev->tnl_cfg.dont_fragment = true;
@@ -433,6 +436,8 @@  tunnel_supported_layers(const char *type,
     } else if (!strcmp(type, "vxlan")
                && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
         return TNL_L2 | TNL_L3;
+    } else if (!strcmp(type, "gtpu")) {
+        return TNL_L3;
     } else {
         return TNL_L2;
     }
@@ -589,6 +594,10 @@  set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
         tnl_cfg.dst_port = htons(STT_DST_PORT);
     }
 
+    if (!strcmp(type, "gtpu")) {
+        tnl_cfg.dst_port = htons(GTPU_DST_PORT);
+    }
+
     needs_dst_port = netdev_vport_needs_dst_port(dev_);
     tnl_cfg.dont_fragment = true;
 
@@ -907,7 +916,8 @@  get_tunnel_config(const struct netdev *dev, struct smap *args)
         if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
             (!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
             (!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
-            (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
+            (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
+            (!strcmp("gtpu", type) && dst_port != GTPU_DST_PORT)) {
             smap_add_format(args, "dst_port", "%d", dst_port);
         }
     }
@@ -1223,6 +1233,17 @@  netdev_vport_tunnel_register(void)
           },
           {{NULL, NULL, 0, 0}}
         },
+        { "gtpu_sys",
+          {
+              TUNNEL_FUNCTIONS_COMMON,
+              .type = "gtpu",
+              .build_header = netdev_gtpu_build_header,
+              .push_header = netdev_tnl_push_udp_header,
+              .pop_header = netdev_gtpu_pop_header,
+          },
+          {{NULL, NULL, 0, 0}}
+        },
+
     };
     static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
 
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 0432ad4de6a7..058816c7b88f 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -1051,7 +1051,7 @@  nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
     ovs_be32 spi_mask;
     int match_len;
 
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     struct nxm_put_ctx ctx = { .output = b, .implied_ethernet = false };
 
@@ -1191,6 +1191,12 @@  nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
     nxm_put_8m(&ctx, MFF_TUN_ERSPAN_HWID, oxm,
                 flow->tunnel.erspan_hwid, match->wc.masks.tunnel.erspan_hwid);
 
+    /* GTP-U */
+    nxm_put_8m(&ctx, MFF_TUN_GTPU_FLAGS, oxm, flow->tunnel.gtpu_flags,
+               match->wc.masks.tunnel.gtpu_flags);
+    nxm_put_8m(&ctx, MFF_TUN_GTPU_MSGTYPE, oxm, flow->tunnel.gtpu_msgtype,
+               match->wc.masks.tunnel.gtpu_msgtype);
+
     /* Network Service Header */
     nxm_put_8m(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags,
             match->wc.masks.nsh.flags);
diff --git a/lib/odp-util.c b/lib/odp-util.c
index c3be2a59697f..9b835da4a934 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -755,7 +755,17 @@  format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
         } else {
             VLOG_WARN("%s Invalid ERSPAN version %d\n", __func__, ersh->ver);
         }
+    } else if (data->tnl_type == OVS_VPORT_TYPE_GTPU) {
+        const struct gtpuhdr *gtph;
+
+        gtph = format_udp_tnl_push_header(ds, udp);
+
+        ds_put_format(ds, "gtpu(flags=0x%"PRIx8
+                          ",msgtype=0x%"PRIx8",teid=0x%"PRIx32")",
+                      gtph->md.flags, gtph->md.msgtype,
+                      ntohl(get_16aligned_be32(&gtph->teid)));
     }
+
     ds_put_format(ds, ")");
 }
 
@@ -1496,6 +1506,8 @@  ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
     void *l3, *l4;
     int n = 0;
     uint8_t hwid, dir;
+    uint32_t teid;
+    uint8_t gtpu_flags, gtpu_msgtype;
 
     if (!ovs_scan_len(s, &n, "tnl_push(tnl_port(%"SCNi32"),", &data->tnl_port)) {
         return -EINVAL;
@@ -1725,6 +1737,18 @@  ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
 
         header_len = sizeof *eth + ip_len + ERSPAN_GREHDR_LEN +
                      sizeof *ersh + ERSPAN_V2_MDSIZE;
+
+    } else if (ovs_scan_len(s, &n, "gtpu(flags=0x%"SCNx8",msgtype=0x%"
+                SCNx8",teid=0x%"SCNx32"))",
+                &gtpu_flags, &gtpu_msgtype, &teid)) {
+        struct gtpuhdr *gtph = (struct gtpuhdr *) (udp + 1);
+
+        gtph->md.flags = gtpu_flags;
+        gtph->md.msgtype = gtpu_msgtype;
+        put_16aligned_be32(&gtph->teid, htonl(teid));
+        tnl_type = OVS_VPORT_TYPE_GTPU;
+        header_len = sizeof *eth + ip_len +
+                     sizeof *udp + sizeof *gtph;
     } else {
         return -EINVAL;
     }
@@ -2625,6 +2649,7 @@  static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX +
     [OVS_TUNNEL_KEY_ATTR_IPV6_SRC]      = { .len = 16 },
     [OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = 16 },
     [OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS]   = { .len = ATTR_LEN_VARIABLE },
+    [OVS_TUNNEL_KEY_ATTR_GTPU_OPTS]   = { .len = ATTR_LEN_VARIABLE },
 };
 
 const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
@@ -3030,6 +3055,13 @@  odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask,
             }
             break;
         }
+        case OVS_TUNNEL_KEY_ATTR_GTPU_OPTS: {
+            const struct gtpu_metadata *opts = nl_attr_get(a);
+
+            tun->gtpu_flags = opts->flags;
+            tun->gtpu_msgtype = opts->msgtype;
+            break;
+        }
 
         default:
             /* Allow this to show up as unexpected, if there are unknown
@@ -3640,6 +3672,22 @@  format_odp_tun_erspan_opt(const struct nlattr *attr,
     ds_chomp(ds, ',');
 }
 
+static void
+format_odp_tun_gtpu_opt(const struct nlattr *attr,
+                        const struct nlattr *mask_attr, struct ds *ds,
+                        bool verbose)
+{
+    const struct gtpu_metadata *opts, *mask;
+
+    opts = nl_attr_get(attr);
+    mask = mask_attr ? nl_attr_get(mask_attr) : NULL;
+
+    format_u8x(ds, "flags", opts->flags, mask ? &mask->flags : NULL, verbose);
+    format_u8x(ds, "msgtype", opts->msgtype, mask ? &mask->msgtype : NULL,
+               verbose);
+    ds_chomp(ds, ',');
+}
+
 #define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
 
 static void
@@ -3892,6 +3940,11 @@  format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
             format_odp_tun_erspan_opt(a, ma, ds, verbose);
             ds_put_cstr(ds, "),");
             break;
+        case OVS_TUNNEL_KEY_ATTR_GTPU_OPTS:
+            ds_put_cstr(ds, "gtpu(");
+            format_odp_tun_gtpu_opt(a, ma, ds, verbose);
+            ds_put_cstr(ds, ")");
+            break;
         case __OVS_TUNNEL_KEY_ATTR_MAX:
         default:
             format_unknown_key(ds, a, ma);
@@ -5100,6 +5153,50 @@  scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
 }
 
 static int
+scan_gtpu_metadata(const char *s,
+                   struct gtpu_metadata *key,
+                   struct gtpu_metadata *mask)
+{
+    const char *s_base = s;
+    uint8_t flags, flags_ma;
+    uint8_t msgtype, msgtype_ma;
+    int len;
+
+    if (!strncmp(s, "flags=", 6)) {
+        s += 6;
+        len = scan_u8(s, &flags, mask ? &flags_ma : NULL);
+        if (len == 0) {
+            return 0;
+        }
+        s += len;
+    }
+
+    if (s[0] == ',') {
+        s++;
+    }
+
+    if (!strncmp(s, "msgtype=", 8)) {
+        s += 8;
+        len = scan_u8(s, &msgtype, mask ? &msgtype_ma : NULL);
+        if (len == 0) {
+            return 0;
+        }
+        s += len;
+    }
+
+    if (!strncmp(s, ")", 1)) {
+        s += 1;
+        key->flags = flags;
+        key->msgtype = msgtype;
+        if (mask) {
+            mask->flags = flags_ma;
+            mask->msgtype = msgtype_ma;
+        }
+    }
+    return s - s_base;
+}
+
+static int
 scan_erspan_metadata(const char *s,
                      struct erspan_metadata *key,
                      struct erspan_metadata *mask)
@@ -5339,6 +5436,15 @@  erspan_to_attr(struct ofpbuf *a, const void *data_)
                       sizeof *md);
 }
 
+static void
+gtpu_to_attr(struct ofpbuf *a, const void *data_)
+{
+    const struct gtpu_metadata *md = data_;
+
+    nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_GTPU_OPTS, md,
+                      sizeof *md);
+}
+
 #define SCAN_PUT_ATTR(BUF, ATTR, DATA, FUNC)                      \
     {                                                             \
         unsigned long call_fn = (unsigned long)FUNC;              \
@@ -5725,6 +5831,8 @@  parse_odp_key_mask_attr__(struct parse_odp_context *context, const char *s,
         SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr);
         SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve,
                                geneve_to_attr);
+        SCAN_FIELD_NESTED_FUNC("gtpu(", struct gtpu_metadata, gtpu_metadata,
+                               gtpu_to_attr);
         SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
     } SCAN_END_NESTED();
 
@@ -5992,7 +6100,7 @@  odp_flow_key_from_flow__(const struct odp_flow_key_parms *parms,
     /* New "struct flow" fields that are visible to the datapath (including all
      * data fields) should be translated into equivalent datapath flow fields
      * here (you will have to add a OVS_KEY_ATTR_* for them). */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     struct ovs_key_ethernet *eth_key;
     size_t encap[FLOW_MAX_VLAN_HEADERS] = {0};
@@ -7080,7 +7188,7 @@  odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
     /* New "struct flow" fields that are visible to the datapath (including all
      * data fields) should be translated from equivalent datapath flow fields
      * here (you will have to add a OVS_KEY_ATTR_* for them).  */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     enum odp_key_fitness fitness = ODP_FIT_ERROR;
     if (errorp) {
@@ -8429,7 +8537,7 @@  commit_odp_actions(const struct flow *flow, struct flow *base,
     /* If you add a field that OpenFlow actions can change, and that is visible
      * to the datapath (including all data fields), then you should also add
      * code here to commit changes to the field. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     enum slow_path_reason slow1, slow2;
     bool mpls_done = false;
diff --git a/lib/odp-util.h b/lib/odp-util.h
index f15e258e6b4a..8e6bc23a543c 100644
--- a/lib/odp-util.h
+++ b/lib/odp-util.h
@@ -147,7 +147,7 @@  void odp_portno_name_format(const struct hmap *portno_names,
  * add another field and forget to adjust this value.
  */
 #define ODPUTIL_FLOW_KEY_BYTES 640
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
 /* A buffer with sufficient size and alignment to hold an nlattr-formatted flow
  * key.  An array of "struct nlattr" might not, in theory, be sufficiently
diff --git a/lib/ofp-match.c b/lib/ofp-match.c
index 2ec28f8036c0..86a082dde141 100644
--- a/lib/ofp-match.c
+++ b/lib/ofp-match.c
@@ -65,7 +65,7 @@  ofputil_netmask_to_wcbits(ovs_be32 netmask)
 void
 ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
 {
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
     /* Initialize most of wc. */
     flow_wildcards_init_catchall(wc);
diff --git a/lib/packets.h b/lib/packets.h
index 5d7f82c45b6a..6a4d3fb87392 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1447,6 +1447,64 @@  static inline ovs_be32 get_erspan_ts(enum erspan_ts_gra gra)
     return ts;
 }
 
+/*
+ * GTP-U protocol header and metadata
+ * See:
+ *   User Plane Protocol and Architectural Analysis on 3GPP 5G System
+ *                 draft-hmm-dmm-5g-uplane-analysis-00
+ *
+ * 0                   1                   2                   3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Ver |P|R|E|S|N| Message Type|             Length              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                Tunnel Endpoint Identifier                     |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |      Sequence Number        |   N-PDU Number  |  Next-Ext-Hdr |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * GTP-U Flags:
+ *   P: Protocol Type (Set to '1')
+ *   R: Reserved Bit (Set to '0')
+ *   E: Extension Header Flag (Set to '1' if extension header exists)
+ *   S: Sequence Number Flag (Set to '1' if sequence number exists)
+ *   N: N-PDU Number Flag (Set to '1' if N-PDU number exists)
+ *
+ * GTP-U Message Type:
+ *   Indicates the type of GTP-U message.
+ *
+ * GTP-U Length:
+ *   Indicates the length in octets of the payload.
+ *
+ * User payload is transmitted in G-PDU packets.
+ */
+
+#define GTPU_VER_MASK   0xe0
+#define GTPU_P_MASK     0x10
+#define GTPU_E_MASK     0x04
+
+/* GTP-U UDP port. */
+#define GTPU_DST_PORT   2152
+
+/* Default GTP-U flags: Ver = 1 and P = 1. */
+#define GTPU_FLAGS_DEFAULT  0x30
+
+/* GTP-U message type for normal user plane PDU. */
+#define GTPU_MSGTYPE_REQ    1   /* Echo Request. */
+#define GTPU_MSGTYPE_REPL   2   /* Echo Reply. */
+#define GTPU_MSGTYPE_GPDU   255 /* User Payload. */
+
+struct gtpu_metadata {
+    uint8_t flags;
+    uint8_t msgtype;
+};
+
+struct gtpuhdr {
+    struct gtpu_metadata md;
+    ovs_be16 len;
+    ovs_16aligned_be32 teid;
+};
+
 /* VXLAN protocol header */
 struct vxlanhdr {
     union {
@@ -1543,6 +1601,7 @@  enum packet_type {
     PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS),
     PT_MPLS_MC = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS_MCAST),
     PT_NSH  = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_NSH),
+    PT_GTPU_MSG = PACKET_TYPE(OFPHTN_UDP_TCP_PORT, GTPU_DST_PORT),
     PT_UNKNOWN = PACKET_TYPE(0xffff, 0xffff),   /* Unknown packet type. */
 };
 
diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c
index 17353046cc6e..446b40763035 100644
--- a/lib/tnl-ports.c
+++ b/lib/tnl-ports.c
@@ -178,6 +178,9 @@  tnl_type_to_nw_proto(const char type[])
     if (!strcmp(type, "vxlan")) {
         return IPPROTO_UDP;
     }
+    if (!strcmp(type, "gtpu")) {
+        return IPPROTO_UDP;
+    }
     return 0;
 }
 
diff --git a/ofproto/ofproto-dpif-rid.h b/ofproto/ofproto-dpif-rid.h
index 147ef9c33348..e5d02caf28a3 100644
--- a/ofproto/ofproto-dpif-rid.h
+++ b/ofproto/ofproto-dpif-rid.h
@@ -99,7 +99,7 @@  struct rule;
 /* Metadata for restoring pipeline context after recirculation.  Helpers
  * are inlined below to keep them together with the definition for easier
  * updates. */
-BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
 
 struct frozen_metadata {
     /* Metadata in struct flow. */
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index daf7fec80c9e..841bb13df1f9 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -3542,6 +3542,7 @@  propagate_tunnel_data_to_flow(struct xlate_ctx *ctx, struct eth_addr dmac,
         break;
     case OVS_VPORT_TYPE_VXLAN:
     case OVS_VPORT_TYPE_GENEVE:
+    case OVS_VPORT_TYPE_GTPU:
         nw_proto = IPPROTO_UDP;
         break;
     case OVS_VPORT_TYPE_LISP:
@@ -4093,7 +4094,7 @@  compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
 
     /* If 'struct flow' gets additional metadata, we'll need to zero it out
      * before traversing a patch port. */
-    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41);
+    BUILD_ASSERT_DECL(FLOW_WC_SEQ == 42);
     memset(&flow_tnl, 0, sizeof flow_tnl);
 
     if (!check_output_prerequisites(ctx, xport, flow, check_stp)) {
@@ -7581,7 +7582,8 @@  xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
     }
 
     if (flow->packet_type != htonl(PT_ETH) && in_port &&
-        in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0) {
+        in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0 &&
+        flow->packet_type != htonl(PT_GTPU_MSG)) {
         /* Add dummy Ethernet header to non-L2 packet if it's coming from a
          * L3 port. So all packets will be L2 packets for lookup.
          * The dl_type has already been set from the packet_type. */
diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c
index 03f0ab76562a..6a8bf40f17b1 100644
--- a/ofproto/tunnel.c
+++ b/ofproto/tunnel.c
@@ -585,7 +585,8 @@  tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock)
                     match.ip_src_flow = ip_src == IP_SRC_FLOW;
 
                     /* Look for a legacy L2 or L3 tunnel port first. */
-                    if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) {
+                    if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE ||
+                        flow->packet_type == htonl(PT_GTPU_MSG)) {
                         match.pt_mode = NETDEV_PT_LEGACY_L3;
                     } else {
                         match.pt_mode = NETDEV_PT_LEGACY_L2;
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 2436434ca9b7..48260c880dcd 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -2406,7 +2406,7 @@  head_table () {
       instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
       Write-Actions and Apply-Actions features:
         actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
-        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver tun_erspan_dir tun_erspan_hwid tun_metadata0 dnl
+        supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver tun_erspan_dir tun_erspan_hwid tun_gtpu_flags tun_gtpu_msgtype tun_metadata0 dnl
 tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl
 metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nd_reserved nd_options_type nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
     matching:
@@ -2426,6 +2426,8 @@  metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
       tun_erspan_ver: arbitrary mask
       tun_erspan_dir: arbitrary mask
       tun_erspan_hwid: arbitrary mask
+      tun_gtpu_flags: arbitrary mask
+      tun_gtpu_msgtype: arbitrary mask
       tun_metadata0: arbitrary mask
       tun_metadata1: arbitrary mask
       tun_metadata2: arbitrary mask
diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
index f7172433ee63..64e77056d5d1 100644
--- a/tests/tunnel-push-pop.at
+++ b/tests/tunnel-push-pop.at
@@ -216,6 +216,8 @@  AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
                        options:remote_ip=1.1.2.92 options:key=456 options:packet_type=legacy_l3 ofport_request=7\
                     -- add-port int-br t7 -- set Interface t7 type=vxlan \
                        options:remote_ip=1.1.2.92 options:key=345 options:exts=gpe ofport_request=8\
+                    -- add-port int-br t8 -- set Interface t8 type=gtpu \
+                       options:remote_ip=1.1.2.92 options:key=123 ofport_request=9\
                        ], [0])
 
 AT_CHECK([ovs-appctl dpif/show], [0], [dnl
@@ -232,6 +234,7 @@  dummy@ovs-dummy: hit:0 missed:0
     t5 6/6081: (geneve: egress_pkt_mark=1234, out_key=flow, remote_ip=1.1.2.93)
     t6 7/3: (gre: key=456, packet_type=legacy_l3, remote_ip=1.1.2.92)
     t7 8/4789: (vxlan: key=345, remote_ip=1.1.2.92)
+    t8 9/2152: (gtpu: key=123, remote_ip=1.1.2.92)
 ])
 
 dnl First setup dummy interface IP address, then add the route
@@ -342,6 +345,7 @@  AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
 Listening ports:
 genev_sys_6081 (6081) ref_cnt=2
 gre_sys (3) ref_cnt=2
+gtpu_sys_2152 (2152) ref_cnt=1
 vxlan_sys_4789 (4789) ref_cnt=3
 ])
 
@@ -369,6 +373,13 @@  AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: tnl_pop(6081)
 ])
 
+dnl Check GTP-U tunnel pop
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=2152)'],
+[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: tnl_pop(2152)
+])
+
 dnl Check VXLAN tunnel push
 AT_CHECK([ovs-ofctl add-flow int-br action=2])
 AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
@@ -426,6 +437,15 @@  AT_CHECK([tail -1 stdout], [0],
   [Datapath actions: clone(tnl_push(tnl_port(6081),header(size=58,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x7b,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(100)),1)
 ])
 
+dnl Check GTP-U tunnel push
+AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:0x30->tun_gtpu_flags,set_field:0xff->tun_gtpu_msgtype,9"])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'],
+[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: pop_eth,clone(tnl_push(tnl_port(2152),header(size=50,type=110,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=2152,csum=0x0),gtpu(flags=0x30,msgtype=0xff,teid=0x7b)),out_port(100)),1)
+])
+AT_CHECK([ovs-ofctl del-flows int-br])
+
 dnl Check decapsulation of GRE packet
 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
 AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
@@ -494,6 +514,7 @@  AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
 Listening ports:
 genev_sys_6081 (6081) ref_cnt=1
 gre_sys (3) ref_cnt=1
+gtpu_sys_2152 (2152) ref_cnt=1
 vxlan_sys_4789 (4789) ref_cnt=2
 vxlan_sys_4790 (4790) ref_cnt=1
 ])
@@ -503,6 +524,7 @@  AT_CHECK([ovs-vsctl del-port int-br t1 \
                     -- del-port int-br t4 \
                     -- del-port int-br t6 \
                     -- del-port int-br t7 \
+                    -- del-port int-br t8 \
                        ], [0])
 
 dnl Check tunnel lookup entries after deleting all remaining tunnel ports
diff --git a/tests/tunnel.at b/tests/tunnel.at
index faffb4149c3b..328a7078663c 100644
--- a/tests/tunnel.at
+++ b/tests/tunnel.at
@@ -1029,3 +1029,81 @@  AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
 
 OVS_VSWITCHD_STOP
 AT_CLEANUP
+
+AT_SETUP([tunnel - GTP-U basic])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gtpu \
+                    options:remote_ip=1.1.1.1 \
+                    options:key=123 ofport_request=1])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
+    br0 65534/100: (dummy-internal)
+    p1 1/2152: (gtpu: key=123, remote_ip=1.1.1.1)
+])
+
+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
+Listening ports:
+gtpu_sys_2152 (2152) ref_cnt=1
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - GTP-U and GRE])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \
+                    ofport_request=1 \
+                    -- add-port br0 p2 -- set Interface p2 type=dummy \
+                    ofport_request=2])
+
+# Add these ports separately to ensure that they get the datapath port
+# number expected below.
+ovs-vsctl -- add-port br0 p3 \
+          -- set Interface p3 type=gtpu \
+                              ofport_request=3 \
+                              options:remote_ip=1.1.1.1 \
+                              options:key=3 \
+                              options:packet_type=legacy_l3
+ovs-vsctl -- add-port br0 p4 \
+          -- set Interface p4 type=gtpu \
+                              ofport_request=4 \
+                              options:remote_ip=1.1.1.2 \
+                              options:key=4 \
+                              options:packet_type=legacy_l3
+ovs-vsctl -- add-port br0 l3gre \
+          -- set Interface l3gre type=gre \
+                              ofport_request=5 \
+                              options:remote_ip=1.1.1.2 \
+                              options:packet_type=legacy_l3
+OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
+
+dnl AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+    l3gre 5/3: (gre: packet_type=legacy_l3, remote_ip=1.1.1.2)
+    p1 1/1: (dummy)
+    p2 2/2: (dummy)
+    p3 3/2152: (gtpu: key=3, remote_ip=1.1.1.1)
+    p4 4/2152: (gtpu: key=4, remote_ip=1.1.1.2)
+])
+
+AT_DATA([flows.txt], [dnl
+in_port=1,actions=3
+in_port=2,actions=4
+in_port=3,tun_gtpu_flags=0x30,tun_gtpu_msgtype=0xff,actions=5
+in_port=4,tun_gtpu_flags=0x30,tun_gtpu_msgtype=0xff,actions=2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+dnl Encap: in_port=1,actions=3
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+  [Datapath actions: set(tunnel(tun_id=0x3,dst=1.1.1.1,ttl=64,tp_dst=2152,flags(df|key))),pop_eth,2152
+])
+
+dnl receive packet from GTP-U port, match it, and output to layer3 GRE
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,ttl=64,gtpu(flags=0x30,msgtype=0xff),flags(df|key)),in_port(2152),packet_type(ns=3,id=2152),skb_mark(0),ipv4(frag=no)'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+  [Megaflow: recirc_id=0,packet_type=(3,0x868),ip,tun_id=0x3,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0,tun_flags=+df-csum+key,in_port=3
+Datapath actions: set(tunnel(dst=1.1.1.2,ttl=64,flags(df))),3
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index b311ef27826a..c08f3e9c16a5 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -2615,6 +2615,30 @@ 
           <dd>
             A pair of virtual devices that act as a patch cable.
           </dd>
+
+          <dt><code>gtpu</code></dt>
+          <dd>
+            <p>
+            GPRS Tunneling Protocol (GTP) is a group of IP-based communications
+            protocols used to carry general packet radio service (GPRS) within
+            GSM, UMTS and LTE networks. GTP-U is used for carrying user data
+            within the GPRS core network and between the radio access network
+            and the core network. The user data transported can be packets in
+            any of IPv4, IPv6, or PPP formats.
+            </p>
+
+            <p>
+            The protocol is documented at
+            http://www.3gpp.org/DynaReport/29281.htm
+            </p>
+
+            <p>
+            Open vSwitch uses UDP destination port 2152. The source port used
+            for GTP traffic varies on a per-flow basis and is in the ephemeral
+            port range.
+            </p>
+          </dd>
+
         </dl>
       </column>
     </group>