From patchwork Tue Jan 21 17:28:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Tu X-Patchwork-Id: 1226672 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=silver.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=s7H50hea; dkim-atps=neutral Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 482FsV0mPxz9sNx for ; Wed, 22 Jan 2020 04:29:49 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id D93DC20517; Tue, 21 Jan 2020 17:29:46 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id SleqwAPp9huy; Tue, 21 Jan 2020 17:29:29 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by silver.osuosl.org (Postfix) with ESMTP id 0C32F20516; Tue, 21 Jan 2020 17:29:29 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id E2BA8C1D87; Tue, 21 Jan 2020 17:29:28 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id D56CAC0174 for ; Tue, 21 Jan 2020 17:29:27 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id C730684488 for ; Tue, 21 Jan 2020 17:29:27 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cbBoeXk9FO5M for ; Tue, 21 Jan 2020 17:29:24 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mail-pf1-f175.google.com (mail-pf1-f175.google.com [209.85.210.175]) by fraxinus.osuosl.org (Postfix) with ESMTPS id 018EB8440A for ; Tue, 21 Jan 2020 17:29:24 +0000 (UTC) Received: by mail-pf1-f175.google.com with SMTP id x185so1831882pfc.5 for ; Tue, 21 Jan 2020 09:29:23 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id; bh=co7lCkvpEcYY7Zj8P81DJ2xQpwopD4fqffQoMXyDpKo=; b=s7H50heavG5f93hAfVoU7SFh+1XZw5WFSKjCKcXEqHw6I7/5DNw1yzZVW6rFiRdLp0 7tuomxYGmB4SNDsC+2fMJ20/vI9xJs3FX2DEAK78RCctyUPmfwbp/XHOapAXyDELCk3D e3s4vPuty62EQhGkz+tZr1u1s2N4MOYpd8dHCn0Mpdd6z8FmVceK5Lp9TIdQe7VrIIUU OMcWA3fd85FSElI9r5ipuo9oOUgIojdIKkGEGbdJt4NtWFp/WIDgIRVQhurn4MQkX+ge GYngk2Cz5tbXAh7Vn17rfl0QjYEMQW3QnTcPnFHWkE1kwroib2F5ltX9HMfIv0h5z33j Z8Mg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=co7lCkvpEcYY7Zj8P81DJ2xQpwopD4fqffQoMXyDpKo=; b=BOkyK7HFsw2ALIqeZVAbDDDV4ByiAzAdxHxS8p6DIZxtCnnazRILmGuYWJwTswxlon YCh1AsF/nQpD5JzarwAhX2bwdIAVppcvNLax4jkYc4RaV1dugbFsMX2k/jTTyaldd/wL EBZ85OLUOkIu6K1C2XIu+7/cjAFGhZewTuqptcx5Xz1GP5Je3DObR6Lv9tPxiyP450uD XYrqsoRgDgl7j8LXIg5U4ESUKyfPNzXUv9e+/QIBI4KAFGt+UDKRMNL3SeBKiBpxQzwI q6XqCCm8dBRObT9b3idajgg8KfGX2VdnCmDJGRqOy8oC3elRBBkh+3UCRF1ql20ii5ma 5mhQ== X-Gm-Message-State: APjAAAVqiOq5AGhy92ZGxFXDsSrce13+N93hzuXpEu//DRmHXdEN3Jw9 FJHlQsTqq5vhS519V0BzjVMEwH68 X-Google-Smtp-Source: APXvYqyy/rvM3p6mInfgoHbpoVqpA7Tyf3gxmTpmJnAvjLjW8OG/ECUA8LhyotJjM1d5+Wk35vIXKQ== X-Received: by 2002:a62:f247:: with SMTP id y7mr5459743pfl.5.1579627762123; Tue, 21 Jan 2020 09:29:22 -0800 (PST) Received: from sc9-mailhost2.vmware.com ([66.170.99.1]) by smtp.gmail.com with ESMTPSA id g21sm45164427pfo.126.2020.01.21.09.29.20 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Tue, 21 Jan 2020 09:29:21 -0800 (PST) From: William Tu To: dev@openvswitch.org Date: Tue, 21 Jan 2020 09:28:43 -0800 Message-Id: <1579627723-2279-1-git-send-email-u9012063@gmail.com> X-Mailer: git-send-email 2.7.4 Cc: Feng Yang Subject: [ovs-dev] [PATCHv8] userspace: Add GTP-U support. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" 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: Feng Yang Co-authored-by: Feng Yang Signed-off-by: Yi Yang Co-authored-by: Yi Yang Signed-off-by: William Tu Acked-by: Ben Pfaff --- v7 -> v8: - Add Feng Yang as co-authored - fix checkpatch error - https://travis-ci.org/williamtu/ovs-travis/builds/640009759 v6 -> v7: - address Ben's feedback - function name: use netdev_tnl_calc_udp_csum - remove unnecessary be32_to_be16(htonl - use SCNi8 instead of SCNx8 - Travis: https://travis-ci.org/williamtu/ovs-travis/builds/638683932 v5 -> v6: - rebase to master - travis: https://travis-ci.org/williamtu/ovs-travis/builds/638083655 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://travis-ci.org/williamtu/ovs-travis/builds/621289678 v2 -> v3: - pick up the code from v2, rebase to master - many fixes in code, docs, and add more tests - travis: https://travis-ci.org/williamtu/ovs-travis/builds/619799361 v1 -> v2: - Add new packet_type PT_GTPU_MSG to handle GTP-U signaling message, GTP-U signaling message should be steered into a control plane handler by explicit openflow entry in flow table. - Fix gtpu_flags and gptu_msgtype set issue. - Verify communication with kernel GTP implementation from Jiannan Ouyang. - Fix unit test issue and make sure all the unit tests can pass. - This patch series add GTP-U tunnel support in DPDK userspace, GTP-U is used for carrying user data within the GPRS core network and between the radio access network and the core network. The user data transported can be packets in any of IPv4, IPv6, or PPP formats. --- Documentation/faq/configuration.rst | 13 ++ Documentation/faq/releases.rst | 1 + NEWS | 3 + datapath/linux/compat/include/linux/openvswitch.h | 2 + include/openvswitch/flow.h | 4 +- include/openvswitch/match.h | 6 + include/openvswitch/meta-flow.h | 28 ++++ include/openvswitch/packets.h | 4 +- lib/dpif-netlink-rtnl.c | 5 + lib/dpif-netlink.c | 5 + lib/flow.c | 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= \ + 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". * 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'
  • LISP has a 24-bit instance ID.
  • GRE has an optional 32-bit key.
  • STT has a 64-bit key.
  • -
  • ERSPAN has a 10-bit key (Session ID).
  • +
  • ERSPAN has a 10-bit key (Session ID).
  • +
  • GTPU has a 32-bit key (Tunnel Endpoint ID).
  • @@ -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. +

    GTP-U Metadata Fields

    + +

    + 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: +

    + + +
    + + + + +
    + +
    + +

    + 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 . +

    + + +

    + This field holds the 8-bit GTP-U flags, encoded as: +

    + + +
    + + + + + + +
    +
    + +

    + The flags are: +

    +
    +
    version
    +
    Used to determine the version of the GTP-U protocol, which should + be set to 1.
    + +
    PT
    +
    Protocol type, used as a protocol discriminator + between GTP (1) and GTP' (0).
    + +
    rsv
    +
    Reserved. Must be zero.
    + +
    E
    +
    If 1, indicates the presence of a meaningful value of the Next + Extension Header field.
    + +
    S
    +
    If 1, indicates the presence of a meaningful value of the Sequence + Number field.
    + +
    PN
    +
    If 1, indicates the presence of a meaningful value of the N-PDU + Number field.
    +
    +
    + + + 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]. + +

    Geneve Fields

    diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c index a78972888e33..0acc8795360c 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_calc_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_calc_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_calc_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 = htons(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..b66d266cca1d 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=%"SCNi8",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 @@

    A pair of virtual devices that act as a patch cable.
    + +
    gtpu
    +
    +

    + 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. +

    + +

    + The protocol is documented at + http://www.3gpp.org/DynaReport/29281.htm +

    + +

    + 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. +

    +
    +