Message ID | 1526365929-3047-2-git-send-email-yi.y.yang@intel.com |
---|---|
State | Changes Requested |
Headers | show |
Series | Add GTP-U tunnel support in DPDK userspace | expand |
Yi Yang <yi.y.yang@intel.com> writes: Hi Yi, Thanks for the patch! Just a brief review. > From: Feng Yang <feng.yang@intel.com> > > 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 can be decomposed into separate protocols, GTP-C, GTP-U and GTP'. > > 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. > > GTP-U is user plane communication protocol between Serving-Gateway > (S-GW) and PDN-Gateway (P-GW) as well as Multi-access Edge Computing > (MEC). > > This patch added a new tunnel type 'gtpu' into userspace datapath and > two new match fields tun_gtpu_flags and tun_gtpu_msgtype, tun_id is > reused for GTP Tunnel Endpoint ID (TEID). > > The below are commands for gtpu tunnel add: > > $ sudo ovs-vsctl add-br br-int -- set bridge br-int datapath_type=netdev protocols=OpenFlow13 > $ sudo ovs-vsctl add-port br-int gtpu1 -- set interface gtpu1 type=gtpu options:packet_type=legacy_l3 options:remote_ip=flow options:key=flow options:dst_port=2152 > > test-flows-gtpu.txt is openflow table for gtpu tunnel test: > > $ cat test-flows-gtpu.txt > table=0,icmp,in_port=4 actions=load:0xc0a83249->NXM_NX_TUN_IPV4_DST[],load:0x9->NXM_NX_TUN_ID[0..31],output:2 > table=0,tcp,tp_dst=80,in_port=4 actions=load:0xc0a83249->NXM_NX_TUN_IPV4_DST[],load:0x9->NXM_NX_TUN_ID[0..31],output:2 > table=0,in_port=2,tun_id=0x10,tun_gtpu_flags=0x30,tun_gtpu_msgtype=255 actions=set_field:00:00:22:22:22:22->eth_src,set_field:00:00:11:11:11:11->eth_dst,output:4 > table=0,actions=NORMAL > > Signed-off-by: Feng Yang <feng.yang@intel.com> > Signed-off-by: Jiannan Ouyang <ouyangj@fb.com> > Signed-off-by: Yi Yang <yi.y.yang@intel.com> Needs Co-author tags. > --- > build-aux/extract-ofp-fields | 1 + > datapath/linux/compat/include/linux/openvswitch.h | 3 + > include/openvswitch/match.h | 4 + > 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 | 7 ++ > lib/match.c | 29 +++++ > lib/meta-flow.c | 40 ++++++ > lib/meta-flow.xml | 76 ++++++++++++ > lib/netdev-native-tnl.c | 145 +++++++++++++++++++++- > lib/netdev-native-tnl.h | 12 ++ > lib/netdev-vport.c | 18 ++- > lib/nx-match.c | 4 + > lib/odp-util.c | 48 +++++++ > lib/packets.h | 19 +++ > lib/tnl-ports.c | 3 + > ofproto/ofproto-dpif-ipfix.c | 5 + > ofproto/ofproto-dpif-sflow.c | 6 +- > ofproto/ofproto-dpif-xlate.c | 4 +- > ofproto/tunnel.c | 9 +- > vswitchd/vswitch.xml | 16 +++ > 23 files changed, 485 insertions(+), 6 deletions(-) > > diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields > index 184b75e..944f095 100755 > --- a/build-aux/extract-ofp-fields > +++ b/build-aux/extract-ofp-fields > @@ -68,6 +68,7 @@ PREREQS = {"none": "MFP_NONE", > OXM_CLASSES = {"NXM_OF_": (0, 0x0000), > "NXM_NX_": (0, 0x0001), > "NXOXM_NSH_": (0x005ad650, 0xffff), > + "NXOXM_GTPU_": (0x005ad651, 0xffff), > "OXM_OF_": (0, 0x8000), > "OXM_OF_PKT_REG": (0, 0x8001), > "ONFOXM_ET_": (0x4f4e4600, 0xffff), > diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h > index 84ebcaf..ad6ea64 100644 > --- a/datapath/linux/compat/include/linux/openvswitch.h > +++ b/datapath/linux/compat/include/linux/openvswitch.h I don't see this in the upstream code. This is introducing another place for kernel and ovs to diverge. Please get this type accepted upstream first. > @@ -236,6 +236,7 @@ enum ovs_vport_type { > OVS_VPORT_TYPE_GRE, /* GRE tunnel. */ > OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel. */ > OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */ > + OVS_VPORT_TYPE_GTPU, /* GTPU tunnel. */ > OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */ > OVS_VPORT_TYPE_STT = 106, /* STT tunnel */ > __OVS_VPORT_TYPE_MAX > @@ -394,6 +395,8 @@ enum ovs_tunnel_key_attr { > OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS, /* Nested OVS_VXLAN_EXT_* */ > OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */ > OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */ > + OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS, /* u8 GTP-U FLAGS. */ > + OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE, /* u8 GTP-U MSGTYPE. */ > OVS_TUNNEL_KEY_ATTR_PAD, > __OVS_TUNNEL_KEY_ATTR_MAX > }; > diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h > index 49333ae..9bcc4f4 100644 > --- a/include/openvswitch/match.h > +++ b/include/openvswitch/match.h > @@ -109,6 +109,10 @@ void match_set_tun_gbp_id_masked(struct match *match, ovs_be16 gbp_id, ovs_be16 > void match_set_tun_gbp_id(struct match *match, ovs_be16 gbp_id); > void match_set_tun_gbp_flags_masked(struct match *match, uint8_t flags, uint8_t mask); > void match_set_tun_gbp_flags(struct match *match, uint8_t flags); > +void match_set_tun_gtpu_flags(struct match *match, uint8_t gtpu_flags); > +void match_set_tun_gtpu_flags_masked(struct match *match, uint8_t gtpu_flags, > + uint8_t mask); > +void match_set_tun_gtpu_msgtype(struct match *match, uint8_t gtpu_msgtype); > 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 98c9e1c..884a2f6 100644 > --- a/include/openvswitch/meta-flow.h > +++ b/include/openvswitch/meta-flow.h > @@ -1848,6 +1848,34 @@ enum OVS_PACKED_ENUM mf_field_id { > */ > MFF_NSH_TTL, > > + /* "tun_gtpu_flags". > + * > + * The flags in a GTP-U tunnel header. > + * > + * Type: u8. > + * Maskable: bitwise. > + * Formatting: decimal. > + * Prerequisites: none. > + * Access: read/write. > + * NXM: none. > + * OXM: NXOXM_GTPU_FLAGS(1) since OF1.3 and v2.10. > + */ > + MFF_TUN_GTPU_FLAGS, > + > + /* "tun_gtpu_msgtype". > + * > + * The message type in a GTP-U tunnel header. > + * > + * Type: u8. > + * Maskable: no. > + * Formatting: decimal. > + * Prerequisites: none. > + * Access: read/write. > + * NXM: none. > + * OXM: NXOXM_GTPU_MSGTYPE(2) since OF1.3 and v2.10. > + */ > + MFF_TUN_GTPU_MSGTYPE, > + > MFF_N_IDS > }; > > diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h > index fef756b..1f3b499 100644 > --- a/include/openvswitch/packets.h > +++ b/include/openvswitch/packets.h > @@ -39,7 +39,9 @@ struct flow_tnl { > ovs_be16 tp_dst; > ovs_be16 gbp_id; > uint8_t gbp_flags; > - uint8_t pad1[5]; /* Pad to 64 bits. */ > + uint8_t gtpu_flags; > + uint8_t gtpu_msgtype; > + uint8_t pad1[3]; /* Pad to 64 bits. */ > struct tun_metadata metadata; > }; > > diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c > index 40c4569..a628b72 100644 > --- a/lib/dpif-netlink-rtnl.c > +++ b/lib/dpif-netlink-rtnl.c Again, you'll need to get the netlink datapath changes through kernel process. At least try. > @@ -99,6 +99,8 @@ vport_type_to_kind(enum ovs_vport_type type, > } > case OVS_VPORT_TYPE_GENEVE: > return "geneve"; > + case OVS_VPORT_TYPE_GTPU: > + return "gtpu"; > case OVS_VPORT_TYPE_NETDEV: > case OVS_VPORT_TYPE_INTERNAL: > case OVS_VPORT_TYPE_LISP: > @@ -262,6 +264,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: > @@ -327,6 +330,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: > @@ -438,6 +442,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 bb9e95d..8b5bd97 100644 > --- a/lib/dpif-netlink.c > +++ b/lib/dpif-netlink.c > @@ -787,6 +787,9 @@ get_vport_type(const struct dpif_netlink_vport *vport) > case OVS_VPORT_TYPE_STT: > return "stt"; > > + case OVS_VPORT_TYPE_GTPU: > + return "gtpu"; > + > case OVS_VPORT_TYPE_UNSPEC: > case __OVS_VPORT_TYPE_MAX: > break; > @@ -814,6 +817,8 @@ netdev_to_ovs_vport_type(const char *type) > return OVS_VPORT_TYPE_VXLAN; > } else if (!strcmp(type, "lisp")) { > return OVS_VPORT_TYPE_LISP; > + } else if (strstr(type, "gtpu")) { Why isn't this using "!strcmp"? > + return OVS_VPORT_TYPE_GTPU; > } else { > return OVS_VPORT_TYPE_UNSPEC; > } > diff --git a/lib/flow.c b/lib/flow.c > index 09b66b8..d5af1e8 100644 > --- a/lib/flow.c > +++ b/lib/flow.c > @@ -695,6 +695,13 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) > miniflow_push_be32(mf, packet_type, packet_type); > } > > + /* It won't be parsed if the packet contains application layer data only. > + * These include, but not limited to GTP-U messages, GTP-C packets. > + */ Why treat GTP-U and C separate here? > + if (packet_type == htonl(PT_GTPU_MSG)) { > + goto out; > + } > + > /* Initialize packet's layer pointer and offsets. */ > frame = data; > dp_packet_reset_offsets(packet); > diff --git a/lib/match.c b/lib/match.c > index 2e9a803..e5cc9c6 100644 > --- a/lib/match.c > +++ b/lib/match.c > @@ -320,6 +320,27 @@ match_set_tun_gbp_flags(struct match *match, uint8_t flags) > } > > void > +match_set_tun_gtpu_flags(struct match *match, uint8_t gtpu_flags) > +{ > + match_set_tun_gtpu_flags_masked(match, gtpu_flags, UINT8_MAX); > +} > + > +void > +match_set_tun_gtpu_flags_masked(struct match *match, uint8_t gtpu_flags, > + uint8_t mask) > +{ > + match->wc.masks.tunnel.gtpu_flags = mask; > + match->flow.tunnel.gtpu_flags = gtpu_flags & mask; > +} > + > +void > +match_set_tun_gtpu_msgtype(struct match *match, uint8_t gtpu_msgtype) > +{ > + match->flow.tunnel.gtpu_msgtype = gtpu_msgtype; > + match->wc.masks.tunnel.gtpu_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); > @@ -1239,6 +1260,14 @@ format_flow_tunnel(struct ds *s, const struct match *match) > FLOW_TNL_F_MASK); > ds_put_char(s, ','); > } > + > + if (wc->masks.tunnel.gtpu_flags) { > + ds_put_format(s, "tun_gtpu_flags=%"PRIx8",", tnl->gtpu_flags); > + } > + if (wc->masks.tunnel.gtpu_msgtype) { > + ds_put_format(s, "tun_gtpu_msgtype=%"PRIu8",", tnl->gtpu_msgtype); > + } > + > tun_metadata_match_format(s, match); > } > > diff --git a/lib/meta-flow.c b/lib/meta-flow.c > index aa2ec01..bde59df 100644 > --- a/lib/meta-flow.c > +++ b/lib/meta-flow.c > @@ -231,6 +231,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) > return !wc->masks.tunnel.gbp_id; > case MFF_TUN_GBP_FLAGS: > return !wc->masks.tunnel.gbp_flags; > + case MFF_TUN_GTPU_FLAGS: > + return !wc->masks.tunnel.gtpu_flags; > + case MFF_TUN_GTPU_MSGTYPE: > + return !wc->masks.tunnel.gtpu_msgtype; > CASE_MFF_TUN_METADATA: > return !ULLONG_GET(wc->masks.tunnel.metadata.present.map, > mf->id - MFF_TUN_METADATA0); > @@ -513,6 +517,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) > case MFF_TUN_TTL: > case MFF_TUN_GBP_ID: > case MFF_TUN_GBP_FLAGS: > + case MFF_TUN_GTPU_MSGTYPE: > CASE_MFF_TUN_METADATA: > case MFF_METADATA: > case MFF_IN_PORT: > @@ -625,6 +630,9 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) > case MFF_NSH_C4: > return true; > > + case MFF_TUN_GTPU_FLAGS: > + return (value->u8 == GTPU_FLAGS_DEFAULT); > + > case MFF_N_IDS: > default: > OVS_NOT_REACHED(); > @@ -674,6 +682,12 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, > case MFF_TUN_GBP_FLAGS: > value->u8 = flow->tunnel.gbp_flags; > 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_TTL: > value->u8 = flow->tunnel.ip_ttl; > break; > @@ -988,6 +1002,12 @@ mf_set_value(const struct mf_field *mf, > case MFF_TUN_GBP_FLAGS: > match_set_tun_gbp_flags(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_TOS: > match_set_tun_tos(match, value->u8); > break; > @@ -1385,6 +1405,12 @@ mf_set_flow_value(const struct mf_field *mf, > case MFF_TUN_GBP_FLAGS: > flow->tunnel.gbp_flags = 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_TOS: > flow->tunnel.ip_tos = value->u8; > break; > @@ -1700,6 +1726,8 @@ mf_is_pipeline_field(const struct mf_field *mf) > case MFF_TUN_FLAGS: > case MFF_TUN_GBP_ID: > case MFF_TUN_GBP_FLAGS: > + case MFF_TUN_GTPU_FLAGS: > + case MFF_TUN_GTPU_MSGTYPE: > CASE_MFF_TUN_METADATA: > case MFF_METADATA: > case MFF_IN_PORT: > @@ -1870,6 +1898,14 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str) > case MFF_TUN_GBP_FLAGS: > match_set_tun_gbp_flags_masked(match, 0, 0); > break; > + case MFF_TUN_GTPU_FLAGS: > + match->wc.masks.tunnel.gtpu_flags = 0; > + match->flow.tunnel.gtpu_flags = 0; > + break; > + case MFF_TUN_GTPU_MSGTYPE: > + match->wc.masks.tunnel.gtpu_msgtype = 0; > + match->flow.tunnel.gtpu_msgtype = 0; > + break; > case MFF_TUN_TOS: > match_set_tun_tos_masked(match, 0, 0); > break; > @@ -2221,6 +2257,7 @@ mf_set(const struct mf_field *mf, > case MFF_ICMPV4_CODE: > case MFF_ICMPV6_TYPE: > case MFF_ICMPV6_CODE: > + case MFF_TUN_GTPU_MSGTYPE: > return OFPUTIL_P_NONE; > > case MFF_DP_HASH: > @@ -2250,6 +2287,9 @@ mf_set(const struct mf_field *mf, > case MFF_TUN_GBP_FLAGS: > match_set_tun_gbp_flags_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_TTL: > match_set_tun_ttl_masked(match, value->u8, mask->u8); > break; > diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml > index 933d4b8..d169772 100644 > --- a/lib/meta-flow.xml > +++ b/lib/meta-flow.xml > @@ -731,6 +731,12 @@ tcp,tp_src=0x07c0/0xfff0 > Used by Open vSwitch for NSH extensions, in the absence of an official > ONF-assigned class. (This OUI is randomly generated.) > </dd> > + > + <dt>0x005ad651 (<code>NXOXM_GTPU</code>)</dt> > + <dd> > + Used by Open vSwitch for GTP-U extensions, in the absence of an official > + ONF-assigned class. (This OUI is randomly generated.) > + </dd> > </dl> > > <p> > @@ -1899,6 +1905,68 @@ ovs-ofctl add-flow br0 tun_metadata0=1234,actions=controller > </p> > </field> > > + <h2>GTP-U Fields</h2> > + > + <p> > + The GTP-U (v1) header is defined as follows [3GPP TS 29.281]. > + </p> > + > + <diagram> > + <header name="GTP-U tunnel flags"> > + <bits name="V" above="3" width="0.15"/> > + <bits name="PT" above="1" width="0.15"/> > + <bits name="(*)" above="1" width="0.15"/> > + <bits name="E" above="1" width="0.15"/> > + <bits name="S" above="1" width="0.15"/> > + <bits name="PN" above="1" width="0.15"/> > + </header> > + <nospace/> > + <header> > + <bits name="Message Type" above="8" width="0.15"/> > + <bits name="Length" above="16" width="0.15"/> > + <bits name="TEID" above="32" width="0.15"/> > + <bits name="..." above="" width="0.15"/> > + </header> > + </diagram> > + > + <p> > + where > + </p> > + <ul> > + <li><code>V</code>: version fields, used to determine the version of the > + GTP-U protocol, which should be set to '1'.</li> > + <li><code>PT</code>: Packet Type, used as a protocol discriminator > + between GTP (when PT is '1') and GTP' (when PT is '0').</li> > + <li><code>E</code>: Extension Header flag, indicating the presence of a > + meaningful value of the Next Extension Header field.</li> > + <li><code>S</code>: Sequence number flag, indicating the presence of a > + meaningful value of the Sequence Number field.</li> > + <li><code>PN</code>: N-PDU Number flag, indicating the presence of a > + meaningful value of the N-PDU Number field.</li> > + <li><code>Message Type</code>: indicating the type of GTP-U message. > + </li> > + <li><code>Length</code>: indicating the length in octets of the payload. > + </li> > + <li><code>TEID</code>: Tunnel Endpoint Identifier,unambiguously > + identifying a tunnel endpoint in the receiving GTP-U protocol entity. > + Open vSwitch makes TEID availabe via <ref field="tun_id"/>.</li> > + </ul> > + <field id="MFF_TUN_GTPU_FLAGS" title="GTP-U tunnel flags"> > + <p> > + For a packet tunneled over GTP-U, this field indicates the presence of > + optional headers. > + </p> > + </field> > + > + <field id="MFF_TUN_GTPU_MSGTYPE" title="GTP-U tunnel message type"> > + <p> > + For a packet tunneled over GTP-U, 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]. > + </p> > + </field> > + > <!-- Open vSwitch uses the following fields internally, but it > does not expose them to the user via OpenFlow, so we do not > document them. --> > @@ -4662,6 +4730,14 @@ r r c c c. > <url href="https://tools.ietf.org/html/rfc7665"/>. > </dd> > > + <dt>3GPP TS 29.281</dt> > + <dd> > + 3GPP, > + ``General Packet Radio System (GPRS) Tunnelling Protocol User Plane > + (GTPv1-U),'' > + <url href="http://www.3gpp.org/ftp/Specs/html-info/29281.htm"/>. > + </dd> > + > <dt>Srinivasan</dt> > <dd> > V. Srinivasan, S. Suriy, and G. Varghese, ``Packet > diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c > index c3e698d..cace175 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; > > @@ -715,7 +718,147 @@ netdev_geneve_build_header(const struct netdev *netdev, > return 0; > } > > - > +void > +netdev_gtpu_push_header(struct dp_packet *packet, > + const struct ovs_action_push_tnl *data) > +{ > + struct udp_header *udp; > + struct gtpuhdr *gtph; > + int gtpu_len; > + int ip_tot_size; > + > + gtpu_len = dp_packet_size(packet); > + > + udp = netdev_tnl_push_ip_header(packet, data->header, data->header_len, > + &ip_tot_size); > + udp->udp_len = htons(ip_tot_size); > + > + gtph = (struct gtpuhdr *)(udp + 1); > + gtph->len = htons(gtpu_len); > + > + 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); > + } > + } > + > + packet->packet_type = htonl(PT_ETH); > +} > + > + > +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; > + struct udp_header *udp; > + > + /* XXX: RCUfy tnl_cfg. */ > + ovs_mutex_lock(&dev->mutex); > + > + tnl_cfg = &dev->tnl_cfg; > + udp = netdev_tnl_ip_build_header(data, params, IPPROTO_UDP); > + udp->udp_dst = tnl_cfg->dst_port; > + udp->udp_src = htons(GTPU_DST_PORT); > + > + if (params->is_ipv6 || params->flow->tunnel.flags & FLOW_TNL_F_CSUM) { > + /* Write a value in now to mark that we should compute the checksum > + * later. 0xffff is handy because it is transparent to the > + * calculation. */ > + udp->udp_csum = htons(0xffff); > + } > + > + ovs_mutex_unlock(&dev->mutex); > + data->header_len += sizeof *udp; > + > + gtph = (struct gtpuhdr *)(udp + 1); > + gtph->flags = params->flow->tunnel.gtpu_flags; > + gtph->msgtype = params->flow->tunnel.gtpu_msgtype; > + > + put_16aligned_be32(>ph->teid, > + htonl(ntohll(params->flow->tunnel.tun_id))); > + data->header_len += sizeof *gtph; > + data->tnl_type = OVS_VPORT_TYPE_GTPU; > + return 0; > +} > + > +struct dp_packet * > +netdev_gtpu_pop_header(struct dp_packet *packet) > +{ > + struct pkt_metadata *md = &packet->md; > + struct flow_tnl *tnl = &md->tunnel; > + struct gtpuhdr *gtph; > + unsigned int hlen; > + > + pkt_metadata_init_tnl(md); > + if (GTPU_HLEN > dp_packet_l4_size(packet)) { > + VLOG_WARN_RL(&err_rl, "GTP-U packet too small: min header=%u " > + "packet size=%"PRIuSIZE"\n", > + (unsigned int)GTPU_HLEN, dp_packet_l4_size(packet)); > + goto err; > + } > + > + gtph = udp_extract_tnl_md(packet, tnl, &hlen); > + if (!gtph) { > + goto err; > + } > + > + if (gtph->flags == 0x30) { > + /* Only GTP-U v1 packets without optional fileds are processed, i.e. > + * 8 7 6 5 4 3 2 1 > + * | ver | PT | 0 | E | S | PN | > + * 0 0 1 1 0 0 0 0 > + */ > + tnl->gtpu_flags = gtph->flags; > + } else { > + goto err; > + } > + tnl->gtpu_msgtype = gtph->msgtype; > + tnl->tun_id = htonll(ntohl(get_16aligned_be32(>ph->teid))); > + > + /*Figure out whether the inner packet is IPv4, IPv6 or a GTP-U message.*/ > + if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) { > + struct ip_header *ip; > + > + ip = (struct ip_header *)(gtph + 1); > + if (IP_VER(ip->ip_ihl_ver) == 4) { > + packet->packet_type = htonl(PT_IPV4); > + } else if (IP_VER(ip->ip_ihl_ver) == 6) { > + packet->packet_type = htonl(PT_IPV6); > + } else { > + goto err; > + } > + } else { > + /* GTP-U messages, including echo request, end marker, etc. > + * OVS will be kicking it to a control-plane entity if properly > + * configured. > + */ > + packet->packet_type = htonl(PT_GTPU_MSG); > + } > + > + dp_packet_reset_packet(packet, hlen + GTPU_HLEN); > + > + return packet; > +err: > + dp_packet_delete(packet); > + return NULL; > +} > + > void > netdev_tnl_egress_port_range(struct unixctl_conn *conn, int argc, > const char *argv[], void *aux OVS_UNUSED) > diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h > index a912ce9..9c4cc66 100644 > --- a/lib/netdev-native-tnl.h > +++ b/lib/netdev-native-tnl.h > @@ -58,6 +58,18 @@ netdev_vxlan_build_header(const struct netdev *netdev, > struct dp_packet * > netdev_vxlan_pop_header(struct dp_packet *packet); > > +void > +netdev_gtpu_push_header(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 *params); > + > +struct dp_packet * > +netdev_gtpu_pop_header(struct dp_packet *packet); > + > static inline bool > netdev_tnl_is_header_ipv6(const void *header) > { > diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c > index 52aa12d..ee454a1 100644 > --- a/lib/netdev-vport.c > +++ b/lib/netdev-vport.c > @@ -107,7 +107,10 @@ 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 * > @@ -199,6 +202,8 @@ netdev_vport_construct(struct netdev *netdev_) > dev->tnl_cfg.dst_port = htons(LISP_DST_PORT); > } else if (!strcmp(type, "stt")) { > dev->tnl_cfg.dst_port = htons(STT_DST_PORT); > + } else if (!strcmp(type, "gtpu")) { > + dev->tnl_cfg.dst_port = htons(GTPU_DST_PORT); > } > > dev->tnl_cfg.dont_fragment = true; > @@ -408,6 +413,8 @@ tunnel_supported_layers(const char *type, > return TNL_L3; > } else if (!strcmp(type, "gre")) { > return TNL_L2 | TNL_L3; > + } else if (!strcmp(type, "gtpu")) { > + return TNL_L3; > } else if (!strcmp(type, "vxlan") > && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) { > return TNL_L2 | TNL_L3; > @@ -455,6 +462,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; > > @@ -700,6 +711,7 @@ 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("gtpu", type) && dst_port != GTPU_DST_PORT) || > (!strcmp("stt", type) && dst_port != STT_DST_PORT)) { > smap_add_format(args, "dst_port", "%d", dst_port); > } > @@ -979,6 +991,10 @@ netdev_vport_tunnel_register(void) > NETDEV_VPORT_GET_IFINDEX), > TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL, NULL), > TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL, NULL), > + TUNNEL_CLASS("gtpu", "gptu_sys", netdev_gtpu_build_header, > + netdev_gtpu_push_header, > + netdev_gtpu_pop_header, > + NULL), > }; > static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; > > diff --git a/lib/nx-match.c b/lib/nx-match.c > index a8edb2e..c7f6c27 100644 > --- a/lib/nx-match.c > +++ b/lib/nx-match.c > @@ -1153,6 +1153,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, > flow->tunnel.gbp_id, match->wc.masks.tunnel.gbp_id); > nxm_put_8m(&ctx, MFF_TUN_GBP_FLAGS, oxm, > flow->tunnel.gbp_flags, match->wc.masks.tunnel.gbp_flags); > + 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); > tun_metadata_to_nx_match(b, oxm, match); > > /* Network Service Header */ > diff --git a/lib/odp-util.c b/lib/odp-util.c > index 70188b6..2c5a1f1 100644 > --- a/lib/odp-util.c > +++ b/lib/odp-util.c > @@ -712,6 +712,15 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data) > options++; > } > ds_put_format(ds, ")"); > + } else if (data->tnl_type == OVS_VPORT_TYPE_GTPU) { > + const struct gtpuhdr *gtph; > + > + gtph = format_udp_tnl_push_header(ds, udp); > + > + ds_put_format(ds, "gtpu(flags=0x%"PRIx8 > + ",msgtype=0x%"PRIx8",teid=0x%"PRIx32")", > + gtph->flags, gtph->msgtype, > + ntohl(get_16aligned_be32(>ph->teid))); > } > ds_put_format(ds, ")"); > } > @@ -1474,6 +1483,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data) > if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),", > &udp_src, &udp_dst, &csum)) { > uint32_t vx_flags, vni; > + uint8_t gtpu_flags, gtpu_msgtype; > > udp->udp_src = htons(udp_src); > udp->udp_dst = htons(udp_dst); > @@ -1528,6 +1538,17 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data) > gnh->proto_type = htons(ETH_TYPE_TEB); > put_16aligned_be32(&gnh->vni, htonl(vni << 8)); > tnl_type = OVS_VPORT_TYPE_GENEVE; > + } else if (ovs_scan_len(s, &n, "gtpu(flags=0x%"SCNx8",msgtype=0x%" > + SCNx8",teid=0x%"SCNx32"))", > + >pu_flags, >pu_msgtype, &vni)) { > + struct gtpuhdr *gtph = (struct gtpuhdr *) (udp + 1); > + > + gtph->flags = gtpu_flags; > + gtph->msgtype = gtpu_msgtype; > + put_16aligned_be32(>ph->teid, htonl(vni)); > + tnl_type = OVS_VPORT_TYPE_GTPU; > + header_len = sizeof *eth + ip_len + > + sizeof *udp + sizeof *gtph; > } else { > return -EINVAL; > } > @@ -2373,6 +2394,8 @@ static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX + > [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = ATTR_LEN_NESTED, > .next = ovs_vxlan_ext_attr_lens , > .next_max = OVS_VXLAN_EXT_MAX}, > + [OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS] = { .len = 1 }, > + [OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE] = { .len = 1 }, > [OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = 16 }, > [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = 16 }, > }; > @@ -2698,6 +2721,12 @@ odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask, > case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: > tun_metadata_from_geneve_nlattr(a, is_mask, tun); > break; > + case OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS: > + tun->gtpu_flags = nl_attr_get_u8(a); > + break; > + case OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE: > + tun->gtpu_msgtype = nl_attr_get_u8(a); > + break; > > default: > /* Allow this to show up as unexpected, if there are unknown > @@ -2767,6 +2796,13 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key, > if (tun_key->flags & FLOW_TNL_F_OAM) { > nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_OAM); > } > + if (tun_key->gtpu_flags) { > + nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS, tun_key->gtpu_flags); > + } > + if (tun_key->gtpu_msgtype) { > + nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE, > + tun_key->gtpu_msgtype); > + } > > /* If tnl_type is set to a particular type of output tunnel, > * only put its relevant tunnel metadata to the nlattr. > @@ -3487,6 +3523,14 @@ format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr, > format_odp_tun_geneve(a, ma, ds, verbose); > ds_put_cstr(ds, "),"); > break; > + case OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS: > + format_u8x(ds, "gtpu_flags", nl_attr_get_u8(a), > + ma ? nl_attr_get(ma) : NULL, verbose); > + break; > + case OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE: > + format_u8x(ds, "gtpu_msgtype", nl_attr_get_u8(a), > + ma ? nl_attr_get(ma) : NULL, verbose); > + break; > case OVS_TUNNEL_KEY_ATTR_PAD: > break; > case __OVS_TUNNEL_KEY_ATTR_MAX: > @@ -5179,6 +5223,10 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names, > SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve, > geneve_to_attr); > SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr); > + SCAN_FIELD_NESTED("gtpu_flags=", uint8_t, u8, > + OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS); > + SCAN_FIELD_NESTED("gtpu_msg_type=", uint8_t, u8, > + OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE); > } SCAN_END_NESTED(); > > SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT); > diff --git a/lib/packets.h b/lib/packets.h > index b2bf706..16cb3f3 100644 > --- a/lib/packets.h > +++ b/lib/packets.h > @@ -1306,6 +1306,23 @@ BUILD_ASSERT_DECL(sizeof(struct vxlanhdr) == 8); > #define VXLAN_F_GPE 0x4000 > #define VXLAN_HF_GPE 0x04000000 > > +/* GTP-U protocol header */ > +struct gtpuhdr { > + uint8_t flags; > + uint8_t msgtype; > + ovs_be16 len; > + ovs_16aligned_be32 teid; > +}; > + > +/* GTP-U UDP port */ > +#define GTPU_DST_PORT 2152 > + > +/* Default GTP-U flags */ > +#define GTPU_FLAGS_DEFAULT 0x30 > + > +/* GTP-U message type for normal user plane PDU */ > +#define GTPU_MSGTYPE_GPDU 255 > + > /* Input values for PACKET_TYPE macros have to be in host byte order. > * The _BE postfix indicates result is in network byte order. Otherwise result > * is in host byte order. */ > @@ -1342,6 +1359,8 @@ enum packet_type { > PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS), > PT_MPLS_MC = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS_MCAST), > PT_NSH = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_NSH), > + /* GTP-U message. */ > + PT_GTPU_MSG = PACKET_TYPE(OFPHTN_UDP_TCP_PORT, GTPU_DST_PORT), > PT_UNKNOWN = PACKET_TYPE(0xffff, 0xffff), /* Unknown packet type. */ > }; > > diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c > index b814f7a..cb8fab8 100644 > --- a/lib/tnl-ports.c > +++ b/lib/tnl-ports.c > @@ -177,6 +177,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-ipfix.c b/ofproto/ofproto-dpif-ipfix.c > index 4d9fe78..2951a74 100644 > --- a/ofproto/ofproto-dpif-ipfix.c > +++ b/ofproto/ofproto-dpif-ipfix.c > @@ -88,6 +88,7 @@ enum dpif_ipfix_tunnel_type { > DPIF_IPFIX_TUNNEL_LISP = 0x03, > DPIF_IPFIX_TUNNEL_STT = 0x04, > DPIF_IPFIX_TUNNEL_GENEVE = 0x07, > + DPIF_IPFIX_TUNNEL_GTPU = 0x08, > NUM_DPIF_IPFIX_TUNNEL > }; > > @@ -389,6 +390,7 @@ static uint8_t tunnel_protocol[NUM_DPIF_IPFIX_TUNNEL] = { > IPPROTO_TCP, /* DPIF_IPFIX_TUNNEL_STT*/ > 0 , /* reserved */ > IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GENEVE*/ > + IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GTPU */ > }; > > OVS_PACKED( > @@ -509,6 +511,7 @@ BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_tcp) == 48); > * support tunnel key for: > * VxLAN: 24-bit VIN, > * GRE: 32-bit key, > + * GTPU: 32-bit key, > * LISP: 24-bit instance ID > * STT: 64-bit key > */ > @@ -811,6 +814,8 @@ dpif_ipfix_tunnel_type(const struct ofport *ofport) > return DPIF_IPFIX_TUNNEL_GENEVE; > } else if (strcmp(type, "stt") == 0) { > return DPIF_IPFIX_TUNNEL_STT; > + } else if (strcmp(type, "gtpu") == 0) { > + return DPIF_IPFIX_TUNNEL_GTPU; > } > > return DPIF_IPFIX_TUNNEL_UNKNOWN; > diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c > index bbec30e..042c7cf 100644 > --- a/ofproto/ofproto-dpif-sflow.c > +++ b/ofproto/ofproto-dpif-sflow.c > @@ -61,7 +61,8 @@ enum dpif_sflow_tunnel_type { > DPIF_SFLOW_TUNNEL_VXLAN, > DPIF_SFLOW_TUNNEL_GRE, > DPIF_SFLOW_TUNNEL_LISP, > - DPIF_SFLOW_TUNNEL_GENEVE > + DPIF_SFLOW_TUNNEL_GENEVE, > + DPIF_SFLOW_TUNNEL_GTPU > }; > > struct dpif_sflow_port { > @@ -621,6 +622,8 @@ dpif_sflow_tunnel_type(struct ofport *ofport) { > return DPIF_SFLOW_TUNNEL_VXLAN; > } else if (strcmp(type, "lisp") == 0) { > return DPIF_SFLOW_TUNNEL_LISP; > + } else if (strcmp(type, "gtpu") == 0) { > + return DPIF_SFLOW_TUNNEL_GTPU; > } else if (strcmp(type, "geneve") == 0) { > return DPIF_SFLOW_TUNNEL_GENEVE; > } > @@ -641,6 +644,7 @@ dpif_sflow_tunnel_proto(enum dpif_sflow_tunnel_type tunnel_type) > > case DPIF_SFLOW_TUNNEL_VXLAN: > case DPIF_SFLOW_TUNNEL_LISP: > + case DPIF_SFLOW_TUNNEL_GTPU: > case DPIF_SFLOW_TUNNEL_GENEVE: > ipproto = IPPROTO_UDP; > > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > index 3cb7b5e..f566e3c 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -3352,6 +3352,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: > @@ -7166,7 +7167,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) > ctx.xin->xport_uuid = in_port->uuid; > } > > - if (flow->packet_type != htonl(PT_ETH) && in_port && > + if (flow->packet_type != htonl(PT_GTPU_MSG) && > + flow->packet_type != htonl(PT_ETH) && in_port && > in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0) { > /* Add dummy Ethernet header to non-L2 packet if it's coming from a > * L3 port. So all packets will be L2 packets for lookup. > diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c > index a040b54..7de68c4 100644 > --- a/ofproto/tunnel.c > +++ b/ofproto/tunnel.c > @@ -384,6 +384,12 @@ tnl_wc_init(struct flow *flow, struct flow_wildcards *wc) > wc->masks.tunnel.tp_src = 0; > wc->masks.tunnel.tp_dst = 0; > > + /* GTP-U header are set to be always unwildcarded for GTP-U packets*/ > + if (flow->tunnel.gtpu_flags || flow->tunnel.gtpu_msgtype) { > + wc->masks.tunnel.gtpu_flags = UINT8_MAX; > + wc->masks.tunnel.gtpu_msgtype = UINT8_MAX; > + } > + > if (is_ip_any(flow) > && IP_ECN_is_ce(flow->tunnel.ip_tos)) { > wc->masks.nw_tos |= IP_ECN_MASK; > @@ -569,7 +575,8 @@ tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock) > match.ip_src_flow = ip_src == IP_SRC_FLOW; > > /* Look for a legacy L2 or L3 tunnel port first. */ > - if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) { > + if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE || > + flow->packet_type == htonl(PT_GTPU_MSG)) { > match.pt_mode = NETDEV_PT_LEGACY_L3; > } else { > match.pt_mode = NETDEV_PT_LEGACY_L2; > diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml > index 7ab90d5..83f6cbb 100644 > --- a/vswitchd/vswitch.xml > +++ b/vswitchd/vswitch.xml > @@ -2283,6 +2283,22 @@ > </p> > </dd> > > + <dt><code>gtpu</code></dt> > + <dd> > + 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. > + </dd> > + > <dt><code>stt</code></dt> > <dd> > The Stateless TCP Tunnel (STT) is particularly useful when tunnel
On Thu, May 17, 2018 at 05:11:24AM +0800, Aaron Conole wrote: > Yi Yang <yi.y.yang@intel.com> writes: > > Hi Yi, > > Thanks for the patch! Just a brief review. > Aaron, thank you so much for your quick review. > > > > Signed-off-by: Feng Yang <feng.yang@intel.com> > > Signed-off-by: Jiannan Ouyang <ouyangj@fb.com> > > Signed-off-by: Yi Yang <yi.y.yang@intel.com> > > Needs Co-author tags. Ok, will add it in v3. > > OXM_CLASSES = {"NXM_OF_": (0, 0x0000), > > "NXM_NX_": (0, 0x0001), > > "NXOXM_NSH_": (0x005ad650, 0xffff), > > + "NXOXM_GTPU_": (0x005ad651, 0xffff), > > "OXM_OF_": (0, 0x8000), > > "OXM_OF_PKT_REG": (0, 0x8001), > > "ONFOXM_ET_": (0x4f4e4600, 0xffff), > > diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h > > index 84ebcaf..ad6ea64 100644 > > --- a/datapath/linux/compat/include/linux/openvswitch.h > > +++ b/datapath/linux/compat/include/linux/openvswitch.h > > I don't see this in the upstream code. This is introducing another > place for kernel and ovs to diverge. Please get this type accepted > upstream first. You're partially right :-), there was the case OVS accepted first, then kernel did so. I think OVS community is very open for this. > > diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c > > index 40c4569..a628b72 100644 > > --- a/lib/dpif-netlink-rtnl.c > > +++ b/lib/dpif-netlink-rtnl.c > > Again, you'll need to get the netlink datapath changes through kernel > process. At least try. Yeah, I will, because Facebook guy Jiannan Ouyang did that before, they won't push this, so I'll try pushing this, but you know this is a slow process. > > break; > > @@ -814,6 +817,8 @@ netdev_to_ovs_vport_type(const char *type) > > return OVS_VPORT_TYPE_VXLAN; > > } else if (!strcmp(type, "lisp")) { > > return OVS_VPORT_TYPE_LISP; > > + } else if (strstr(type, "gtpu")) { > > Why isn't this using "!strcmp"? good catch, it should be "!strcmp", I'll change it in v3. > > > > + /* It won't be parsed if the packet contains application layer data only. > > + * These include, but not limited to GTP-U messages, GTP-C packets. > > + */ > > Why treat GTP-U and C separate here? GTP-C is very complicated and it is for control plane, so it won't be helpful to have it in OVS datapath, Joe mentioned this in v1 comments, we discussed this, GTP-U is what we care in real user scenarios. In addition, GPT-C and GTP-U use different UDP port, GTP-U tunnel can't handle GTP-C, here the comment may be misleading you. It means ovs won't parse the packet if it is GTP-U signaling message but not normal GTP-U PDU.
Hi Aaron, Yi, Good questions and explanation. Regarding the GTP-U and GTP-C issue, the comment that "These include, but not limited to GTP-U messages, GTP-C packets" sounds misleading indeed. We will remove "GTP-C packets" in v3. Regards, Feng 2018-5-17 -----Original Message----- From: Yang, Yi Y Sent: Thursday, May 17, 2018 10:22 AM To: Aaron Conole <aconole@redhat.com> Cc: dev@openvswitch.org; Yang, Feng <feng.yang@intel.com> Subject: Re: [ovs-dev] [PATCH v2 1/2] userspace datapath: Add GTP-U tunnel support On Thu, May 17, 2018 at 05:11:24AM +0800, Aaron Conole wrote: > Yi Yang <yi.y.yang@intel.com> writes: > > Hi Yi, > > Thanks for the patch! Just a brief review. > Aaron, thank you so much for your quick review. > > > > Signed-off-by: Feng Yang <feng.yang@intel.com> > > Signed-off-by: Jiannan Ouyang <ouyangj@fb.com> > > Signed-off-by: Yi Yang <yi.y.yang@intel.com> > > Needs Co-author tags. Ok, will add it in v3. > > OXM_CLASSES = {"NXM_OF_": (0, 0x0000), > > "NXM_NX_": (0, 0x0001), > > "NXOXM_NSH_": (0x005ad650, 0xffff), > > + "NXOXM_GTPU_": (0x005ad651, 0xffff), > > "OXM_OF_": (0, 0x8000), > > "OXM_OF_PKT_REG": (0, 0x8001), > > "ONFOXM_ET_": (0x4f4e4600, 0xffff), > > diff --git a/datapath/linux/compat/include/linux/openvswitch.h > > b/datapath/linux/compat/include/linux/openvswitch.h > > index 84ebcaf..ad6ea64 100644 > > --- a/datapath/linux/compat/include/linux/openvswitch.h > > +++ b/datapath/linux/compat/include/linux/openvswitch.h > > I don't see this in the upstream code. This is introducing another > place for kernel and ovs to diverge. Please get this type accepted > upstream first. You're partially right :-), there was the case OVS accepted first, then kernel did so. I think OVS community is very open for this. > > diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c index > > 40c4569..a628b72 100644 > > --- a/lib/dpif-netlink-rtnl.c > > +++ b/lib/dpif-netlink-rtnl.c > > Again, you'll need to get the netlink datapath changes through kernel > process. At least try. Yeah, I will, because Facebook guy Jiannan Ouyang did that before, they won't push this, so I'll try pushing this, but you know this is a slow process. > > break; > > @@ -814,6 +817,8 @@ netdev_to_ovs_vport_type(const char *type) > > return OVS_VPORT_TYPE_VXLAN; > > } else if (!strcmp(type, "lisp")) { > > return OVS_VPORT_TYPE_LISP; > > + } else if (strstr(type, "gtpu")) { > > Why isn't this using "!strcmp"? good catch, it should be "!strcmp", I'll change it in v3. > > > > + /* It won't be parsed if the packet contains application layer data only. > > + * These include, but not limited to GTP-U messages, GTP-C packets. > > + */ > > Why treat GTP-U and C separate here? GTP-C is very complicated and it is for control plane, so it won't be helpful to have it in OVS datapath, Joe mentioned this in v1 comments, we discussed this, GTP-U is what we care in real user scenarios. In addition, GPT-C and GTP-U use different UDP port, GTP-U tunnel can't handle GTP-C, here the comment may be misleading you. It means ovs won't parse the packet if it is GTP-U signaling message but not normal GTP-U PDU.
diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields index 184b75e..944f095 100755 --- a/build-aux/extract-ofp-fields +++ b/build-aux/extract-ofp-fields @@ -68,6 +68,7 @@ PREREQS = {"none": "MFP_NONE", OXM_CLASSES = {"NXM_OF_": (0, 0x0000), "NXM_NX_": (0, 0x0001), "NXOXM_NSH_": (0x005ad650, 0xffff), + "NXOXM_GTPU_": (0x005ad651, 0xffff), "OXM_OF_": (0, 0x8000), "OXM_OF_PKT_REG": (0, 0x8001), "ONFOXM_ET_": (0x4f4e4600, 0xffff), diff --git a/datapath/linux/compat/include/linux/openvswitch.h b/datapath/linux/compat/include/linux/openvswitch.h index 84ebcaf..ad6ea64 100644 --- a/datapath/linux/compat/include/linux/openvswitch.h +++ b/datapath/linux/compat/include/linux/openvswitch.h @@ -236,6 +236,7 @@ enum ovs_vport_type { OVS_VPORT_TYPE_GRE, /* GRE tunnel. */ OVS_VPORT_TYPE_VXLAN, /* VXLAN tunnel. */ OVS_VPORT_TYPE_GENEVE, /* Geneve tunnel. */ + OVS_VPORT_TYPE_GTPU, /* GTPU tunnel. */ OVS_VPORT_TYPE_LISP = 105, /* LISP tunnel */ OVS_VPORT_TYPE_STT = 106, /* STT tunnel */ __OVS_VPORT_TYPE_MAX @@ -394,6 +395,8 @@ enum ovs_tunnel_key_attr { OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS, /* Nested OVS_VXLAN_EXT_* */ OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */ OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */ + OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS, /* u8 GTP-U FLAGS. */ + OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE, /* u8 GTP-U MSGTYPE. */ OVS_TUNNEL_KEY_ATTR_PAD, __OVS_TUNNEL_KEY_ATTR_MAX }; diff --git a/include/openvswitch/match.h b/include/openvswitch/match.h index 49333ae..9bcc4f4 100644 --- a/include/openvswitch/match.h +++ b/include/openvswitch/match.h @@ -109,6 +109,10 @@ void match_set_tun_gbp_id_masked(struct match *match, ovs_be16 gbp_id, ovs_be16 void match_set_tun_gbp_id(struct match *match, ovs_be16 gbp_id); void match_set_tun_gbp_flags_masked(struct match *match, uint8_t flags, uint8_t mask); void match_set_tun_gbp_flags(struct match *match, uint8_t flags); +void match_set_tun_gtpu_flags(struct match *match, uint8_t gtpu_flags); +void match_set_tun_gtpu_flags_masked(struct match *match, uint8_t gtpu_flags, + uint8_t mask); +void match_set_tun_gtpu_msgtype(struct match *match, uint8_t gtpu_msgtype); 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 98c9e1c..884a2f6 100644 --- a/include/openvswitch/meta-flow.h +++ b/include/openvswitch/meta-flow.h @@ -1848,6 +1848,34 @@ enum OVS_PACKED_ENUM mf_field_id { */ MFF_NSH_TTL, + /* "tun_gtpu_flags". + * + * The flags in a GTP-U tunnel header. + * + * Type: u8. + * Maskable: bitwise. + * Formatting: decimal. + * Prerequisites: none. + * Access: read/write. + * NXM: none. + * OXM: NXOXM_GTPU_FLAGS(1) since OF1.3 and v2.10. + */ + MFF_TUN_GTPU_FLAGS, + + /* "tun_gtpu_msgtype". + * + * The message type in a GTP-U tunnel header. + * + * Type: u8. + * Maskable: no. + * Formatting: decimal. + * Prerequisites: none. + * Access: read/write. + * NXM: none. + * OXM: NXOXM_GTPU_MSGTYPE(2) since OF1.3 and v2.10. + */ + MFF_TUN_GTPU_MSGTYPE, + MFF_N_IDS }; diff --git a/include/openvswitch/packets.h b/include/openvswitch/packets.h index fef756b..1f3b499 100644 --- a/include/openvswitch/packets.h +++ b/include/openvswitch/packets.h @@ -39,7 +39,9 @@ struct flow_tnl { ovs_be16 tp_dst; ovs_be16 gbp_id; uint8_t gbp_flags; - uint8_t pad1[5]; /* Pad to 64 bits. */ + uint8_t gtpu_flags; + uint8_t gtpu_msgtype; + uint8_t pad1[3]; /* Pad to 64 bits. */ struct tun_metadata metadata; }; diff --git a/lib/dpif-netlink-rtnl.c b/lib/dpif-netlink-rtnl.c index 40c4569..a628b72 100644 --- a/lib/dpif-netlink-rtnl.c +++ b/lib/dpif-netlink-rtnl.c @@ -99,6 +99,8 @@ vport_type_to_kind(enum ovs_vport_type type, } case OVS_VPORT_TYPE_GENEVE: return "geneve"; + case OVS_VPORT_TYPE_GTPU: + return "gtpu"; case OVS_VPORT_TYPE_NETDEV: case OVS_VPORT_TYPE_INTERNAL: case OVS_VPORT_TYPE_LISP: @@ -262,6 +264,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: @@ -327,6 +330,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: @@ -438,6 +442,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 bb9e95d..8b5bd97 100644 --- a/lib/dpif-netlink.c +++ b/lib/dpif-netlink.c @@ -787,6 +787,9 @@ get_vport_type(const struct dpif_netlink_vport *vport) case OVS_VPORT_TYPE_STT: return "stt"; + case OVS_VPORT_TYPE_GTPU: + return "gtpu"; + case OVS_VPORT_TYPE_UNSPEC: case __OVS_VPORT_TYPE_MAX: break; @@ -814,6 +817,8 @@ netdev_to_ovs_vport_type(const char *type) return OVS_VPORT_TYPE_VXLAN; } else if (!strcmp(type, "lisp")) { return OVS_VPORT_TYPE_LISP; + } else if (strstr(type, "gtpu")) { + return OVS_VPORT_TYPE_GTPU; } else { return OVS_VPORT_TYPE_UNSPEC; } diff --git a/lib/flow.c b/lib/flow.c index 09b66b8..d5af1e8 100644 --- a/lib/flow.c +++ b/lib/flow.c @@ -695,6 +695,13 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst) miniflow_push_be32(mf, packet_type, packet_type); } + /* It won't be parsed if the packet contains application layer data only. + * These include, but not limited to GTP-U messages, GTP-C packets. + */ + if (packet_type == htonl(PT_GTPU_MSG)) { + goto out; + } + /* Initialize packet's layer pointer and offsets. */ frame = data; dp_packet_reset_offsets(packet); diff --git a/lib/match.c b/lib/match.c index 2e9a803..e5cc9c6 100644 --- a/lib/match.c +++ b/lib/match.c @@ -320,6 +320,27 @@ match_set_tun_gbp_flags(struct match *match, uint8_t flags) } void +match_set_tun_gtpu_flags(struct match *match, uint8_t gtpu_flags) +{ + match_set_tun_gtpu_flags_masked(match, gtpu_flags, UINT8_MAX); +} + +void +match_set_tun_gtpu_flags_masked(struct match *match, uint8_t gtpu_flags, + uint8_t mask) +{ + match->wc.masks.tunnel.gtpu_flags = mask; + match->flow.tunnel.gtpu_flags = gtpu_flags & mask; +} + +void +match_set_tun_gtpu_msgtype(struct match *match, uint8_t gtpu_msgtype) +{ + match->flow.tunnel.gtpu_msgtype = gtpu_msgtype; + match->wc.masks.tunnel.gtpu_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); @@ -1239,6 +1260,14 @@ format_flow_tunnel(struct ds *s, const struct match *match) FLOW_TNL_F_MASK); ds_put_char(s, ','); } + + if (wc->masks.tunnel.gtpu_flags) { + ds_put_format(s, "tun_gtpu_flags=%"PRIx8",", tnl->gtpu_flags); + } + if (wc->masks.tunnel.gtpu_msgtype) { + ds_put_format(s, "tun_gtpu_msgtype=%"PRIu8",", tnl->gtpu_msgtype); + } + tun_metadata_match_format(s, match); } diff --git a/lib/meta-flow.c b/lib/meta-flow.c index aa2ec01..bde59df 100644 --- a/lib/meta-flow.c +++ b/lib/meta-flow.c @@ -231,6 +231,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc) return !wc->masks.tunnel.gbp_id; case MFF_TUN_GBP_FLAGS: return !wc->masks.tunnel.gbp_flags; + case MFF_TUN_GTPU_FLAGS: + return !wc->masks.tunnel.gtpu_flags; + case MFF_TUN_GTPU_MSGTYPE: + return !wc->masks.tunnel.gtpu_msgtype; CASE_MFF_TUN_METADATA: return !ULLONG_GET(wc->masks.tunnel.metadata.present.map, mf->id - MFF_TUN_METADATA0); @@ -513,6 +517,7 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_TUN_TTL: case MFF_TUN_GBP_ID: case MFF_TUN_GBP_FLAGS: + case MFF_TUN_GTPU_MSGTYPE: CASE_MFF_TUN_METADATA: case MFF_METADATA: case MFF_IN_PORT: @@ -625,6 +630,9 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value) case MFF_NSH_C4: return true; + case MFF_TUN_GTPU_FLAGS: + return (value->u8 == GTPU_FLAGS_DEFAULT); + case MFF_N_IDS: default: OVS_NOT_REACHED(); @@ -674,6 +682,12 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow, case MFF_TUN_GBP_FLAGS: value->u8 = flow->tunnel.gbp_flags; 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_TTL: value->u8 = flow->tunnel.ip_ttl; break; @@ -988,6 +1002,12 @@ mf_set_value(const struct mf_field *mf, case MFF_TUN_GBP_FLAGS: match_set_tun_gbp_flags(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_TOS: match_set_tun_tos(match, value->u8); break; @@ -1385,6 +1405,12 @@ mf_set_flow_value(const struct mf_field *mf, case MFF_TUN_GBP_FLAGS: flow->tunnel.gbp_flags = 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_TOS: flow->tunnel.ip_tos = value->u8; break; @@ -1700,6 +1726,8 @@ mf_is_pipeline_field(const struct mf_field *mf) case MFF_TUN_FLAGS: case MFF_TUN_GBP_ID: case MFF_TUN_GBP_FLAGS: + case MFF_TUN_GTPU_FLAGS: + case MFF_TUN_GTPU_MSGTYPE: CASE_MFF_TUN_METADATA: case MFF_METADATA: case MFF_IN_PORT: @@ -1870,6 +1898,14 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str) case MFF_TUN_GBP_FLAGS: match_set_tun_gbp_flags_masked(match, 0, 0); break; + case MFF_TUN_GTPU_FLAGS: + match->wc.masks.tunnel.gtpu_flags = 0; + match->flow.tunnel.gtpu_flags = 0; + break; + case MFF_TUN_GTPU_MSGTYPE: + match->wc.masks.tunnel.gtpu_msgtype = 0; + match->flow.tunnel.gtpu_msgtype = 0; + break; case MFF_TUN_TOS: match_set_tun_tos_masked(match, 0, 0); break; @@ -2221,6 +2257,7 @@ mf_set(const struct mf_field *mf, case MFF_ICMPV4_CODE: case MFF_ICMPV6_TYPE: case MFF_ICMPV6_CODE: + case MFF_TUN_GTPU_MSGTYPE: return OFPUTIL_P_NONE; case MFF_DP_HASH: @@ -2250,6 +2287,9 @@ mf_set(const struct mf_field *mf, case MFF_TUN_GBP_FLAGS: match_set_tun_gbp_flags_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_TTL: match_set_tun_ttl_masked(match, value->u8, mask->u8); break; diff --git a/lib/meta-flow.xml b/lib/meta-flow.xml index 933d4b8..d169772 100644 --- a/lib/meta-flow.xml +++ b/lib/meta-flow.xml @@ -731,6 +731,12 @@ tcp,tp_src=0x07c0/0xfff0 Used by Open vSwitch for NSH extensions, in the absence of an official ONF-assigned class. (This OUI is randomly generated.) </dd> + + <dt>0x005ad651 (<code>NXOXM_GTPU</code>)</dt> + <dd> + Used by Open vSwitch for GTP-U extensions, in the absence of an official + ONF-assigned class. (This OUI is randomly generated.) + </dd> </dl> <p> @@ -1899,6 +1905,68 @@ ovs-ofctl add-flow br0 tun_metadata0=1234,actions=controller </p> </field> + <h2>GTP-U Fields</h2> + + <p> + The GTP-U (v1) header is defined as follows [3GPP TS 29.281]. + </p> + + <diagram> + <header name="GTP-U tunnel flags"> + <bits name="V" above="3" width="0.15"/> + <bits name="PT" above="1" width="0.15"/> + <bits name="(*)" above="1" width="0.15"/> + <bits name="E" above="1" width="0.15"/> + <bits name="S" above="1" width="0.15"/> + <bits name="PN" above="1" width="0.15"/> + </header> + <nospace/> + <header> + <bits name="Message Type" above="8" width="0.15"/> + <bits name="Length" above="16" width="0.15"/> + <bits name="TEID" above="32" width="0.15"/> + <bits name="..." above="" width="0.15"/> + </header> + </diagram> + + <p> + where + </p> + <ul> + <li><code>V</code>: version fields, used to determine the version of the + GTP-U protocol, which should be set to '1'.</li> + <li><code>PT</code>: Packet Type, used as a protocol discriminator + between GTP (when PT is '1') and GTP' (when PT is '0').</li> + <li><code>E</code>: Extension Header flag, indicating the presence of a + meaningful value of the Next Extension Header field.</li> + <li><code>S</code>: Sequence number flag, indicating the presence of a + meaningful value of the Sequence Number field.</li> + <li><code>PN</code>: N-PDU Number flag, indicating the presence of a + meaningful value of the N-PDU Number field.</li> + <li><code>Message Type</code>: indicating the type of GTP-U message. + </li> + <li><code>Length</code>: indicating the length in octets of the payload. + </li> + <li><code>TEID</code>: Tunnel Endpoint Identifier,unambiguously + identifying a tunnel endpoint in the receiving GTP-U protocol entity. + Open vSwitch makes TEID availabe via <ref field="tun_id"/>.</li> + </ul> + <field id="MFF_TUN_GTPU_FLAGS" title="GTP-U tunnel flags"> + <p> + For a packet tunneled over GTP-U, this field indicates the presence of + optional headers. + </p> + </field> + + <field id="MFF_TUN_GTPU_MSGTYPE" title="GTP-U tunnel message type"> + <p> + For a packet tunneled over GTP-U, 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]. + </p> + </field> + <!-- Open vSwitch uses the following fields internally, but it does not expose them to the user via OpenFlow, so we do not document them. --> @@ -4662,6 +4730,14 @@ r r c c c. <url href="https://tools.ietf.org/html/rfc7665"/>. </dd> + <dt>3GPP TS 29.281</dt> + <dd> + 3GPP, + ``General Packet Radio System (GPRS) Tunnelling Protocol User Plane + (GTPv1-U),'' + <url href="http://www.3gpp.org/ftp/Specs/html-info/29281.htm"/>. + </dd> + <dt>Srinivasan</dt> <dd> V. Srinivasan, S. Suriy, and G. Varghese, ``Packet diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c index c3e698d..cace175 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; @@ -715,7 +718,147 @@ netdev_geneve_build_header(const struct netdev *netdev, return 0; } - +void +netdev_gtpu_push_header(struct dp_packet *packet, + const struct ovs_action_push_tnl *data) +{ + struct udp_header *udp; + struct gtpuhdr *gtph; + int gtpu_len; + int ip_tot_size; + + gtpu_len = dp_packet_size(packet); + + udp = netdev_tnl_push_ip_header(packet, data->header, data->header_len, + &ip_tot_size); + udp->udp_len = htons(ip_tot_size); + + gtph = (struct gtpuhdr *)(udp + 1); + gtph->len = htons(gtpu_len); + + 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); + } + } + + packet->packet_type = htonl(PT_ETH); +} + + +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; + struct udp_header *udp; + + /* XXX: RCUfy tnl_cfg. */ + ovs_mutex_lock(&dev->mutex); + + tnl_cfg = &dev->tnl_cfg; + udp = netdev_tnl_ip_build_header(data, params, IPPROTO_UDP); + udp->udp_dst = tnl_cfg->dst_port; + udp->udp_src = htons(GTPU_DST_PORT); + + if (params->is_ipv6 || params->flow->tunnel.flags & FLOW_TNL_F_CSUM) { + /* Write a value in now to mark that we should compute the checksum + * later. 0xffff is handy because it is transparent to the + * calculation. */ + udp->udp_csum = htons(0xffff); + } + + ovs_mutex_unlock(&dev->mutex); + data->header_len += sizeof *udp; + + gtph = (struct gtpuhdr *)(udp + 1); + gtph->flags = params->flow->tunnel.gtpu_flags; + gtph->msgtype = params->flow->tunnel.gtpu_msgtype; + + put_16aligned_be32(>ph->teid, + htonl(ntohll(params->flow->tunnel.tun_id))); + data->header_len += sizeof *gtph; + data->tnl_type = OVS_VPORT_TYPE_GTPU; + return 0; +} + +struct dp_packet * +netdev_gtpu_pop_header(struct dp_packet *packet) +{ + struct pkt_metadata *md = &packet->md; + struct flow_tnl *tnl = &md->tunnel; + struct gtpuhdr *gtph; + unsigned int hlen; + + pkt_metadata_init_tnl(md); + if (GTPU_HLEN > dp_packet_l4_size(packet)) { + VLOG_WARN_RL(&err_rl, "GTP-U packet too small: min header=%u " + "packet size=%"PRIuSIZE"\n", + (unsigned int)GTPU_HLEN, dp_packet_l4_size(packet)); + goto err; + } + + gtph = udp_extract_tnl_md(packet, tnl, &hlen); + if (!gtph) { + goto err; + } + + if (gtph->flags == 0x30) { + /* Only GTP-U v1 packets without optional fileds are processed, i.e. + * 8 7 6 5 4 3 2 1 + * | ver | PT | 0 | E | S | PN | + * 0 0 1 1 0 0 0 0 + */ + tnl->gtpu_flags = gtph->flags; + } else { + goto err; + } + tnl->gtpu_msgtype = gtph->msgtype; + tnl->tun_id = htonll(ntohl(get_16aligned_be32(>ph->teid))); + + /*Figure out whether the inner packet is IPv4, IPv6 or a GTP-U message.*/ + if (tnl->gtpu_msgtype == GTPU_MSGTYPE_GPDU) { + struct ip_header *ip; + + ip = (struct ip_header *)(gtph + 1); + if (IP_VER(ip->ip_ihl_ver) == 4) { + packet->packet_type = htonl(PT_IPV4); + } else if (IP_VER(ip->ip_ihl_ver) == 6) { + packet->packet_type = htonl(PT_IPV6); + } else { + goto err; + } + } else { + /* GTP-U messages, including echo request, end marker, etc. + * OVS will be kicking it to a control-plane entity if properly + * configured. + */ + packet->packet_type = htonl(PT_GTPU_MSG); + } + + dp_packet_reset_packet(packet, hlen + GTPU_HLEN); + + return packet; +err: + dp_packet_delete(packet); + return NULL; +} + void netdev_tnl_egress_port_range(struct unixctl_conn *conn, int argc, const char *argv[], void *aux OVS_UNUSED) diff --git a/lib/netdev-native-tnl.h b/lib/netdev-native-tnl.h index a912ce9..9c4cc66 100644 --- a/lib/netdev-native-tnl.h +++ b/lib/netdev-native-tnl.h @@ -58,6 +58,18 @@ netdev_vxlan_build_header(const struct netdev *netdev, struct dp_packet * netdev_vxlan_pop_header(struct dp_packet *packet); +void +netdev_gtpu_push_header(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 *params); + +struct dp_packet * +netdev_gtpu_pop_header(struct dp_packet *packet); + static inline bool netdev_tnl_is_header_ipv6(const void *header) { diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index 52aa12d..ee454a1 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -107,7 +107,10 @@ 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 * @@ -199,6 +202,8 @@ netdev_vport_construct(struct netdev *netdev_) dev->tnl_cfg.dst_port = htons(LISP_DST_PORT); } else if (!strcmp(type, "stt")) { dev->tnl_cfg.dst_port = htons(STT_DST_PORT); + } else if (!strcmp(type, "gtpu")) { + dev->tnl_cfg.dst_port = htons(GTPU_DST_PORT); } dev->tnl_cfg.dont_fragment = true; @@ -408,6 +413,8 @@ tunnel_supported_layers(const char *type, return TNL_L3; } else if (!strcmp(type, "gre")) { return TNL_L2 | TNL_L3; + } else if (!strcmp(type, "gtpu")) { + return TNL_L3; } else if (!strcmp(type, "vxlan") && tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) { return TNL_L2 | TNL_L3; @@ -455,6 +462,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; @@ -700,6 +711,7 @@ 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("gtpu", type) && dst_port != GTPU_DST_PORT) || (!strcmp("stt", type) && dst_port != STT_DST_PORT)) { smap_add_format(args, "dst_port", "%d", dst_port); } @@ -979,6 +991,10 @@ netdev_vport_tunnel_register(void) NETDEV_VPORT_GET_IFINDEX), TUNNEL_CLASS("lisp", "lisp_sys", NULL, NULL, NULL, NULL), TUNNEL_CLASS("stt", "stt_sys", NULL, NULL, NULL, NULL), + TUNNEL_CLASS("gtpu", "gptu_sys", netdev_gtpu_build_header, + netdev_gtpu_push_header, + netdev_gtpu_pop_header, + NULL), }; static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; diff --git a/lib/nx-match.c b/lib/nx-match.c index a8edb2e..c7f6c27 100644 --- a/lib/nx-match.c +++ b/lib/nx-match.c @@ -1153,6 +1153,10 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match, flow->tunnel.gbp_id, match->wc.masks.tunnel.gbp_id); nxm_put_8m(&ctx, MFF_TUN_GBP_FLAGS, oxm, flow->tunnel.gbp_flags, match->wc.masks.tunnel.gbp_flags); + 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); tun_metadata_to_nx_match(b, oxm, match); /* Network Service Header */ diff --git a/lib/odp-util.c b/lib/odp-util.c index 70188b6..2c5a1f1 100644 --- a/lib/odp-util.c +++ b/lib/odp-util.c @@ -712,6 +712,15 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data) options++; } ds_put_format(ds, ")"); + } else if (data->tnl_type == OVS_VPORT_TYPE_GTPU) { + const struct gtpuhdr *gtph; + + gtph = format_udp_tnl_push_header(ds, udp); + + ds_put_format(ds, "gtpu(flags=0x%"PRIx8 + ",msgtype=0x%"PRIx8",teid=0x%"PRIx32")", + gtph->flags, gtph->msgtype, + ntohl(get_16aligned_be32(>ph->teid))); } ds_put_format(ds, ")"); } @@ -1474,6 +1483,7 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data) if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16",csum=0x%"SCNx16"),", &udp_src, &udp_dst, &csum)) { uint32_t vx_flags, vni; + uint8_t gtpu_flags, gtpu_msgtype; udp->udp_src = htons(udp_src); udp->udp_dst = htons(udp_dst); @@ -1528,6 +1538,17 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data) gnh->proto_type = htons(ETH_TYPE_TEB); put_16aligned_be32(&gnh->vni, htonl(vni << 8)); tnl_type = OVS_VPORT_TYPE_GENEVE; + } else if (ovs_scan_len(s, &n, "gtpu(flags=0x%"SCNx8",msgtype=0x%" + SCNx8",teid=0x%"SCNx32"))", + >pu_flags, >pu_msgtype, &vni)) { + struct gtpuhdr *gtph = (struct gtpuhdr *) (udp + 1); + + gtph->flags = gtpu_flags; + gtph->msgtype = gtpu_msgtype; + put_16aligned_be32(>ph->teid, htonl(vni)); + tnl_type = OVS_VPORT_TYPE_GTPU; + header_len = sizeof *eth + ip_len + + sizeof *udp + sizeof *gtph; } else { return -EINVAL; } @@ -2373,6 +2394,8 @@ static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX + [OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS] = { .len = ATTR_LEN_NESTED, .next = ovs_vxlan_ext_attr_lens , .next_max = OVS_VXLAN_EXT_MAX}, + [OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS] = { .len = 1 }, + [OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE] = { .len = 1 }, [OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = 16 }, [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = 16 }, }; @@ -2698,6 +2721,12 @@ odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask, case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: tun_metadata_from_geneve_nlattr(a, is_mask, tun); break; + case OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS: + tun->gtpu_flags = nl_attr_get_u8(a); + break; + case OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE: + tun->gtpu_msgtype = nl_attr_get_u8(a); + break; default: /* Allow this to show up as unexpected, if there are unknown @@ -2767,6 +2796,13 @@ tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key, if (tun_key->flags & FLOW_TNL_F_OAM) { nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_OAM); } + if (tun_key->gtpu_flags) { + nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS, tun_key->gtpu_flags); + } + if (tun_key->gtpu_msgtype) { + nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE, + tun_key->gtpu_msgtype); + } /* If tnl_type is set to a particular type of output tunnel, * only put its relevant tunnel metadata to the nlattr. @@ -3487,6 +3523,14 @@ format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr, format_odp_tun_geneve(a, ma, ds, verbose); ds_put_cstr(ds, "),"); break; + case OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS: + format_u8x(ds, "gtpu_flags", nl_attr_get_u8(a), + ma ? nl_attr_get(ma) : NULL, verbose); + break; + case OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE: + format_u8x(ds, "gtpu_msgtype", nl_attr_get_u8(a), + ma ? nl_attr_get(ma) : NULL, verbose); + break; case OVS_TUNNEL_KEY_ATTR_PAD: break; case __OVS_TUNNEL_KEY_ATTR_MAX: @@ -5179,6 +5223,10 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names, SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve, geneve_to_attr); SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr); + SCAN_FIELD_NESTED("gtpu_flags=", uint8_t, u8, + OVS_TUNNEL_KEY_ATTR_GTPU_FLAGS); + SCAN_FIELD_NESTED("gtpu_msg_type=", uint8_t, u8, + OVS_TUNNEL_KEY_ATTR_GTPU_MSGTYPE); } SCAN_END_NESTED(); SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT); diff --git a/lib/packets.h b/lib/packets.h index b2bf706..16cb3f3 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -1306,6 +1306,23 @@ BUILD_ASSERT_DECL(sizeof(struct vxlanhdr) == 8); #define VXLAN_F_GPE 0x4000 #define VXLAN_HF_GPE 0x04000000 +/* GTP-U protocol header */ +struct gtpuhdr { + uint8_t flags; + uint8_t msgtype; + ovs_be16 len; + ovs_16aligned_be32 teid; +}; + +/* GTP-U UDP port */ +#define GTPU_DST_PORT 2152 + +/* Default GTP-U flags */ +#define GTPU_FLAGS_DEFAULT 0x30 + +/* GTP-U message type for normal user plane PDU */ +#define GTPU_MSGTYPE_GPDU 255 + /* Input values for PACKET_TYPE macros have to be in host byte order. * The _BE postfix indicates result is in network byte order. Otherwise result * is in host byte order. */ @@ -1342,6 +1359,8 @@ enum packet_type { PT_MPLS = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS), PT_MPLS_MC = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_MPLS_MCAST), PT_NSH = PACKET_TYPE(OFPHTN_ETHERTYPE, ETH_TYPE_NSH), + /* GTP-U message. */ + PT_GTPU_MSG = PACKET_TYPE(OFPHTN_UDP_TCP_PORT, GTPU_DST_PORT), PT_UNKNOWN = PACKET_TYPE(0xffff, 0xffff), /* Unknown packet type. */ }; diff --git a/lib/tnl-ports.c b/lib/tnl-ports.c index b814f7a..cb8fab8 100644 --- a/lib/tnl-ports.c +++ b/lib/tnl-ports.c @@ -177,6 +177,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-ipfix.c b/ofproto/ofproto-dpif-ipfix.c index 4d9fe78..2951a74 100644 --- a/ofproto/ofproto-dpif-ipfix.c +++ b/ofproto/ofproto-dpif-ipfix.c @@ -88,6 +88,7 @@ enum dpif_ipfix_tunnel_type { DPIF_IPFIX_TUNNEL_LISP = 0x03, DPIF_IPFIX_TUNNEL_STT = 0x04, DPIF_IPFIX_TUNNEL_GENEVE = 0x07, + DPIF_IPFIX_TUNNEL_GTPU = 0x08, NUM_DPIF_IPFIX_TUNNEL }; @@ -389,6 +390,7 @@ static uint8_t tunnel_protocol[NUM_DPIF_IPFIX_TUNNEL] = { IPPROTO_TCP, /* DPIF_IPFIX_TUNNEL_STT*/ 0 , /* reserved */ IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GENEVE*/ + IPPROTO_UDP, /* DPIF_IPFIX_TUNNEL_GTPU */ }; OVS_PACKED( @@ -509,6 +511,7 @@ BUILD_ASSERT_DECL(sizeof(struct ipfix_data_record_aggregated_tcp) == 48); * support tunnel key for: * VxLAN: 24-bit VIN, * GRE: 32-bit key, + * GTPU: 32-bit key, * LISP: 24-bit instance ID * STT: 64-bit key */ @@ -811,6 +814,8 @@ dpif_ipfix_tunnel_type(const struct ofport *ofport) return DPIF_IPFIX_TUNNEL_GENEVE; } else if (strcmp(type, "stt") == 0) { return DPIF_IPFIX_TUNNEL_STT; + } else if (strcmp(type, "gtpu") == 0) { + return DPIF_IPFIX_TUNNEL_GTPU; } return DPIF_IPFIX_TUNNEL_UNKNOWN; diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c index bbec30e..042c7cf 100644 --- a/ofproto/ofproto-dpif-sflow.c +++ b/ofproto/ofproto-dpif-sflow.c @@ -61,7 +61,8 @@ enum dpif_sflow_tunnel_type { DPIF_SFLOW_TUNNEL_VXLAN, DPIF_SFLOW_TUNNEL_GRE, DPIF_SFLOW_TUNNEL_LISP, - DPIF_SFLOW_TUNNEL_GENEVE + DPIF_SFLOW_TUNNEL_GENEVE, + DPIF_SFLOW_TUNNEL_GTPU }; struct dpif_sflow_port { @@ -621,6 +622,8 @@ dpif_sflow_tunnel_type(struct ofport *ofport) { return DPIF_SFLOW_TUNNEL_VXLAN; } else if (strcmp(type, "lisp") == 0) { return DPIF_SFLOW_TUNNEL_LISP; + } else if (strcmp(type, "gtpu") == 0) { + return DPIF_SFLOW_TUNNEL_GTPU; } else if (strcmp(type, "geneve") == 0) { return DPIF_SFLOW_TUNNEL_GENEVE; } @@ -641,6 +644,7 @@ dpif_sflow_tunnel_proto(enum dpif_sflow_tunnel_type tunnel_type) case DPIF_SFLOW_TUNNEL_VXLAN: case DPIF_SFLOW_TUNNEL_LISP: + case DPIF_SFLOW_TUNNEL_GTPU: case DPIF_SFLOW_TUNNEL_GENEVE: ipproto = IPPROTO_UDP; diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c index 3cb7b5e..f566e3c 100644 --- a/ofproto/ofproto-dpif-xlate.c +++ b/ofproto/ofproto-dpif-xlate.c @@ -3352,6 +3352,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: @@ -7166,7 +7167,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout) ctx.xin->xport_uuid = in_port->uuid; } - if (flow->packet_type != htonl(PT_ETH) && in_port && + if (flow->packet_type != htonl(PT_GTPU_MSG) && + flow->packet_type != htonl(PT_ETH) && in_port && in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0) { /* Add dummy Ethernet header to non-L2 packet if it's coming from a * L3 port. So all packets will be L2 packets for lookup. diff --git a/ofproto/tunnel.c b/ofproto/tunnel.c index a040b54..7de68c4 100644 --- a/ofproto/tunnel.c +++ b/ofproto/tunnel.c @@ -384,6 +384,12 @@ tnl_wc_init(struct flow *flow, struct flow_wildcards *wc) wc->masks.tunnel.tp_src = 0; wc->masks.tunnel.tp_dst = 0; + /* GTP-U header are set to be always unwildcarded for GTP-U packets*/ + if (flow->tunnel.gtpu_flags || flow->tunnel.gtpu_msgtype) { + wc->masks.tunnel.gtpu_flags = UINT8_MAX; + wc->masks.tunnel.gtpu_msgtype = UINT8_MAX; + } + if (is_ip_any(flow) && IP_ECN_is_ce(flow->tunnel.ip_tos)) { wc->masks.nw_tos |= IP_ECN_MASK; @@ -569,7 +575,8 @@ tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock) match.ip_src_flow = ip_src == IP_SRC_FLOW; /* Look for a legacy L2 or L3 tunnel port first. */ - if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE) { + if (pt_ns(flow->packet_type) == OFPHTN_ETHERTYPE || + flow->packet_type == htonl(PT_GTPU_MSG)) { match.pt_mode = NETDEV_PT_LEGACY_L3; } else { match.pt_mode = NETDEV_PT_LEGACY_L2; diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml index 7ab90d5..83f6cbb 100644 --- a/vswitchd/vswitch.xml +++ b/vswitchd/vswitch.xml @@ -2283,6 +2283,22 @@ </p> </dd> + <dt><code>gtpu</code></dt> + <dd> + 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. + </dd> + <dt><code>stt</code></dt> <dd> The Stateless TCP Tunnel (STT) is particularly useful when tunnel