[ovs-dev] [PATCHv6] userspace: Add GTP-U support.
Roni Bar Yanai
roniba at mellanox.com
Tue Jan 21 19:20:54 UTC 2020
Hi William,
Two comments, please inline.
>-----Original Message-----
>From: William Tu <u9012063 at gmail.com>
>Sent: Thursday, January 16, 2020 9:45 PM
>To: dev at openvswitch.org
>Cc: Roni Bar Yanai <roniba at mellanox.com>; aconole at redhat.com;
>yangyi01 at inspur.com; fhallal at redhat.com; ashishvarma.ovs at gmail.com;
>blp at ovn.org
>Subject: [PATCHv6] 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&data=02%7C01%7Croniba%40mellanox.com%7Cf870ce16727e40fbf3cf08d
>79abcc4ef%7Ca652971c7d2e4d9ba6a4d149256f461b%7C0%7C0%7C6371480078905
>90483&sdata=vU5nHP%2BTF1GqyHJfyfyYKhdla%2FT8BblYRCu2NUfxt7Q%3D
>&reserved=0
>
>Signed-off-by: Yi Yang <yangyi01 at inspur.com>
>Co-authored-by: Yi Yang <yangyi01 at inspur.com>
>Signed-off-by: William Tu <u9012063 at gmail.com>
>---
>v5 -> v6:
> - rebase to master
> - travis:
>https://eur03.safelinks.protection.outlook.com/?url=https%3A%2F%2Ftravis-
>ci.org%2Fwilliamtu%2Fovs-
>travis%2Fbuilds%2F638083655&data=02%7C01%7Croniba%40mellanox.com%
>7Cf870ce16727e40fbf3cf08d79abcc4ef%7Ca652971c7d2e4d9ba6a4d149256f461b%7
>C0%7C0%7C637148007890590483&sdata=lgdyvMiOvCjgEiLcFtqJvW2n2sm2clB
>aBqkwoWCzBbg%3D&reserved=0
>
>v4 -> v5:
> - address Ben and Aaron comments
> 1) flow_get_metadata, format_flow_tunnel
> 2) use of ?: in MSVS
> 3) tun_key_to_attr
> 4) handling PT_GTPU_MSG packets
> 5) make gtpu_flags and gtpu_msgtype read-only
> - use be32_to_be64 and be64_to_be32
> - make gtpu_flags as hexadicimal, gtpu_msgtype as decimal
> - remove PT_GTPU_MSG
> Address Roni's comments
> - for non-GPDU msgtype, don't pop header
> - add seq number flags parsing, push/pop header support
> - refactor netdev_tnl_push_udp_header()
>
>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&data=02%7C01%7Croniba%40mellanox.com%
>7Cf870ce16727e40fbf3cf08d79abcc4ef%7Ca652971c7d2e4d9ba6a4d149256f461b%7
>C0%7C0%7C637148007890590483&sdata=6%2FPIBAFpCtpWXip89BU8B3R6S8B
>2CZFWRxB3rYNRp9Q%3D&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&data=02%7C01%7Croniba%40mellanox.com%
>7Cf870ce16727e40fbf3cf08d79abcc4ef%7Ca652971c7d2e4d9ba6a4d149256f461b%7
>C0%7C0%7C637148007890590483&sdata=ESUBrHITGL7Tn4PuyoCiWq0%2F%2
>FaxTDqIuckfw2ynrAyQ%3D&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 | 28 ++--
> lib/flow.h | 2 +-
> lib/match.c | 36 ++++-
> lib/meta-flow.c | 38 +++++
> lib/meta-flow.xml | 79 ++++++++++-
> lib/netdev-native-tnl.c | 165 ++++++++++++++++++++--
> lib/netdev-native-tnl.h | 13 ++
> lib/netdev-vport.c | 25 +++-
> lib/nx-match.c | 8 +-
> lib/odp-util.c | 123 +++++++++++++++-
> lib/odp-util.h | 2 +-
> lib/ofp-match.c | 2 +-
> lib/packets.h | 68 +++++++++
> lib/tnl-ports.c | 3 +
> ofproto/ofproto-dpif-rid.h | 2 +-
> ofproto/ofproto-dpif-xlate.c | 3 +-
> tests/ofproto.at | 2 +-
> tests/tunnel-push-pop.at | 22 +++
> tests/tunnel.at | 76 ++++++++++
> vswitchd/vswitch.xml | 24 ++++
> 30 files changed, 752 insertions(+), 40 deletions(-)
>
>diff --git a/Documentation/faq/configuration.rst
>b/Documentation/faq/configuration.rst
>index ff3b71a5d4ef..4a98740c5d4d 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. 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.
>+
>+ ::
>+
>+ $ 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 e8d662a0c15f..a7ec65dd844d 100644
>--- a/NEWS
>+++ b/NEWS
>@@ -39,6 +39,9 @@ Post-v2.12.0
> - 'ovs-appctl dpctl/dump-flows' can now show offloaded=partial for
> partially offloaded flows, dp:dpdk for fully offloaded by dpdk, and
> type filter supports new filters: "dpdk" and "partially-offloaded".
>+ - 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 2f0c6559eaf5..f7c3b2e99a78 100644
>--- a/datapath/linux/compat/include/linux/openvswitch.h
>+++ b/datapath/linux/compat/include/linux/openvswitch.h
>@@ -245,6 +245,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
> };
>
>@@ -404,6 +405,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 eeabd5f470a7..8af3b74ed3e0 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 1f81d830e70f..d529a9f0d21c 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: hexadecimal.
>+ * Prerequisites: none.
>+ * Access: read-only.
>+ * 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-only.
>+ * 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 d865c0bdb939..24d0cd3a5466 100644
>--- a/lib/dpif-netlink.c
>+++ b/lib/dpif-netlink.c
>@@ -745,6 +745,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;
>@@ -778,6 +781,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..0ba482045847 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)) {
>@@ -1228,6 +1228,12 @@ flow_get_metadata(const struct flow *flow, struct
>match *flow_metadata)
> if (flow->tunnel.erspan_hwid) {
> match_set_tun_erspan_hwid(flow_metadata, flow->tunnel.erspan_hwid);
> }
>+ if (flow->tunnel.gtpu_flags) {
>+ match_set_tun_gtpu_flags(flow_metadata, flow->tunnel.gtpu_flags);
>+ }
>+ if (flow->tunnel.gtpu_msgtype) {
>+ match_set_tun_gtpu_msgtype(flow_metadata, flow-
>>tunnel.gtpu_msgtype);
>+ }
> tun_metadata_get_fmd(&flow->tunnel, flow_metadata);
> if (flow->metadata != htonll(0)) {
> match_set_metadata(flow_metadata, flow->metadata);
>@@ -1768,7 +1774,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 +1795,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 +1927,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 +2030,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 +2174,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 +2221,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 +2899,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 +3197,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 0d1ec31ef843..25c277cc670b 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);
>@@ -1325,6 +1353,12 @@ format_flow_tunnel(struct ds *s, const struct match
>*match)
> if (wc->masks.tunnel.erspan_hwid && tnl->erspan_ver == 2) {
> ds_put_format(s, "tun_erspan_hwid=%#"PRIx8",", tnl->erspan_hwid);
> }
>+ if (wc->masks.tunnel.gtpu_flags) {
>+ ds_put_format(s, "gtpu_flags=%#"PRIx8",", tnl->gtpu_flags);
>+ }
>+ if (wc->masks.tunnel.gtpu_msgtype) {
>+ ds_put_format(s, "gtpu_msgtype=%"PRIu8",", tnl->gtpu_msgtype);
>+ }
> if (wc->masks.tunnel.flags & FLOW_TNL_F_MASK) {
> format_flags_masked(s, "tun_flags", flow_tun_flag_to_string,
> tnl->flags & FLOW_TNL_F_MASK,
>@@ -1396,7 +1430,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 8b62e6d96835..9ab82460bfc4 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..16e28802114b 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;
>
>@@ -213,6 +216,27 @@ udp_extract_tnl_md(struct dp_packet *packet, struct
>flow_tnl *tnl,
> return udp + 1;
> }
>
>+static void
>+netdev_tnl_update_udp_csum(struct udp_header *udp, struct dp_packet
>*packet,
>+ int ip_tot_size)
>+{
>+ uint32_t csum;
>+
>+ if (netdev_tnl_is_header_ipv6(dp_packet_data(packet))) {
>+ csum = packet_csum_pseudoheader6(netdev_tnl_ipv6_hdr(
>+ dp_packet_data(packet)));
>+ } else {
>+ csum = packet_csum_pseudoheader(netdev_tnl_ip_hdr(
>+ dp_packet_data(packet)));
>+ }
>+
>+ csum = csum_continue(csum, udp, ip_tot_size);
>+ udp->udp_csum = csum_finish(csum);
>+
>+ if (!udp->udp_csum) {
>+ udp->udp_csum = htons(0xffff);
>+ }
>+}
>
> void
> netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,
>@@ -229,19 +253,7 @@ netdev_tnl_push_udp_header(const struct netdev
>*netdev OVS_UNUSED,
> udp->udp_len = htons(ip_tot_size);
>
> if (udp->udp_csum) {
>- uint32_t csum;
>- if (netdev_tnl_is_header_ipv6(dp_packet_data(packet))) {
>- csum =
>packet_csum_pseudoheader6(netdev_tnl_ipv6_hdr(dp_packet_data(packet)));
>- } else {
>- csum =
>packet_csum_pseudoheader(netdev_tnl_ip_hdr(dp_packet_data(packet)));
>- }
>-
>- csum = csum_continue(csum, udp, ip_tot_size);
>- udp->udp_csum = csum_finish(csum);
>-
>- if (!udp->udp_csum) {
>- udp->udp_csum = htons(0xffff);
>- }
>+ netdev_tnl_update_udp_csum(udp, packet, ip_tot_size);
> }
> }
>
>@@ -708,6 +720,133 @@ 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 gtpu_hlen;
>+ 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;
>+ }
>+
>+ tnl->gtpu_flags = gtph->md.flags;
>+ tnl->gtpu_msgtype = gtph->md.msgtype;
>+ tnl->tun_id = be32_to_be64(get_16aligned_be32(>ph->teid));
>+
>+ if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) {
>+ struct ip_header *ip;
>+
>+ if (gtph->md.flags & GTPU_S_MASK) {
>+ gtpu_hlen = GTPU_HLEN + sizeof(struct gtpuhdr_opt);
>+ } else {
>+ gtpu_hlen = GTPU_HLEN;
>+ }
>+ ip = ALIGNED_CAST(struct ip_header *, (char *)gtph + gtpu_hlen);
>+
>+ 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.");
>+ }
>+ dp_packet_reset_packet(packet, hlen + gtpu_hlen);
GTP internal packet has no l2, so stripping it without putting some dummy
L2 or keeping outer one, sounds like a problem. How OVS can continue
the processing? Or do expect user to do explicit encap?
>+ } else {
>+ /* non-GPDU GTP-U messages, ex: echo request, end marker.
>+ * Users should redirect these packets to controller, or.
>+ * any application that handles GTP-U messages, so keep
>+ * the original packet.
>+ */
>+ packet->packet_type = htonl(PT_ETH);
>+ VLOG_WARN_ONCE("Receive non-GPDU msgtype: %"PRIu8,
>+ gtph->md.msgtype);
>+ }
>+
>+ return packet;
>+
>+err:
>+ dp_packet_delete(packet);
>+ return NULL;
>+}
>+
>+void
>+netdev_gtpu_push_header(const struct netdev *netdev,
>+ struct dp_packet *packet,
>+ const struct ovs_action_push_tnl *data)
>+{
>+ struct netdev_vport *dev = netdev_vport_cast(netdev);
>+ struct netdev_tunnel_config *tnl_cfg;
>+ struct udp_header *udp;
>+ struct gtpuhdr *gtpuh;
>+ int ip_tot_size;
>+ unsigned int payload_len;
>+
>+ payload_len = dp_packet_size(packet);
>+ udp = netdev_tnl_push_ip_header(packet, data->header,
>+ data->header_len, &ip_tot_size);
Same here, you need to pop the l2 before pushing the tunnel.
>+ udp->udp_src = netdev_tnl_get_src_port(packet);
>+ udp->udp_len = htons(ip_tot_size);
>+ netdev_tnl_update_udp_csum(udp, packet, ip_tot_size);
>+
>+ gtpuh = ALIGNED_CAST(struct gtpuhdr *, udp + 1);
>+
>+ tnl_cfg = &dev->tnl_cfg;
>+ if (tnl_cfg->set_seq) {
>+ ovs_be16 *seqno = ALIGNED_CAST(ovs_be16 *, gtpuh + 1);
>+ *seqno = be32_to_be16(htonl(tnl_cfg->seqno++));
>+ payload_len += sizeof(struct gtpuhdr_opt);
>+ }
>+ gtpuh->len = htons(payload_len);
>+}
>+
>+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;
>+ unsigned int gtpu_hlen;
>+
>+ ovs_mutex_lock(&dev->mutex);
>+ tnl_cfg = &dev->tnl_cfg;
>+ gtph = udp_build_header(tnl_cfg, data, params);
>+
>+ /* Set to default if not set in flow. */
>+ gtph->md.flags = params->flow->tunnel.gtpu_flags ?
>+ params->flow->tunnel.gtpu_flags : GTPU_FLAGS_DEFAULT;
>+ gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ?
>+ params->flow->tunnel.gtpu_msgtype : GTPU_MSGTYPE_GPDU;
>+ put_16aligned_be32(>ph->teid,
>+ be64_to_be32(params->flow->tunnel.tun_id));
>+
>+ gtpu_hlen = sizeof *gtph;
>+ if (tnl_cfg->set_seq) {
>+ gtph->md.flags |= GTPU_S_MASK;
>+ gtpu_hlen += sizeof(struct gtpuhdr_opt);
>+ }
>+ ovs_mutex_unlock(&dev->mutex);
>+
>+ data->header_len += gtpu_hlen;
>+ 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..22ae2ce5369b 100644
>--- a/lib/netdev-native-tnl.h
>+++ b/lib/netdev-native-tnl.h
>@@ -52,6 +52,19 @@ netdev_erspan_push_header(const struct netdev *netdev,
> struct dp_packet *
> netdev_erspan_pop_header(struct dp_packet *packet);
>
>+struct dp_packet *
>+netdev_gtpu_pop_header(struct dp_packet *packet);
>+
>+void
>+netdev_gtpu_push_header(const struct netdev *netdev,
>+ struct dp_packet *packet,
>+ const struct ovs_action_push_tnl *data);
>+
>+int
>+netdev_gtpu_build_header(const struct netdev *netdev,
>+ struct ovs_action_push_tnl *data,
>+ const struct netdev_tnl_build_header_params *p);
>+
> 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..8efd1eee8302 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_gtpu_push_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 746d1e97d474..2a6a38622afb 100644
>--- a/lib/odp-util.c
>+++ b/lib/odp-util.c
>@@ -756,7 +756,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=%"PRIu8",teid=0x%"PRIx32")",
>+ gtph->md.flags, gtph->md.msgtype,
>+ ntohl(get_16aligned_be32(>ph->teid)));
> }
>+
> ds_put_format(ds, ")");
> }
>
>@@ -1500,6 +1510,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;
>@@ -1729,6 +1741,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=%"
>+ SCNu8",teid=0x%"SCNx32"))",
>+ >pu_flags, >pu_msgtype, &teid)) {
>+ struct gtpuhdr *gtph = (struct gtpuhdr *) (udp + 1);
>+
>+ gtph->md.flags = gtpu_flags;
>+ gtph->md.msgtype = gtpu_msgtype;
>+ put_16aligned_be32(>ph->teid, htonl(teid));
>+ tnl_type = OVS_VPORT_TYPE_GTPU;
>+ header_len = sizeof *eth + ip_len +
>+ sizeof *udp + sizeof *gtph;
> } else {
> return -EINVAL;
> }
>@@ -2630,6 +2654,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] = {
>@@ -3035,6 +3060,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
>@@ -3149,6 +3181,15 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl
>*tun_key,
> &opts, sizeof(opts));
> }
>
>+ if ((!tnl_type || !strcmp(tnl_type, "gtpu")) &&
>+ (tun_key->gtpu_flags && tun_key->gtpu_msgtype)) {
>+ struct gtpu_metadata opts;
>+
>+ opts.flags = tun_key->gtpu_flags;
>+ opts.msgtype = tun_key->gtpu_msgtype;
>+ nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,
>+ &opts, sizeof(opts));
>+ }
> nl_msg_end_nested(a, tun_key_ofs);
> }
>
>@@ -3645,6 +3686,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_u8u(ds, "msgtype", opts->msgtype, mask ? &mask->msgtype : NULL,
>+ verbose);
>+ ds_chomp(ds, ',');
>+}
>+
> #define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
>
> static void
>@@ -3897,6 +3954,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);
>@@ -5105,6 +5167,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)
>@@ -5344,6 +5450,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; \
>@@ -5730,6 +5845,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();
>
>@@ -5997,7 +6114,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};
>@@ -7096,7 +7213,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) {
>@@ -8445,7 +8562,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 4ecce1aac5d6..623a66aa28f4 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..7c33d4652baf 100644
>--- a/lib/packets.h
>+++ b/lib/packets.h
>@@ -1447,6 +1447,74 @@ 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
>+#define GTPU_S_MASK 0x02
>+
>+/* 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;
>+};
>+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);
>+
>+struct gtpuhdr_opt {
>+ ovs_be16 seqno;
>+ uint8_t pdu_number;
>+ uint8_t next_ext_type;
>+};
>+BUILD_ASSERT_DECL(sizeof(struct gtpuhdr_opt) == 4);
>+
> /* VXLAN protocol header */
> struct vxlanhdr {
> union {
>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 4407f9c97a9e..a0bc193b7dda 100644
>--- a/ofproto/ofproto-dpif-xlate.c
>+++ b/ofproto/ofproto-dpif-xlate.c
>@@ -3548,6 +3548,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:
>@@ -4099,7 +4100,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)) {
>diff --git a/tests/ofproto.at b/tests/ofproto.at
>index 23a5e150510a..76a3be44dd66 100644
>--- a/tests/ofproto.at
>+++ b/tests/ofproto.at
>@@ -2352,7 +2352,7 @@ head_table () {
> 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,src,dst,ipv6_{src,dst},flags,gbp_{id,flags},erspan_{idx,ver,dir,hwid},metad
>ata0...metadata63} metadata in_{port,port_oxm} pkt_mark ct_{mark,label}
>reg0...reg15 xreg0...xreg7 xxreg0...xxreg3 eth_{src,dst} vlan_{tci,vid,pcp}
>mpls_{label,tc,ttl} ip_{src,dst} ipv6_{src,dst,label} nw_tos ip_dscp nw_{ecn,ttl}
>arp_{op,spa,tpa,sha,tha} tcp_{src,dst} udp_{src,dst} sctp_{src,dst}
>icmp_{type,code} icmpv6_{type,code} nd_{target,sll,tll,reserved,options_type}
>nsh_{flags,spi,si,c1...c4,ttl}
> matching:
>- arbitrary mask: dp_hash
>tun_{id,src,dst,ipv6_{src,dst},flags,gbp_{id,flags},erspan_{idx,ver,dir,hwid},metad
>ata0...metadata63} metadata pkt_mark
>ct_{state,mark,label,nw_{src,dst},ipv6_{src,dst},tp_{src,dst}} reg0...reg15
>xreg0...xreg7 xxreg0...xxreg3 eth_{src,dst} vlan_{tci,vid} ip_{src,dst}
>ipv6_{src,dst,label} ip_frag arp_{spa,tpa,sha,tha} tcp_{src,dst,flags} udp_{src,dst}
>sctp_{src,dst} nd_{target,sll,tll} nsh_{flags,c1...c4}
>+ arbitrary mask: dp_hash
>tun_{id,src,dst,ipv6_{src,dst},flags,gbp_{id,flags},erspan_{idx,ver,dir,hwid},gtpu_{
>flags,msgtype},metadata0...metadata63} metadata pkt_mark
>ct_{state,mark,label,nw_{src,dst},ipv6_{src,dst},tp_{src,dst}} reg0...reg15
>xreg0...xreg7 xxreg0...xxreg3 eth_{src,dst} vlan_{tci,vid} ip_{src,dst}
>ipv6_{src,dst,label} ip_frag arp_{spa,tpa,sha,tha} tcp_{src,dst,flags} udp_{src,dst}
>sctp_{src,dst} nd_{target,sll,tll} nsh_{flags,c1...c4}
> exact match or wildcard: recirc_id packet_type conj_id in_{port,port_oxm}
>actset_output ct_{zone,nw_proto} eth_type vlan_pcp mpls_{label,tc,bos,ttl}
>nw_{proto,tos} ip_dscp nw_{ecn,ttl} arp_op icmp_{type,code}
>icmpv6_{type,code} nd_{reserved,options_type} nsh_{mdtype,np,spi,si,ttl}
>
> ' "$1"
>diff --git a/tests/tunnel-push-pop.at b/tests/tunnel-push-pop.at
>index b92c23fde8f7..48c5de9d1907 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 at 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=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=255,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'])
>@@ -515,6 +535,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
> ])
>@@ -524,6 +545,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 ce000a25e6b6..d65bf4412aa9 100644
>--- a/tests/tunnel.at
>+++ b/tests/tunnel.at
>@@ -1041,3 +1041,79 @@ 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 push and pop])
>+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_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
>+ 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=255,actions=1
>+])
>+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
>+
>+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
>+Listening ports:
>+gtpu_sys_2152 (2152) ref_cnt=2
>+])
>+
>+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=255),flags(df|key)),in_port(2152),packet_type(ns=1,id=0),skb_mark(0),ipv4
>(frag=no)'], [0], [stdout])
>+AT_CHECK([tail -2 stdout], [0],
>+ [Megaflow:
>recirc_id=0,packet_type=(1,0),tun_id=0x3,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_to
>s=0,gtpu_flags=0x30,gtpu_msgtype=255,tun_flags=+df-
>csum+key,in_port=3,dl_type=0x0000
>+Datapath actions: push_eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00),1
>+])
>+
>+OVS_VSWITCHD_STOP
>+AT_CLEANUP
>diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
>index c43cb1aa4e0d..49853b45377b 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&data=02%7C01%7Croniba%40mellanox.co
>m%7Cf870ce16727e40fbf3cf08d79abcc4ef%7Ca652971c7d2e4d9ba6a4d149256f461
>b%7C0%7C0%7C637148007890600479&sdata=9qGk%2FbRat6TH4Nw0S%2Bap
>tZkEoxdLEAc2Khwvxim5SrU%3D&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
More information about the dev
mailing list