@@ -225,6 +225,19 @@ Q: Does Open vSwitch support IPv6 GRE?
options:remote_ip=fc00:100::1 \
options:packet_type=legacy_l2
+Q: Does Open vSwitch support GTP-U?
+
+ A: Yes. GTP-U (GPRS Tunnelling Protocol User Plane (GTPv1-U))
+ is supported in userspace datapath. TEID is set by using tunnel
+ key field.
+
+ ::
+
+ $ ovs-vsctl add-br br0
+ $ ovs-vsctl add-port br0 gtpu0 -- \
+ set int gtpu0 type=gtpu options:key=<teid> \
+ options:remote_ip=172.31.1.1
+
Q: How do I connect two bridges?
A: First, why do you want to do this? Two connected bridges are not much
@@ -130,6 +130,7 @@ Q: Are all features available with all datapaths?
Tunnel - Geneve-IPv6 4.4 2.6 2.6 NO
Tunnel - ERSPAN 4.18 2.10 2.10 NO
Tunnel - ERSPAN-IPv6 4.18 2.10 2.10 NO
+ Tunnel - GTP-U NO NO 2.13 NO
QoS - Policing YES 1.1 2.6 NO
QoS - Shaping YES 1.1 NO NO
sFlow YES 1.0 1.0 NO
@@ -26,6 +26,9 @@ Post-v2.12.0
releases.
* OVS validated with DPDK 18.11.5, due to the inclusion of a fix for
CVE-2019-14818, this DPDK version is strongly recommended to be used.
+ - GTP-U Tunnel Protocol
+ * Add two new fields: tun_gtpu_flags, tun_gtpu_msgtype.
+ * Only support for userspace datapath.
v2.12.0 - 03 Sep 2019
---------------------
@@ -242,6 +242,7 @@ enum ovs_vport_type {
OVS_VPORT_TYPE_ERSPAN = 107, /* ERSPAN tunnel. */
OVS_VPORT_TYPE_IP6ERSPAN = 108, /* ERSPAN tunnel. */
OVS_VPORT_TYPE_IP6GRE = 109,
+ OVS_VPORT_TYPE_GTPU = 110,
__OVS_VPORT_TYPE_MAX
};
@@ -401,6 +402,7 @@ enum ovs_tunnel_key_attr {
OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */
OVS_TUNNEL_KEY_ATTR_PAD,
OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, /* struct erspan_metadata */
+ OVS_TUNNEL_KEY_ATTR_GTPU_OPTS, /* struct gtpu_metadata */
__OVS_TUNNEL_KEY_ATTR_MAX
};
@@ -121,6 +121,12 @@ void match_set_tun_erspan_dir_masked(struct match *match, uint8_t dir,
void match_set_tun_erspan_hwid(struct match *match, uint8_t hwid);
void match_set_tun_erspan_hwid_masked(struct match *match, uint8_t hwid,
uint8_t mask);
+void match_set_tun_gtpu_flags(struct match *match, uint8_t flags);
+void match_set_tun_gtpu_flags_masked(struct match *match, uint8_t flags,
+ uint8_t mask);
+void match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype);
+void match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t msgtype,
+ uint8_t mask);
void match_set_in_port(struct match *, ofp_port_t ofp_port);
void match_set_pkt_mark(struct match *, uint32_t pkt_mark);
void match_set_pkt_mark_masked(struct match *, uint32_t pkt_mark, uint32_t mask);
@@ -506,6 +506,34 @@ enum OVS_PACKED_ENUM mf_field_id {
*/
MFF_TUN_ERSPAN_HWID,
+ /* "tun_gtpu_flags".
+ *
+ * GTP-U tunnel flags.
+ *
+ * Type: u8.
+ * Maskable: bitwise.
+ * Formatting: decimal.
+ * Prerequisites: none.
+ * Access: read/write.
+ * NXM: none.
+ * OXM: NXOXM_ET_GTPU_FLAGS(15) since v2.13.
+ */
+ MFF_TUN_GTPU_FLAGS,
+
+ /* "tun_gtpu_msgtype".
+ *
+ * GTP-U tunnel message type.
+ *
+ * Type: u8.
+ * Maskable: bitwise.
+ * Formatting: decimal.
+ * Prerequisites: none.
+ * Access: read/write.
+ * NXM: none.
+ * OXM: NXOXM_ET_GTPU_MSGTYPE(16) since v2.13.
+ */
+ MFF_TUN_GTPU_MSGTYPE,
+
#if TUN_METADATA_NUM_OPTS == 64
/* "tun_metadata<N>".
*
@@ -43,7 +43,9 @@ struct flow_tnl {
uint32_t erspan_idx;
uint8_t erspan_dir;
uint8_t erspan_hwid;
- uint8_t pad1[6]; /* Pad to 64 bits. */
+ uint8_t gtpu_flags;
+ uint8_t gtpu_msgtype;
+ uint8_t pad1[4]; /* Pad to 64 bits. */
struct tun_metadata metadata;
};
@@ -111,6 +111,8 @@ vport_type_to_kind(enum ovs_vport_type type,
} else {
return NULL;
}
+ case OVS_VPORT_TYPE_GTPU:
+ return NULL;
case OVS_VPORT_TYPE_NETDEV:
case OVS_VPORT_TYPE_INTERNAL:
case OVS_VPORT_TYPE_LISP:
@@ -277,6 +279,7 @@ dpif_netlink_rtnl_verify(const struct netdev_tunnel_config *tnl_cfg,
case OVS_VPORT_TYPE_INTERNAL:
case OVS_VPORT_TYPE_LISP:
case OVS_VPORT_TYPE_STT:
+ case OVS_VPORT_TYPE_GTPU:
case OVS_VPORT_TYPE_UNSPEC:
case __OVS_VPORT_TYPE_MAX:
default:
@@ -358,6 +361,7 @@ dpif_netlink_rtnl_create(const struct netdev_tunnel_config *tnl_cfg,
case OVS_VPORT_TYPE_INTERNAL:
case OVS_VPORT_TYPE_LISP:
case OVS_VPORT_TYPE_STT:
+ case OVS_VPORT_TYPE_GTPU:
case OVS_VPORT_TYPE_UNSPEC:
case __OVS_VPORT_TYPE_MAX:
default:
@@ -471,6 +475,7 @@ dpif_netlink_rtnl_port_destroy(const char *name, const char *type)
case OVS_VPORT_TYPE_INTERNAL:
case OVS_VPORT_TYPE_LISP:
case OVS_VPORT_TYPE_STT:
+ case OVS_VPORT_TYPE_GTPU:
case OVS_VPORT_TYPE_UNSPEC:
case __OVS_VPORT_TYPE_MAX:
default:
@@ -703,6 +703,9 @@ get_vport_type(const struct dpif_netlink_vport *vport)
case OVS_VPORT_TYPE_IP6GRE:
return "ip6gre";
+ case OVS_VPORT_TYPE_GTPU:
+ return "gtpu";
+
case OVS_VPORT_TYPE_UNSPEC:
case __OVS_VPORT_TYPE_MAX:
break;
@@ -736,6 +739,8 @@ netdev_to_ovs_vport_type(const char *type)
return OVS_VPORT_TYPE_IP6GRE;
} else if (!strcmp(type, "gre")) {
return OVS_VPORT_TYPE_GRE;
+ } else if (!strcmp(type, "gtpu")) {
+ return OVS_VPORT_TYPE_GTPU;
} else {
return OVS_VPORT_TYPE_UNSPEC;
}
@@ -375,6 +375,34 @@ match_set_tun_erspan_hwid(struct match *match, uint8_t hwid)
}
void
+match_set_tun_gtpu_flags_masked(struct match *match, uint8_t flags,
+ uint8_t mask)
+{
+ match->wc.masks.tunnel.gtpu_flags = flags;
+ match->flow.tunnel.gtpu_flags = flags & mask;
+}
+
+void
+match_set_tun_gtpu_flags(struct match *match, uint8_t flags)
+{
+ match_set_tun_gtpu_flags_masked(match, flags, UINT8_MAX);
+}
+
+void
+match_set_tun_gtpu_msgtype_masked(struct match *match, uint8_t msgtype,
+ uint8_t mask)
+{
+ match->wc.masks.tunnel.gtpu_msgtype = msgtype;
+ match->flow.tunnel.gtpu_msgtype = msgtype & mask;
+}
+
+void
+match_set_tun_gtpu_msgtype(struct match *match, uint8_t msgtype)
+{
+ match_set_tun_gtpu_msgtype_masked(match, msgtype, UINT8_MAX);
+}
+
+void
match_set_in_port(struct match *match, ofp_port_t ofp_port)
{
match->wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
@@ -391,6 +391,10 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
case MFF_NSH_C3:
case MFF_NSH_C4:
return !wc->masks.nsh.context[mf->id - MFF_NSH_C1];
+ case MFF_TUN_GTPU_FLAGS:
+ return !wc->masks.tunnel.gtpu_flags;
+ case MFF_TUN_GTPU_MSGTYPE:
+ return !wc->masks.tunnel.gtpu_msgtype;
case MFF_N_IDS:
default:
@@ -530,6 +534,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
case MFF_TUN_ERSPAN_VER:
case MFF_TUN_ERSPAN_DIR:
case MFF_TUN_ERSPAN_HWID:
+ case MFF_TUN_GTPU_FLAGS:
+ case MFF_TUN_GTPU_MSGTYPE:
CASE_MFF_TUN_METADATA:
case MFF_METADATA:
case MFF_IN_PORT:
@@ -711,6 +717,12 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
case MFF_TUN_ERSPAN_HWID:
value->u8 = flow->tunnel.erspan_hwid;
break;
+ case MFF_TUN_GTPU_FLAGS:
+ value->u8 = flow->tunnel.gtpu_flags;
+ break;
+ case MFF_TUN_GTPU_MSGTYPE:
+ value->u8 = flow->tunnel.gtpu_msgtype;
+ break;
CASE_MFF_TUN_METADATA:
tun_metadata_read(&flow->tunnel, mf, value);
break;
@@ -1042,6 +1054,12 @@ mf_set_value(const struct mf_field *mf,
case MFF_TUN_ERSPAN_HWID:
match_set_tun_erspan_hwid(match, value->u8);
break;
+ case MFF_TUN_GTPU_FLAGS:
+ match_set_tun_gtpu_flags(match, value->u8);
+ break;
+ case MFF_TUN_GTPU_MSGTYPE:
+ match_set_tun_gtpu_msgtype(match, value->u8);
+ break;
CASE_MFF_TUN_METADATA:
tun_metadata_set_match(mf, value, NULL, match, err_str);
break;
@@ -1459,6 +1477,12 @@ mf_set_flow_value(const struct mf_field *mf,
case MFF_TUN_ERSPAN_HWID:
flow->tunnel.erspan_hwid = value->u8;
break;
+ case MFF_TUN_GTPU_FLAGS:
+ flow->tunnel.gtpu_flags = value->u8;
+ break;
+ case MFF_TUN_GTPU_MSGTYPE:
+ flow->tunnel.gtpu_msgtype = value->u8;
+ break;
CASE_MFF_TUN_METADATA:
tun_metadata_write(&flow->tunnel, mf, value);
break;
@@ -1780,6 +1804,8 @@ mf_is_pipeline_field(const struct mf_field *mf)
case MFF_TUN_ERSPAN_IDX:
case MFF_TUN_ERSPAN_DIR:
case MFF_TUN_ERSPAN_HWID:
+ case MFF_TUN_GTPU_FLAGS:
+ case MFF_TUN_GTPU_MSGTYPE:
CASE_MFF_TUN_METADATA:
case MFF_METADATA:
case MFF_IN_PORT:
@@ -1970,6 +1996,12 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
case MFF_TUN_ERSPAN_HWID:
match_set_tun_erspan_hwid_masked(match, 0, 0);
break;
+ case MFF_TUN_GTPU_FLAGS:
+ match_set_tun_gtpu_flags_masked(match, 0, 0);
+ break;
+ case MFF_TUN_GTPU_MSGTYPE:
+ match_set_tun_gtpu_msgtype_masked(match, 0, 0);
+ break;
CASE_MFF_TUN_METADATA:
tun_metadata_set_match(mf, NULL, NULL, match, err_str);
break;
@@ -2377,6 +2409,12 @@ mf_set(const struct mf_field *mf,
case MFF_TUN_ERSPAN_HWID:
match_set_tun_erspan_hwid_masked(match, value->u8, mask->u8);
break;
+ case MFF_TUN_GTPU_FLAGS:
+ match_set_tun_gtpu_flags_masked(match, value->u8, mask->u8);
+ break;
+ case MFF_TUN_GTPU_MSGTYPE:
+ match_set_tun_gtpu_msgtype_masked(match, value->u8, mask->u8);
+ break;
CASE_MFF_TUN_METADATA:
tun_metadata_set_match(mf, value, mask, match, err_str);
break;
@@ -1456,7 +1456,8 @@ ovs-ofctl add-flow br-int 'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
<li>LISP has a 24-bit instance ID.</li>
<li>GRE has an optional 32-bit key.</li>
<li>STT has a 64-bit key.</li>
- <li>ERSPAN has a 10-bit key (Session ID).</li>
+ <li>ERSPAN has a 10-bit key (Session ID).</li>
+ <li>GTPU has a 32-bit key (Tunnel Endpoint ID).</li>
</ul>
<p>
@@ -1797,6 +1798,78 @@ ovs-ofctl add-flow br-int 'in_port=3,tun_src=192.168.1.1,tun_id=5001 actions=1'
A 6-bit unique identifier of an ERSPAN v2 engine within a system.
</field>
+ <h2>GTP-U Metadata Fields</h2>
+
+ <p>
+ These fields provide access to set-up GPRS Tunnelling Protocol for
+ User Plane (GTPv1-U), based on 3GPP TS 29.281.
+ </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, used to determine the version of the
+ GTP-U protocol, which should be set to '1'.
+ </li>
+ <li><code>PT</code>:
+ Protocol 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, indicating the presence of a
+ meaningful value of the Next Extension Header field.
+ </li>
+ <li><code>S</code>:
+ Sequence number, indicating the presence of a
+ meaningful value of the Sequence Number field.
+ </li>
+ <li><code>PN</code>:
+ N-PDU Number, 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 Flags">
+ This field is a 8-bit flags representing GTP-U V, PT, E, S, and PN.
+ </field>
+
+ <field id="MFF_TUN_GTPU_MSGTYPE" title="GTP-U Message Type">
+ This field indicates whether it's a signalling message used for path
+ management, or a user plane message which carries the original packet.
+ The complete range of message types can be referred to [3GPP TS 29.281].
+ </field>
+
<h2>Geneve Fields</h2>
<p>
@@ -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;
@@ -708,6 +711,88 @@ netdev_erspan_build_header(const struct netdev *netdev,
}
struct dp_packet *
+netdev_gtpu_pop_header(struct dp_packet *packet)
+{
+ struct pkt_metadata *md = &packet->md;
+ struct flow_tnl *tnl = &md->tunnel;
+ struct gtpuhdr *gtph;
+ unsigned int hlen;
+
+ ovs_assert(packet->l3_ofs > 0);
+ ovs_assert(packet->l4_ofs > 0);
+
+ pkt_metadata_init_tnl(md);
+ if (GTPU_HLEN > dp_packet_l4_size(packet)) {
+ goto err;
+ }
+
+ gtph = udp_extract_tnl_md(packet, tnl, &hlen);
+ if (!gtph) {
+ goto err;
+ }
+
+ if (gtph->md.flags != GTPU_FLAGS_DEFAULT) {
+ VLOG_WARN_RL(&err_rl, "GTP-U not supported");
+ goto err;
+ }
+
+ tnl->gtpu_flags = gtph->md.flags;
+ tnl->gtpu_msgtype = gtph->md.msgtype;
+ tnl->tun_id = htonll(ntohl(get_16aligned_be32(>ph->teid)));
+
+ 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 {
+ VLOG_WARN_RL(&err_rl, "GTP-U: Receive non-IP packet");
+ }
+ } else {
+ /* non GPDU GTP-U messages, ex: echo request, end marker. */
+ packet->packet_type = htonl(PT_GTPU_MSG);
+ }
+
+ dp_packet_reset_packet(packet, hlen + GTPU_HLEN);
+
+ return packet;
+
+err:
+ dp_packet_delete(packet);
+ return NULL;
+}
+
+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;
+
+ ovs_mutex_lock(&dev->mutex);
+ tnl_cfg = &dev->tnl_cfg;
+ gtph = udp_build_header(tnl_cfg, data, params);
+ ovs_mutex_unlock(&dev->mutex);
+
+ /* Set to default if not set in flow. */
+ gtph->md.flags = params->flow->tunnel.gtpu_flags ? : GTPU_FLAGS_DEFAULT;
+ gtph->md.msgtype = params->flow->tunnel.gtpu_msgtype ? :
+ GTPU_MSGTYPE_GPDU;
+ 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_vxlan_pop_header(struct dp_packet *packet)
{
struct pkt_metadata *md = &packet->md;
@@ -52,6 +52,14 @@ netdev_erspan_push_header(const struct netdev *netdev,
struct dp_packet *
netdev_erspan_pop_header(struct dp_packet *packet);
+int
+netdev_gtpu_build_header(const struct netdev *netdev,
+ struct ovs_action_push_tnl *data,
+ const struct netdev_tnl_build_header_params *p);
+
+struct dp_packet *
+netdev_gtpu_pop_header(struct dp_packet *packet);
+
void
netdev_tnl_push_udp_header(const struct netdev *netdev,
struct dp_packet *packet,
@@ -111,7 +111,8 @@ netdev_vport_needs_dst_port(const struct netdev *dev)
return (class->get_config == get_tunnel_config &&
(!strcmp("geneve", type) || !strcmp("vxlan", type) ||
- !strcmp("lisp", type) || !strcmp("stt", type)) );
+ !strcmp("lisp", type) || !strcmp("stt", type) ||
+ !strcmp("gtpu", type)));
}
const char *
@@ -216,6 +217,8 @@ netdev_vport_construct(struct netdev *netdev_)
dev->tnl_cfg.dst_port = port ? htons(port) : htons(LISP_DST_PORT);
} else if (!strcmp(type, "stt")) {
dev->tnl_cfg.dst_port = port ? htons(port) : htons(STT_DST_PORT);
+ } else if (!strcmp(type, "gtpu")) {
+ dev->tnl_cfg.dst_port = port ? htons(port) : htons(GTPU_DST_PORT);
}
dev->tnl_cfg.dont_fragment = true;
@@ -433,6 +436,8 @@ tunnel_supported_layers(const char *type,
} else if (!strcmp(type, "vxlan")
&& tnl_cfg->exts & (1 << OVS_VXLAN_EXT_GPE)) {
return TNL_L2 | TNL_L3;
+ } else if (!strcmp(type, "gtpu")) {
+ return TNL_L3;
} else {
return TNL_L2;
}
@@ -589,6 +594,10 @@ set_tunnel_config(struct netdev *dev_, const struct smap *args, char **errp)
tnl_cfg.dst_port = htons(STT_DST_PORT);
}
+ if (!strcmp(type, "gtpu")) {
+ tnl_cfg.dst_port = htons(GTPU_DST_PORT);
+ }
+
needs_dst_port = netdev_vport_needs_dst_port(dev_);
tnl_cfg.dont_fragment = true;
@@ -907,7 +916,8 @@ get_tunnel_config(const struct netdev *dev, struct smap *args)
if ((!strcmp("geneve", type) && dst_port != GENEVE_DST_PORT) ||
(!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
(!strcmp("lisp", type) && dst_port != LISP_DST_PORT) ||
- (!strcmp("stt", type) && dst_port != STT_DST_PORT)) {
+ (!strcmp("stt", type) && dst_port != STT_DST_PORT) ||
+ (!strcmp("gtpu", type) && dst_port != GTPU_DST_PORT)) {
smap_add_format(args, "dst_port", "%d", dst_port);
}
}
@@ -1223,6 +1233,17 @@ netdev_vport_tunnel_register(void)
},
{{NULL, NULL, 0, 0}}
},
+ { "gtpu_sys",
+ {
+ TUNNEL_FUNCTIONS_COMMON,
+ .type = "gtpu",
+ .build_header = netdev_gtpu_build_header,
+ .push_header = netdev_tnl_push_udp_header,
+ .pop_header = netdev_gtpu_pop_header,
+ },
+ {{NULL, NULL, 0, 0}}
+ },
+
};
static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
@@ -1191,6 +1191,12 @@ nx_put_raw(struct ofpbuf *b, enum ofp_version oxm, const struct match *match,
nxm_put_8m(&ctx, MFF_TUN_ERSPAN_HWID, oxm,
flow->tunnel.erspan_hwid, match->wc.masks.tunnel.erspan_hwid);
+ /* GTP-U */
+ nxm_put_8m(&ctx, MFF_TUN_GTPU_FLAGS, oxm, flow->tunnel.gtpu_flags,
+ match->wc.masks.tunnel.gtpu_flags);
+ nxm_put_8m(&ctx, MFF_TUN_GTPU_MSGTYPE, oxm, flow->tunnel.gtpu_msgtype,
+ match->wc.masks.tunnel.gtpu_msgtype);
+
/* Network Service Header */
nxm_put_8m(&ctx, MFF_NSH_FLAGS, oxm, flow->nsh.flags,
match->wc.masks.nsh.flags);
@@ -755,7 +755,17 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
} else {
VLOG_WARN("%s Invalid ERSPAN version %d\n", __func__, ersh->ver);
}
+ } else if (data->tnl_type == OVS_VPORT_TYPE_GTPU) {
+ const struct gtpuhdr *gtph;
+
+ gtph = format_udp_tnl_push_header(ds, udp);
+
+ ds_put_format(ds, "gtpu(flags=0x%"PRIx8
+ ",msgtype=0x%"PRIx8",teid=0x%"PRIx32")",
+ gtph->md.flags, gtph->md.msgtype,
+ ntohl(get_16aligned_be32(>ph->teid)));
}
+
ds_put_format(ds, ")");
}
@@ -1496,6 +1506,8 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
void *l3, *l4;
int n = 0;
uint8_t hwid, dir;
+ uint32_t teid;
+ uint8_t gtpu_flags, gtpu_msgtype;
if (!ovs_scan_len(s, &n, "tnl_push(tnl_port(%"SCNi32"),", &data->tnl_port)) {
return -EINVAL;
@@ -1725,6 +1737,18 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
header_len = sizeof *eth + ip_len + ERSPAN_GREHDR_LEN +
sizeof *ersh + ERSPAN_V2_MDSIZE;
+
+ } else if (ovs_scan_len(s, &n, "gtpu(flags=0x%"SCNx8",msgtype=0x%"
+ SCNx8",teid=0x%"SCNx32"))",
+ >pu_flags, >pu_msgtype, &teid)) {
+ struct gtpuhdr *gtph = (struct gtpuhdr *) (udp + 1);
+
+ gtph->md.flags = gtpu_flags;
+ gtph->md.msgtype = gtpu_msgtype;
+ put_16aligned_be32(>ph->teid, htonl(teid));
+ tnl_type = OVS_VPORT_TYPE_GTPU;
+ header_len = sizeof *eth + ip_len +
+ sizeof *udp + sizeof *gtph;
} else {
return -EINVAL;
}
@@ -2625,6 +2649,7 @@ static const struct attr_len_tbl ovs_tun_key_attr_lens[OVS_TUNNEL_KEY_ATTR_MAX +
[OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = 16 },
[OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = 16 },
[OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS] = { .len = ATTR_LEN_VARIABLE },
+ [OVS_TUNNEL_KEY_ATTR_GTPU_OPTS] = { .len = ATTR_LEN_VARIABLE },
};
const struct attr_len_tbl ovs_flow_key_attr_lens[OVS_KEY_ATTR_MAX + 1] = {
@@ -3030,6 +3055,13 @@ odp_tun_key_from_attr__(const struct nlattr *attr, bool is_mask,
}
break;
}
+ case OVS_TUNNEL_KEY_ATTR_GTPU_OPTS: {
+ const struct gtpu_metadata *opts = nl_attr_get(a);
+
+ tun->gtpu_flags = opts->flags;
+ tun->gtpu_msgtype = opts->msgtype;
+ break;
+ }
default:
/* Allow this to show up as unexpected, if there are unknown
@@ -3640,6 +3672,22 @@ format_odp_tun_erspan_opt(const struct nlattr *attr,
ds_chomp(ds, ',');
}
+static void
+format_odp_tun_gtpu_opt(const struct nlattr *attr,
+ const struct nlattr *mask_attr, struct ds *ds,
+ bool verbose)
+{
+ const struct gtpu_metadata *opts, *mask;
+
+ opts = nl_attr_get(attr);
+ mask = mask_attr ? nl_attr_get(mask_attr) : NULL;
+
+ format_u8x(ds, "flags", opts->flags, mask ? &mask->flags : NULL, verbose);
+ format_u8x(ds, "msgtype", opts->msgtype, mask ? &mask->msgtype : NULL,
+ verbose);
+ ds_chomp(ds, ',');
+}
+
#define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
static void
@@ -3892,6 +3940,11 @@ format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
format_odp_tun_erspan_opt(a, ma, ds, verbose);
ds_put_cstr(ds, "),");
break;
+ case OVS_TUNNEL_KEY_ATTR_GTPU_OPTS:
+ ds_put_cstr(ds, "gtpu(");
+ format_odp_tun_gtpu_opt(a, ma, ds, verbose);
+ ds_put_cstr(ds, ")");
+ break;
case __OVS_TUNNEL_KEY_ATTR_MAX:
default:
format_unknown_key(ds, a, ma);
@@ -5100,6 +5153,50 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *mask)
}
static int
+scan_gtpu_metadata(const char *s,
+ struct gtpu_metadata *key,
+ struct gtpu_metadata *mask)
+{
+ const char *s_base = s;
+ uint8_t flags, flags_ma;
+ uint8_t msgtype, msgtype_ma;
+ int len;
+
+ if (!strncmp(s, "flags=", 6)) {
+ s += 6;
+ len = scan_u8(s, &flags, mask ? &flags_ma : NULL);
+ if (len == 0) {
+ return 0;
+ }
+ s += len;
+ }
+
+ if (s[0] == ',') {
+ s++;
+ }
+
+ if (!strncmp(s, "msgtype=", 8)) {
+ s += 8;
+ len = scan_u8(s, &msgtype, mask ? &msgtype_ma : NULL);
+ if (len == 0) {
+ return 0;
+ }
+ s += len;
+ }
+
+ if (!strncmp(s, ")", 1)) {
+ s += 1;
+ key->flags = flags;
+ key->msgtype = msgtype;
+ if (mask) {
+ mask->flags = flags_ma;
+ mask->msgtype = msgtype_ma;
+ }
+ }
+ return s - s_base;
+}
+
+static int
scan_erspan_metadata(const char *s,
struct erspan_metadata *key,
struct erspan_metadata *mask)
@@ -5339,6 +5436,15 @@ erspan_to_attr(struct ofpbuf *a, const void *data_)
sizeof *md);
}
+static void
+gtpu_to_attr(struct ofpbuf *a, const void *data_)
+{
+ const struct gtpu_metadata *md = data_;
+
+ nl_msg_put_unspec(a, OVS_TUNNEL_KEY_ATTR_GTPU_OPTS, md,
+ sizeof *md);
+}
+
#define SCAN_PUT_ATTR(BUF, ATTR, DATA, FUNC) \
{ \
unsigned long call_fn = (unsigned long)FUNC; \
@@ -5725,6 +5831,8 @@ parse_odp_key_mask_attr__(struct parse_odp_context *context, const char *s,
SCAN_FIELD_NESTED_FUNC("vxlan(gbp(", uint32_t, vxlan_gbp, vxlan_gbp_to_attr);
SCAN_FIELD_NESTED_FUNC("geneve(", struct geneve_scan, geneve,
geneve_to_attr);
+ SCAN_FIELD_NESTED_FUNC("gtpu(", struct gtpu_metadata, gtpu_metadata,
+ gtpu_to_attr);
SCAN_FIELD_NESTED_FUNC("flags(", uint16_t, tun_flags, tun_flags_to_attr);
} SCAN_END_NESTED();
@@ -1447,6 +1447,64 @@ static inline ovs_be32 get_erspan_ts(enum erspan_ts_gra gra)
return ts;
}
+/*
+ * GTP-U protocol header and metadata
+ * See:
+ * User Plane Protocol and Architectural Analysis on 3GPP 5G System
+ * draft-hmm-dmm-5g-uplane-analysis-00
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Ver |P|R|E|S|N| Message Type| Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Tunnel Endpoint Identifier |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number | N-PDU Number | Next-Ext-Hdr |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * GTP-U Flags:
+ * P: Protocol Type (Set to '1')
+ * R: Reserved Bit (Set to '0')
+ * E: Extension Header Flag (Set to '1' if extension header exists)
+ * S: Sequence Number Flag (Set to '1' if sequence number exists)
+ * N: N-PDU Number Flag (Set to '1' if N-PDU number exists)
+ *
+ * GTP-U Message Type:
+ * Indicates the type of GTP-U message.
+ *
+ * GTP-U Length:
+ * Indicates the length in octets of the payload.
+ *
+ * User payload is transmitted in G-PDU packets.
+ */
+
+#define GTPU_VER_MASK 0xe0
+#define GTPU_P_MASK 0x10
+#define GTPU_E_MASK 0x04
+
+/* GTP-U UDP port. */
+#define GTPU_DST_PORT 2152
+
+/* Default GTP-U flags: Ver = 1 and P = 1. */
+#define GTPU_FLAGS_DEFAULT 0x30
+
+/* GTP-U message type for normal user plane PDU. */
+#define GTPU_MSGTYPE_REQ 1 /* Echo Request. */
+#define GTPU_MSGTYPE_REPL 2 /* Echo Reply. */
+#define GTPU_MSGTYPE_GPDU 255 /* User Payload. */
+
+struct gtpu_metadata {
+ uint8_t flags;
+ uint8_t msgtype;
+};
+
+struct gtpuhdr {
+ struct gtpu_metadata md;
+ ovs_be16 len;
+ ovs_16aligned_be32 teid;
+};
+
/* VXLAN protocol header */
struct vxlanhdr {
union {
@@ -1543,6 +1601,7 @@ 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),
+ PT_GTPU_MSG = PACKET_TYPE(OFPHTN_UDP_TCP_PORT, GTPU_DST_PORT),
PT_UNKNOWN = PACKET_TYPE(0xffff, 0xffff), /* Unknown packet type. */
};
@@ -178,6 +178,9 @@ tnl_type_to_nw_proto(const char type[])
if (!strcmp(type, "vxlan")) {
return IPPROTO_UDP;
}
+ if (!strcmp(type, "gtpu")) {
+ return IPPROTO_UDP;
+ }
return 0;
}
@@ -3535,6 +3535,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:
@@ -7573,7 +7574,8 @@ xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
}
if (flow->packet_type != htonl(PT_ETH) && in_port &&
- in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0) {
+ in_port->pt_mode == NETDEV_PT_LEGACY_L3 && ctx.table_id == 0 &&
+ flow->packet_type != htonl(PT_GTPU_MSG)) {
/* 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.
* The dl_type has already been set from the packet_type. */
@@ -585,7 +585,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;
@@ -2406,7 +2406,7 @@ head_table () {
instructions: meter,apply_actions,clear_actions,write_actions,write_metadata,goto_table
Write-Actions and Apply-Actions features:
actions: output group set_field strip_vlan push_vlan mod_nw_ttl dec_ttl set_mpls_ttl dec_mpls_ttl push_mpls pop_mpls set_queue
- supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver tun_erspan_dir tun_erspan_hwid tun_metadata0 dnl
+ supported on Set-Field: tun_id tun_src tun_dst tun_ipv6_src tun_ipv6_dst tun_flags tun_gbp_id tun_gbp_flags tun_erspan_idx tun_erspan_ver tun_erspan_dir tun_erspan_hwid tun_gtpu_flags tun_gtpu_msgtype tun_metadata0 dnl
tun_metadata1 tun_metadata2 tun_metadata3 tun_metadata4 tun_metadata5 tun_metadata6 tun_metadata7 tun_metadata8 tun_metadata9 tun_metadata10 tun_metadata11 tun_metadata12 tun_metadata13 tun_metadata14 tun_metadata15 tun_metadata16 tun_metadata17 tun_metadata18 tun_metadata19 tun_metadata20 tun_metadata21 tun_metadata22 tun_metadata23 tun_metadata24 tun_metadata25 tun_metadata26 tun_metadata27 tun_metadata28 tun_metadata29 tun_metadata30 tun_metadata31 tun_metadata32 tun_metadata33 tun_metadata34 tun_metadata35 tun_metadata36 tun_metadata37 tun_metadata38 tun_metadata39 tun_metadata40 tun_metadata41 tun_metadata42 tun_metadata43 tun_metadata44 tun_metadata45 tun_metadata46 tun_metadata47 tun_metadata48 tun_metadata49 tun_metadata50 tun_metadata51 tun_metadata52 tun_metadata53 tun_metadata54 tun_metadata55 tun_metadata56 tun_metadata57 tun_metadata58 tun_metadata59 tun_metadata60 tun_metadata61 tun_metadata62 tun_metadata63 dnl
metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 reg9 reg10 reg11 reg12 reg13 reg14 reg15 xreg0 xreg1 xreg2 xreg3 xreg4 xreg5 xreg6 xreg7 xxreg0 xxreg1 xxreg2 xxreg3 eth_src eth_dst vlan_tci vlan_vid vlan_pcp mpls_label mpls_tc mpls_ttl ip_src ip_dst ipv6_src ipv6_dst ipv6_label nw_tos ip_dscp nw_ecn nw_ttl arp_op arp_spa arp_tpa arp_sha arp_tha tcp_src tcp_dst udp_src udp_dst sctp_src sctp_dst icmp_type icmp_code icmpv6_type icmpv6_code nd_target nd_sll nd_tll nd_reserved nd_options_type nsh_flags nsh_spi nsh_si nsh_c1 nsh_c2 nsh_c3 nsh_c4 nsh_ttl
matching:
@@ -2426,6 +2426,8 @@ metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
tun_erspan_ver: arbitrary mask
tun_erspan_dir: arbitrary mask
tun_erspan_hwid: arbitrary mask
+ tun_gtpu_flags: arbitrary mask
+ tun_gtpu_msgtype: arbitrary mask
tun_metadata0: arbitrary mask
tun_metadata1: arbitrary mask
tun_metadata2: arbitrary mask
@@ -216,6 +216,8 @@ AT_CHECK([ovs-vsctl add-port int-br t2 -- set Interface t2 type=vxlan \
options:remote_ip=1.1.2.92 options:key=456 options:packet_type=legacy_l3 ofport_request=7\
-- add-port int-br t7 -- set Interface t7 type=vxlan \
options:remote_ip=1.1.2.92 options:key=345 options:exts=gpe ofport_request=8\
+ -- add-port int-br t8 -- set Interface t8 type=gtpu \
+ options:remote_ip=1.1.2.92 options:key=123 ofport_request=9\
], [0])
AT_CHECK([ovs-appctl dpif/show], [0], [dnl
@@ -232,6 +234,7 @@ dummy@ovs-dummy: hit:0 missed:0
t5 6/6081: (geneve: egress_pkt_mark=1234, out_key=flow, remote_ip=1.1.2.93)
t6 7/3: (gre: key=456, packet_type=legacy_l3, remote_ip=1.1.2.92)
t7 8/4789: (vxlan: key=345, remote_ip=1.1.2.92)
+ t8 9/2152: (gtpu: key=123, remote_ip=1.1.2.92)
])
dnl First setup dummy interface IP address, then add the route
@@ -342,6 +345,7 @@ AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
Listening ports:
genev_sys_6081 (6081) ref_cnt=2
gre_sys (3) ref_cnt=2
+gtpu_sys_2152 (2152) ref_cnt=1
vxlan_sys_4789 (4789) ref_cnt=3
])
@@ -369,6 +373,13 @@ AT_CHECK([tail -1 stdout], [0],
[Datapath actions: tnl_pop(6081)
])
+dnl Check GTP-U tunnel pop
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.2.92,dst=1.1.2.88,proto=17,tos=0,ttl=64,frag=no),udp(src=51283,dst=2152)'],
+[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: tnl_pop(2152)
+])
+
dnl Check VXLAN tunnel push
AT_CHECK([ovs-ofctl add-flow int-br action=2])
AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'], [0], [stdout])
@@ -426,6 +437,15 @@ AT_CHECK([tail -1 stdout], [0],
[Datapath actions: clone(tnl_push(tnl_port(6081),header(size=58,type=5,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=6081,csum=0x0),geneve(crit,vni=0x7b,options({class=0xffff,type=0x80,len=4,0xa}))),out_port(100)),1)
])
+dnl Check GTP-U tunnel push
+AT_CHECK([ovs-ofctl add-flow int-br "actions=set_field:0x30->tun_gtpu_flags,set_field:0xff->tun_gtpu_msgtype,9"])
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(2),eth(src=f8:bc:12:44:34:b6,dst=aa:55:aa:55:00:00),eth_type(0x0800),ipv4(src=1.1.3.88,dst=1.1.3.112,proto=47,tos=0,ttl=64,frag=no)'],
+[0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: pop_eth,clone(tnl_push(tnl_port(2152),header(size=50,type=110,eth(dst=f8:bc:12:44:34:b6,src=aa:55:aa:55:00:00,dl_type=0x0800),ipv4(src=1.1.2.88,dst=1.1.2.92,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=2152,csum=0x0),gtpu(flags=0x30,msgtype=0xff,teid=0x7b)),out_port(100)),1)
+])
+AT_CHECK([ovs-ofctl del-flows int-br])
+
dnl Check decapsulation of GRE packet
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
AT_CHECK([ovs-appctl netdev-dummy/receive p0 'aa55aa550000001b213cab6408004500007e79464000402fba550101025c0101025820006558000001c8fe71d883724fbeb6f4e1494a080045000054ba200000400184861e0000011e00000200004227e75400030af3195500000000f265010000000000101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f3031323334353637'])
@@ -494,6 +514,7 @@ AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
Listening ports:
genev_sys_6081 (6081) ref_cnt=1
gre_sys (3) ref_cnt=1
+gtpu_sys_2152 (2152) ref_cnt=1
vxlan_sys_4789 (4789) ref_cnt=2
vxlan_sys_4790 (4790) ref_cnt=1
])
@@ -503,6 +524,7 @@ AT_CHECK([ovs-vsctl del-port int-br t1 \
-- del-port int-br t4 \
-- del-port int-br t6 \
-- del-port int-br t7 \
+ -- del-port int-br t8 \
], [0])
dnl Check tunnel lookup entries after deleting all remaining tunnel ports
@@ -1029,3 +1029,81 @@ AT_CHECK([ovs-appctl tnl/neigh/show | tail -n+3 | sort], [0], [dnl
OVS_VSWITCHD_STOP
AT_CLEANUP
+
+AT_SETUP([tunnel - GTP-U basic])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=gtpu \
+ options:remote_ip=1.1.1.1 \
+ options:key=123 ofport_request=1])
+
+AT_CHECK([ovs-appctl dpif/show | tail -n +3], [0], [dnl
+ br0 65534/100: (dummy-internal)
+ p1 1/2152: (gtpu: key=123, remote_ip=1.1.1.1)
+])
+
+AT_CHECK([ovs-appctl tnl/ports/show |sort], [0], [dnl
+Listening ports:
+gtpu_sys_2152 (2152) ref_cnt=1
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
+AT_SETUP([tunnel - GTP-U and GRE])
+OVS_VSWITCHD_START([add-port br0 p1 -- set Interface p1 type=dummy \
+ ofport_request=1 \
+ -- add-port br0 p2 -- set Interface p2 type=dummy \
+ ofport_request=2])
+
+# Add these ports separately to ensure that they get the datapath port
+# number expected below.
+ovs-vsctl -- add-port br0 p3 \
+ -- set Interface p3 type=gtpu \
+ ofport_request=3 \
+ options:remote_ip=1.1.1.1 \
+ options:key=3 \
+ options:packet_type=legacy_l3
+ovs-vsctl -- add-port br0 p4 \
+ -- set Interface p4 type=gtpu \
+ ofport_request=4 \
+ options:remote_ip=1.1.1.2 \
+ options:key=4 \
+ options:packet_type=legacy_l3
+ovs-vsctl -- add-port br0 l3gre \
+ -- set Interface l3gre type=gre \
+ ofport_request=5 \
+ options:remote_ip=1.1.1.2 \
+ options:packet_type=legacy_l3
+OVS_VSWITCHD_DISABLE_TUNNEL_PUSH_POP
+
+dnl AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+AT_CHECK([ovs-appctl dpif/show | tail -n +4], [0], [dnl
+ l3gre 5/3: (gre: packet_type=legacy_l3, remote_ip=1.1.1.2)
+ p1 1/1: (dummy)
+ p2 2/2: (dummy)
+ p3 3/2152: (gtpu: key=3, remote_ip=1.1.1.1)
+ p4 4/2152: (gtpu: key=4, remote_ip=1.1.1.2)
+])
+
+AT_DATA([flows.txt], [dnl
+in_port=1,actions=3
+in_port=2,actions=4
+in_port=3,tun_gtpu_flags=0x30,tun_gtpu_msgtype=0xff,actions=5
+in_port=4,tun_gtpu_flags=0x30,tun_gtpu_msgtype=0xff,actions=2
+])
+AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
+
+dnl Encap: in_port=1,actions=3
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'in_port(1),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0,ttl=128,frag=no),icmp(type=8,code=0)'], [0], [stdout])
+AT_CHECK([tail -1 stdout], [0],
+ [Datapath actions: set(tunnel(tun_id=0x3,dst=1.1.1.1,ttl=64,tp_dst=2152,flags(df|key))),pop_eth,2152
+])
+
+dnl receive packet from GTP-U port, match it, and output to layer3 GRE
+AT_CHECK([ovs-appctl ofproto/trace ovs-dummy 'recirc_id(0),tunnel(tun_id=0x3,src=1.1.1.1,dst=2.2.2.2,ttl=64,gtpu(flags=0x30,msgtype=0xff),flags(df|key)),in_port(2152),packet_type(ns=3,id=2152),skb_mark(0),ipv4(frag=no)'], [0], [stdout])
+AT_CHECK([tail -2 stdout], [0],
+ [Megaflow: recirc_id=0,packet_type=(3,0x868),ip,tun_id=0x3,tun_src=1.1.1.1,tun_dst=2.2.2.2,tun_tos=0,tun_flags=+df-csum+key,in_port=3
+Datapath actions: set(tunnel(dst=1.1.1.2,ttl=64,flags(df))),3
+])
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
@@ -2615,6 +2615,24 @@
<dd>
A pair of virtual devices that act as a patch cable.
</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>
+
</dl>
</column>
</group>