GTP Parsing Example
This example demonstrates how to add a simple flex parser node to the existing hardware defined parse graph.

This example starts with some basic definitions.
#include <doca_model.p4>
#include <doca_headers.p4>
#include <doca_externs.p4>
#include <doca_parser.p4>
const
bit<32
> WIRE_PORT = 32w00;
const
bit<32
> GTP_VPORT = 32w01;
const
bit<32
> DEFAULT_VPORT = 32w04;
struct metadata_t {
}
#define GTP_U_PORT 2152
Then we define the GTP-U version 1 header.
header Gtp_v1_h {
bit<3
> version; /** For GTPv1, this has a value of 1. */
bit protocol_type; /** GTP (value 1) from GTP' (value 0) */
bit reserved;
bit extension_header_flag; /** extension header optional field. */
bit seq_number_flag; /** Sequence Number optional field */
bit n_pdu_number_flag; /** N-PDU number optional field */
bit<8
> message_type; /** types of messages are defined in 3GPP TS 29.060 section 7.1 */
bit<16
> message_length; /** length of the payload in bytes */
bit<32
> teid; /** Tunnel endpoint identifier */
bit<16
> sequence_number; /** optional */
bit<8
> n_pdu_number; /** optional */
bit<8
> next_extension_hdr_type; /** optional if any of the E, S, or PN bits are on. The field must be interpreted only if the E bit is on */
}
Then we add NV_FIXED_HEADERS
to the headers struct, along with the new GTP header.
struct headers_t {
NV_FIXED_HEADERS
Gtp_v1_h gtpv1;
}
Using the nv_transition_from
annotation, the GTP parser state is connected as a select transition from the UDP state.
parser packet_parser(packet_in packet, out headers_t headers) {
NV_FIXED_PARSER(packet, headers)
@nv_transition_from
("nv_parse_udp"
, GTP_U_PORT)
state parse_gtp
{
packet.extract(headers.gtpv1);
transition accept;
}
}
The control example uses a single flow table that matches on input port and GTP tunnel endpoint ID. The policy is then to forward the GTP packet to a port or drop the packet.
/**
* This control admits GTP packets only if the tunnel ID matches
*
*/
control gtp_tunnel(
inout headers_t headers,
in nv_standard_metadata_t std_meta,
inout metadata_t user_meta,
inout nv_empty_metadata_t pkt_out_meta
) {
NvDirectCounter(NvCounterType.PACKETS_AND_BYTES) gtp_counter;
action send_to_port_gtp_table(nv_logical_port_t port) {
gtp_counter.count();
nv_set_l4_src_port(headers, 1234
);
nv_send_to_port(port);
}
action drop_gtp_table() {
gtp_counter.count();
nv_drop();
}
table gtp_table {
key = {
std_meta.ingress_port: exact;
headers.gtpv1.teid: exact;
}
actions = {
send_to_port_gtp_table;
drop_gtp_table;
NoAction;
}
default_action = NoAction;
direct_counter = gtp_counter;
}
apply {
if
(headers.gtpv1.isValid()) {
if
(gtp_table.apply().miss) {
nv_send_to_port(DEFAULT_VPORT);
}
}
drop();
}
}
NvDocaPipeline(
packet_parser(),
gtp_tunnel()
) main;
Example P4Runtime shell entries:
te_tunnel_1 = table_entry["gtp_tunnel.gtp_table"
](action="gtp_tunnel.send_to_port_gtp_table"
)
te_tunnel_1.match["std_meta.ingress_port"
] = "0"
te_tunnel_1.match["headers.gtpv1.teid"
] = "0x000000001"
te_tunnel_1.action["port"
] = "1"
te_tunnel_1.insert()
te_tunnel_2 = table_entry["gtp_tunnel.gtp_table"
](action="gtp_tunnel.send_to_port_gtp_table"
)
te_tunnel_2.match["std_meta.ingress_port"
] = "1"
te_tunnel_2.match["headers.gtpv1.teid"
] = "0x000000001"
te_tunnel_2.action["port"
] = "0"
te_tunnel_2.insert()
te_tunnel_3 = table_entry["gtp_tunnel.gtp_table"
](action="gtp_tunnel.drop_gtp_table"
)
te_tunnel_3.match["std_meta.ingress_port"
] = "4"
te_tunnel_3.match["headers.gtpv1.teid"
] = "0x000000001"
te_tunnel_3.insert()
te_tunnel_1.read(lambda te_tunnel_1: print(te_tunnel_1))
te_tunnel_2.read(lambda te_tunnel_2: print(te_tunnel_2))
te_tunnel_3.read(lambda te_tunnel_3: print(te_tunnel_3))
Example Scapy packets:
from scapy.contrib.gtp import
GTP_U_Header
tunnel = Ether(src=""
3C:6D:66
:11
:11
:11
",dst="
ff:ff:ff:ff:ff:ff")/IP(src="
100.100
.100.1
", dst="
100.100
.100.81
")/UDP(sport=2152
, dport=2152
)
p_l3 = IP(dst='1.2.3.4'
, src='4.4.4.4'
) / TCP(sport=100
,dport=100
,seq=1001
,flags='S'
)
gtp = tunnel / GTP_U_Header(teid=1
)/ p_l3
gtp2 = tunnel / GTP_U_Header(teid=2
)/ p_l3
sendp(gtp, iface="p1"
)
sendp(gtp, iface="enp3s0f0s0"
)
sendp(gtp, iface="enp3s0f0s3"
)
sendp(gtp2, iface="p1"
)
Expected results:
Packet gtp is sent on port 0 and is received on port 1 with UDP src port of 1234
Packet gtp is sent on port 1 and is received on port 0 with UDP src port of 1234
Packet gtp is sent on port 4 and is dropped
Packet gtp2 is sent on port 0 and is received on port 4 unmodified
For more information, refer to the full DPL example, gtp_parsing.p4.