What can I help you with?
DOCA Documentation v3.0.0

GTP Parsing Example

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

DPU_Flex_Parser_GTP-version-1-modificationdate-1745264857730-api-v2.png

This example starts with some basic definitions.

Copy
Copied!
            

#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.

Copy
Copied!
            

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.

Copy
Copied!
            

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.

Copy
Copied!
            

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.

Copy
Copied!
            

/** * 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:

Copy
Copied!
            

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:

Copy
Copied!
            

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

Info

For more information, refer to the full DPL example, gtp_parsing.p4.

© Copyright 2025, NVIDIA. Last updated on May 5, 2025.