Message ID | 1579203920-108775-1-git-send-email-u9012063@gmail.com |
---|---|
State | Superseded |
Headers | show |
Series | [ovs-dev,PATCHv6] userspace: Add GTP-U support. | expand |
On Thu, Jan 16, 2020 at 11:45:20AM -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 for the patch! I have only a few comments. To me, the "update" in the name netdev_tnl_update_udp_csum() suggests that it's starting from an existing checksum and then adding a few more things into it. I think that it calculates the whole checksum from the beginning. I would say "calculate" or "calc" or "set" instead of "update". In netdev_gtpu_push_header(), I think that be32_to_be16(htonl(...)) is the same as htons(). In ovs_parse_tnl_push(), I suggest using "%"SCNi8, etc., instead of "0x%"SCNx8. That allows the user to specify the values in decimal if desired. (That is particularly desirable if the value is 0, since it's not really a normal thing to write 0x0 if one does not need to.) Acked-by: Ben Pfaff <blp@ovn.org>
On Fri, Jan 17, 2020 at 02:14:27PM -0800, Ben Pfaff wrote: > On Thu, Jan 16, 2020 at 11:45:20AM -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 for the patch! > > I have only a few comments. > > To me, the "update" in the name netdev_tnl_update_udp_csum() suggests > that it's starting from an existing checksum and then adding a few more > things into it. I think that it calculates the whole checksum from the > beginning. I would say "calculate" or "calc" or "set" instead of > "update". > > In netdev_gtpu_push_header(), I think that be32_to_be16(htonl(...)) is > the same as htons(). > > In ovs_parse_tnl_push(), I suggest using "%"SCNi8, etc., instead of > "0x%"SCNx8. That allows the user to specify the values in decimal if > desired. (That is particularly desirable if the value is 0, since it's > not really a normal thing to write 0x0 if one does not need to.) > > Acked-by: Ben Pfaff <blp@ovn.org> Thanks Ben, I will fix the above and send v7. Probably wait for a couple days to see if there is any more feedbacks. Regards, William
On Fri, Jan 17, 2020 at 04:24:42PM -0800, William Tu wrote: > On Fri, Jan 17, 2020 at 02:14:27PM -0800, Ben Pfaff wrote: > > On Thu, Jan 16, 2020 at 11:45:20AM -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 for the patch! > > > > I have only a few comments. > > > > To me, the "update" in the name netdev_tnl_update_udp_csum() suggests > > that it's starting from an existing checksum and then adding a few more > > things into it. I think that it calculates the whole checksum from the > > beginning. I would say "calculate" or "calc" or "set" instead of > > "update". > > > > In netdev_gtpu_push_header(), I think that be32_to_be16(htonl(...)) is > > the same as htons(). > > > > In ovs_parse_tnl_push(), I suggest using "%"SCNi8, etc., instead of > > "0x%"SCNx8. That allows the user to specify the values in decimal if > > desired. (That is particularly desirable if the value is 0, since it's > > not really a normal thing to write 0x0 if one does not need to.) > > > > Acked-by: Ben Pfaff <blp@ovn.org> > > Thanks Ben, > I will fix the above and send v7. > Probably wait for a couple days to see if there is any more feedbacks. That makes sense to me, especially since neither of us really understands GTP-U properly, even though we can read the specs.
Hi William, Two comments, please inline. >-----Original Message----- >From: William Tu <u9012063@gmail.com> >Sent: Thursday, January 16, 2020 9:45 PM >To: dev@openvswitch.org >Cc: Roni Bar Yanai <roniba@mellanox.com>; aconole@redhat.com; >yangyi01@inspur.com; fhallal@redhat.com; ashishvarma.ovs@gmail.com; >blp@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@inspur.com> >Co-authored-by: Yi Yang <yangyi01@inspur.com> >Signed-off-by: William Tu <u9012063@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@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
Hi Roni, Thanks for your feedbacks. See my reply below. On Tue, Jan 21, 2020 at 11:20 AM Roni Bar Yanai <roniba@mellanox.com> wrote: > > Hi William, > > Two comments, please inline. > <snip> > > 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? Similar to L3 GRE and VxLAN tunnel, I think it's already handled by OVS when we set the tunnel to be TNL_L3. If you look at the tests/tunnel.at in this patch, OVS will push a dummy ethernet header AT_SETUP([tunnel - GTP-U push and pop]) +dnl receive packet from GTP-U port, match it, and output to layer3 GRE ... +Datapath actions: push_eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00),1 > >+ } 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. > Yes, I think it's also done by OVS already. See tests/tunnel.at AT_SETUP([tunnel - GTP-U push and pop]) +dnl Encap: in_port=1,actions=3 ... + [Datapath actions: set(tunnel(tun_id=0x3,dst=1.1.1.1,ttl=64,tp_dst=2152,flags(df|key))),pop_eth,2152 Regards, William
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); + } 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); + 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},metadata0...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},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} + 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@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=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=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 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637']) @@ -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),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=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_tos=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 + 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>