What can I help you with?
DOCA Documentation v2.10.0

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 an open-source CLI tool that provides an interface to the P4Runtime API. It is especially useful for:

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

Info

For detailed instructions, refer to Loading DPL Applications.

The following are example commands for using the p4runtime_sh P4 Controller after loading a program.

P4 info

Operation

Command

Retrieves the content of p4info.txt of the currently loaded DPL program

Copy
Copied!
            

p4info


P4 Table

Operation

Command

Lists all P4 tables

Copy
Copied!
            

tables

Displays information about a specific P4 table

Copy
Copied!
            

tables["<P4_TABLE_NAME>"]


Working with P4 Table Entries

Operation

Command

Default Entry

Reads P4 table's default entry without counter's value

Copy
Copied!
            

te = table_entry["<P4_TABLE_NAME>"] te.is_default = True te.read(lambda te: print(te))

Reads P4 table's default entry with counter's value

Note

Supported only if a direct counter is enabled on the P4 table.

Copy
Copied!
            

te = table_entry["<P4_TABLE_NAME>"] te.is_default = True te.counter_data.byte_count = 0 te.read(lambda te: print(te))

Modifies P4 table's default entry action

Note

A default entry cannot be removed or inserted. It can only be modified to perform a different P4 action.

Copy
Copied!
            

te = table_entry["<P4_TABLE_NAME>"](action="<P4_ACTION_NAME>") te.is_default = True # Set value for all parameters required by desired action. te.action["<PARAMETER NAME>"] = "<PARAMETER VALUE>" te.modify()

Regular Entries

Note: In the following examples, some commands require specifying the match key of the desired regular P4 table key.

The syntax for specifying a match key varies according to the defined match method for each key (per the DPL source code where the keys are defined on the P4 table).

Match Method

Syntax for Specifying Match Key Value

exact

Copy
Copied!
            

te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>"

ternary

Copy
Copied!
            

te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>&&&<MASK_VALUE>" te.priority = <PRIORITY VALUE>

Note that mask is provided in the match line, separated by &&& . If mask is not specified , a full match mask will be used.

lpm

Copy
Copied!
            

te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>/<PREFIX_LENGTH_VALUE>"

Note that LPM prefix_len is provided in the match line, separated by / . If LPM prefix_len is not specified , a prefix_len with full field bitwidth is used.

For simplicity, the following examples are written using exact match syntax.

Reading Entries

Reads a specific regular P4 table entry

Copy
Copied!
            

te = table_entry["<P4_TABLE_NAME>"] try: pass    # Comment out the next line to disable reading counters te.counter_data.byte_count = 0 except Exception as e: # Table does not have a Direct Counter pass # Set value for all keys required by the P4 table. te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>" te.read(lambda te: print(te))

Reads all regular entries from a P4 table

Copy
Copied!
            

num = 1 def hndlr(te): global num print(f">> Entry number {num}:") print(te) print("----------------------------------------------------") num += 1   te = table_entry["<P4_TABLE_NAME>"] try: pass    # Comment out the next line to disable reading counters te.counter_data.byte_count = 0 except Exception as e: # Table does not have a Direct Counter pass   # Read regular entries te.is_default = False te.read(lambda te: hndlr(te))

Reads all regular entries from all P4 tables in the P4 program

Copy
Copied!
            

for tbl in tables: num = 1 def hndlr(te): global num print(f">> Entry number {num}:") print(te) print("----------------------------------------------------") num += 1   print(f"================= {tbl.name} =================") te = table_entry[tbl.name] try:        pass # Comment out the next line to disable reading counters te.counter_data.byte_count = 0 except Exception as e:        # Table does not have a Direct Counter pass   # Read regular entries te.is_default = False te.read(lambda te: hndlr(te))

Adding Entries

Adds a regular P4 table entry

Copy
Copied!
            

te = table_entry["<P4_TABLE_NAME>"](action="<P4_ACTION_NAME>") # Set value for all keys required by the P4 table. te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>" # Set value for all parameters required by desired action. te.action["<PARAMETER NAME>"] = "<PARAMETER VALUE>" te.insert()

Deleting Entries

Deletes a specific regular P4 table entry

Copy
Copied!
            

te = table_entry["<P4_TABLE_NAME>"] # Set value for all keys required by the P4 table. te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>" te.delete()

Deletes all regular entries from a P4 table

Copy
Copied!
            

te = table_entry["<P4_TABLE_NAME>"] te.read(lambda te: te.delete())


Working with Direct Counters

This allows w orking with Direct Counter directly without getting the whole Table Entry info.

Operation

Command

Lists all defined Direct Counters

Copy
Copied!
            

direct_counters

Default Entry

Reads counter data of a default entry in a Direct Counter

Copy
Copied!
            

DirectCounterEntry("<P4_DIRECT_COUNTER_NAME>") ce.table_entry.is_default = True ce.read((lambda ce: print(ce)))

Clears counter data of a default entry in a Direct Counter

Copy
Copied!
            

DirectCounterEntry("<P4_DIRECT_COUNTER_NAME>") ce.table_entry.is_default = True ce.packet_count = 0 ce.modify()

Regular Entries

Reads counter data of a a specific P4 table entry in a Direct Counter

Copy
Copied!
            

