P4 Runtime Controller
DPL applications are deployed to the NVIDIA® BlueField® networking platform (DPU or SuperNIC) via the P4Runtime API. Since DPL is derived from the P4-16 language, it is compatible with the P4Runtime specification, enabling standard runtime interaction with the compiled DPL pipeline
The P4 Runtime shell (p4runtime_sh) is a Python-based CLI tool used to interact with this API. It is primarily used for loading DPL programs, testing match-action tables, and debugging pipeline behavior.
Loading simple DPL programs
Testing match-action tables
Debugging pipeline behavior
The shell can be invoked using the launch script provided in the DPL Development container.
For detailed instructions, refer to "Loading DPL Applications" documentation.
These commands allow you to view the high-level structure of your loaded P4 program.
Retrieves the content of
p4info.txtfor the currently loaded DPL program:p4info
Lists all available tables:
tables
Displays information about a specific table:
tables[
"<P4_TABLE_NAME>"]Lists all defined actions:
actions
Shows parameters and sizes for a specific action:
actions[
"<P4_ACTION_NAME>"]
Specifying Match Keys
When creating or modifying entries, you must define the match keys. The syntax depends on the match type defined in your DPL program.
Match Method | Syntax for Specifying Match Key Value | Note |
Exact |
| Exact value match. |
Ternary |
| The mask is provided in the match line, separated by |
LPM |
| The prefix length is provided in the match line, separated by |
Range |
|
The values are are separated by
|
Adding Entries
To add a standard entry, you must define the table, the action, the match keys, and the action parameters.
Regular entry:
te
=table_entry["<P4_TABLE_NAME>"](action="<P4_ACTION_NAME>") te.match["<MATCH_KEY_NAME>"]="<MATCH_VALUE>"te.action["<PARAMETER_NAME>"]="<PARAMETER_VALUE>"te.insert()Entry with idle timeout:
te
=table_entry["<P4_TABLE_NAME>"](action="<P4_ACTION_NAME>") te.match["<MATCH_KEY_NAME>"]="<MATCH_VALUE>"te.action["<PARAMETER_NAME>"]="<PARAMETER_VALUE>"te.idle_timeout_ns=3000000000# Example: 3 secondste.insert()
Reading Entries
Read a specific entry:
te
=table_entry["<P4_TABLE_NAME>"] te.match["<MATCH_KEY_NAME>"]="<MATCH_VALUE>"te.read(lambdate:print(te))Read all entries in a table:
te
=table_entry["<P4_TABLE_NAME>"] te.is_default=Falsete.read(lambdate:print(te))Read time since last hit:
NoteOnly for tables with
idle-timeoutenabled.te = table_entry[
"<P4_TABLE_NAME>"] te.time_since_last_hit.elapsed_ns =0# Enable reading timestamp te.match["<MATCH_KEY_NAME>"] ="<MATCH_VALUE>"te.read(lambda te: print(te))
Deleting Entries
Delete a specific entry:
te
=table_entry["<P4_TABLE_NAME>"] te.match["<MATCH_KEY_NAME>"]="<MATCH_VALUE>"te.delete()Delete all entries in a table:
te
=table_entry["<P4_TABLE_NAME>"] te.read(lambdate: te.delete())
Modifying Entries
You can modify specific action parameters of an existing table entry without removing it, provided those parameters are explicitly marked as modifiable in the P4 program.
Modification is supported only for action parameters annotated with @nv_modifiable. Refer to DOCA Target Architecture for more details.
Match keys, Action IDs, and non-annotated parameters cannot be changed in place. To modify these fields, you must delete the existing entry and insert a new one.
The following example demonstrates how to update a modifiable parameter:
# Select the table and action
te = table_entry["<P4_TABLE_NAME>"](action="<P4_ACTION_NAME>")
# Define the match keys to identify the specific entry
te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>"
# Update the specific parameter (Must be annotated with @nv_modifiable)
te.action["<PARAMETER_NAME>"] = "<NEW_VALUE>"
# Commit the modification
te.modify()
Managing Default Entries
Default entries are implicit. They cannot be inserted or deleted, only modified or read.
Reading a default entry:
te
=table_entry["<P4_TABLE_NAME>"] te.is_default=Truete.read(lambdate:print(te))Reading with counter:
te = table_entry[
"<P4_TABLE_NAME>"] te.is_default = True te.counter_data.byte_count =0# Request counter data te.read(lambda te: print(te))Modifying a default entry: Modification follows the same rules as standard entries. You can only update action parameters annotated with
@nv_modifiable.te
=table_entry["<P4_TABLE_NAME>"](action="<P4_ACTION_NAME>") te.is_default=Truete.action["<@nv_modifiable PARAMETER_NAME>"]="<PARAMETER_VALUE>"te.modify()
Direct Counters
Direct counters are attached to specific table entries.
List counters:
direct_countersRead specific entry counter:
ce
=DirectCounterEntry("<P4_DIRECT_COUNTER_NAME>") ce.table_entry.match["<KEY_NAME>"]="<VALUE>"ce.read(lambdace:print(ce))Read default entry counter: Set
ce.table_entry.is_default = Truebefore reading.Clear specific counter: Set
ce.packet_count = 0and callce.modify().Clear all counters:
fordcindirect_counters: ce=DirectCounterEntry(dc.name) ce.packet_count=0ce.modify()
Indirect Counters
Indirect counters are accessed by index.
List counters:
countersRead specific index:
ce
=counter_entry["<P4_INDIRECT_COUNTER_NAME>"] ce.index=<INDEX> ce.read(lambdace:print(ce))Clear specific index: Set
ce.byte_count = 0andce.packet_count = 0, then callce.modify().
Direct Meters
List meters:
direct_metersRead specific meter:
me
=DirectMeterEntry("<P4_DIRECT_METER_NAME>") me.table_entry.match["<KEY>"]="<VALUE>"me.read(lambdame:print(me))Configure meter: Set config values (
cir,cburst,pir,pburst) and callme.modify().
Indirect Meters
List meters:
metersRead specific index:
me
=meter_entry["<P4_INDIRECT_METER_NAME>"] me.index=<INDEX> me.read(lambdame:print(me))Clear meter: Set
me.cir = 0(and other rates) and callme.modify().
Packet In (Sniffing)
To capture packets sent from the DPL Runtime to the Controller:
packet_in.sniff(lambda m: print(m), timeout=10)
For detailed parsing, you can import impacket and decode the payload
Packet Out (Sending)
To send packets from the Controller to the DPL Runtime. You typically use scapy to build the packet first.
# 1. Build packet string (using Scapy or raw bytes)
my_pkt = b'<PACKET_BYTE_STRING>'
# 2. Send packet with metadata
# Repeat metadata args for every metadata defined in P4
packet_out(payload=my_pkt, <METADATA_NAME>="<VALUE>").send()
Idle Timeout Notifications
To receive notifications for entry timeouts (requires nv_support_timeout in DPL source):
idle_timeout_notification.sniff(lambda m: print(m), timeout=10)