Yi Yang <[email protected]> writes: Hi Yi,
Thanks for the patch! Just a brief review. > From: Feng Yang <[email protected]> > > 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 <[email protected]> > Signed-off-by: Jiannan Ouyang <[email protected]> > Signed-off-by: Yi Yang <[email protected]> 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 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