ce = DirectCounterEntry("<P4_DIRECT_COUNTER_NAME>") # Set value for all keys required by the P4 table. ce.table_entry.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>" ce.read((lambda ce: print(ce)))

Reads all counter data of specific Direct Counter

Info

This will also read the default entry counter data.

Copy
Copied!
            

ce = DirectCounterEntry("<P4_DIRECT_COUNTER_NAME>") ce.read((lambda ce: print(ce)))

Clears counter data of a a specific P4 table entry in a Direct Counter

Copy
Copied!
            

ce = DirectCounterEntry("<P4_DIRECT_COUNTER_NAME>") # Set value for all keys required by the P4 table. ce.table_entry.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>" ce.packet_count = 0 ce.modify()

Clears all counter data of specific Direct Counter

Note

This also clears the default entry counter data.

Copy
Copied!
            

ce = DirectCounterEntry("<P4_DIRECT_COUNTER_NAME>") ce.packet_count = 0 ce.modify()

Clears all direct counters from all table

Copy
Copied!
            

for dc in direct_counters: ce = DirectCounterEntry(dc.name) ce.packet_count = 0 ce.modify()


Working with Indirect Counters

Operation

Command

Lists all defined Indirect Counters

Copy
Copied!
            

counters

Shows info about a specific Indirect Counter

Copy
Copied!
            

counter_entry["<P4_INDIRECT_COUNTER_NAME>"]

Reads a specific value from a specific indirect counter

Copy
Copied!
            

ce = counter_entry["<P4_INDIRECT_COUNTER_NAME>"] ce.index = <COUNTER_CELL_INDEX> ce.read((lambda ce: print(ce)))

Reads all values from a specific indirect counter

Copy
Copied!
            

ce = counter_entry["<P4_INDIRECT_COUNTER_NAME>"] ce.read((lambda ce: print(ce)))

Clears a specific value from a specific indirect counter

Copy
Copied!
            

ce = counter_entry["<P4_INDIRECT_COUNTER_NAME>"] ce.index = <COUNTER_CELL_INDEX> ce.byte_count = 0 ce.packet_count = 0 ce.modify()

Clears all values from a specific indirect counter

Copy
Copied!
            

ce = counter_entry["<P4_INDIRECT_COUNTER_NAME>"] ce.byte_count = 0 ce.packet_count = 0 ce.modify()


P4 Actions

Operation

Command

Lists all defined P4 actions

Copy
Copied!
            

actions

Shows info about a specific P4 actions

Info

This includes its parameters' names and sizes.

Copy
Copied!
            

actions["<P4_ACTION_NAME>"]


Packet IO

To use packet IO, it must be enabled in the P4 program source code.

Operation

Command

Packet In – For receiving packets sent from the DPL Runtime daemon to the P4 Controller (according to defined rules in the DPL program)

Captures packets for 10 second, then displays them

Copy
Copied!
            

packet_in.sniff(lambda m: print(m), timeout=10)

Capturs packets for 10 second, then displays them parsed as well as their metadata info

This example command uses the impacket package for parsing the packets, so make sure that it is installed on your system prior to running the p4runtime_sh P4 Controller:

Copy
Copied!
            

pip install impacket

Then, from the p4runtime_sh P4 Controller, run:

Copy
Copied!
            

from impacket.ImpactDecoder import * for msg in packet_in.sniff(timeout=10): print("----------------------------------------------------------") print(msg) print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") print("Raw packet (hex):\n") print(msg.packet.payload.hex()) print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") print("Parsed packet:\n") print(EthDecoder().decode(msg.packet.payload)) print("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") print("Metadata:\n") for md in msg.packet.metadata: val = int.from_bytes(md.value, "big") print("metadata ID (", md.metadata_id, "): ", hex(val))

Packet Out – For sending a packet from the p4runtime_sh P4 Controller to the DPL Runtime daemon (which will process it according to defined rules in the DPL program)

Syntax of sending a packet

Copy
Copied!
            

my_pkt = b'<PACKET_BYTE_STRING_MESSAGE_GOES_HERE>' packet_out(payload=my_pkt, <METADATA_NAME>="METADATA_VALUE_GOES_HERE").send()

Repeat the <METADATA_NAME>="METADATA_VALUE_GOES_HERE" parameter for each defined metadata in the P4 program.

Example of building and sending a packet

Using scapy tool, build the desired packet:

Copy
Copied!
            

>>> p = Ether(src='00:11:11:11:11:11', dst='00:22:22:22:22:22') / IP(src="1.1.1.1", dst="2.2.2.2") >>> print(p.build()) b'\x00"""""\x00\x11\x11\x11\x11\x11\x08\x00E\x00\x00\x14\x00\x01\x00\x00@\x00t\xe4\x01\x01\x01\x01\x02\x02\x02\x02' >>>

Then send it using the p4runtime_sh P4 Controller:

Copy
Copied!
            

my_pkt = b'\x00"""""\x00\x11\x11\x11\x11\x11\x08\x00E\x00\x00\x14\x00\x01\x00\x00@\x00t\xe4\x01\x01\x01\x01\x02\x02\x02\x02' packet_out(payload=my_pkt, controller_metadata="0x123").send()


© Copyright 2025, NVIDIA. Last updated on Apr 24, 2025.