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(&gtph->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(&gtph->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(&gtph->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"))",
> +                                &gtpu_flags, &gtpu_msgtype, &vni)) {
> +            struct gtpuhdr *gtph = (struct gtpuhdr *) (udp + 1);
> +
> +            gtph->flags = gtpu_flags;
> +            gtph->msgtype = gtpu_msgtype;
> +            put_16aligned_be32(&gtph->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

Reply via email to