DOCA Documentation v3.2.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 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.

Info

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.txt for the currently loaded DPL program:

    Copy
    Copied!
                

    p4info

  • Lists all available tables:

    Copy
    Copied!
                

    tables

  • Displays information about a specific table:

    Copy
    Copied!
                

    tables["<P4_TABLE_NAME>"]

  • Lists all defined actions:

    Copy
    Copied!
                

    actions

  • Shows parameters and sizes for a specific action:

    Copy
    Copied!
                

    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

te.match["<KEY_NAME>"] = "<VALUE>"

Exact value match.

Ternary

te.match["<KEY_NAME>"] = "<VALUE>&&&<MASK>"

te.priority = <PRIORITY_VALUE>

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

LPM

te.match["<KEY_NAME>"] = "<VALUE>/<PREFIX_LEN>"

The prefix length is provided in the match line, separated by /. If the prefix length is not specified, the full field bitwidth is used.


Adding Entries

To add a standard entry, you must define the table, the action, the match keys, and the action parameters.

  • Regular entry:

    Copy
    Copied!
                

    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:

    Copy
    Copied!
                

    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 seconds te.insert()

Reading Entries

  • Read a specific entry:

    Copy
    Copied!
                

    te = table_entry["<P4_TABLE_NAME>"] te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>" te.read(lambda te: print(te))

  • Read all entries in a table:

    Copy
    Copied!
                

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

  • Read time since last hit:

    Note

    Only for tables with idle-timeout enabled.

    Copy
    Copied!
                

    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:

    Copy
    Copied!
                

    te = table_entry["<P4_TABLE_NAME>"] te.match["<MATCH_KEY_NAME>"] = "<MATCH_VALUE>" te.delete()

  • Delete all entries in a table:

    Copy
    Copied!
                

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

Managing Default Entries

Note

Default entries cannot be inserted or deleted, only modified.

  • Read default entry:

    Copy
    Copied!
                

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

  • Read default entry with counter:

    Copy
    Copied!
                

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

  • Modify default action:

    Copy
    Copied!
                

    te = table_entry["<P4_TABLE_NAME>"](action="<P4_ACTION_NAME>") te.is_default = True te.action["<PARAMETER_NAME>"] = "<PARAMETER_VALUE>" te.modify()

Direct Counters

Direct counters are attached to specific table entries.

  • List counters: direct_counters

  • Read specific entry counter:

    Copy
    Copied!
                

    ce = DirectCounterEntry("<P4_DIRECT_COUNTER_NAME>") ce.table_entry.match["<KEY_NAME>"] = "<VALUE>" ce.read(lambda ce: print(ce))

  • Read default entry counter: Set ce.table_entry.is_default = True before reading.

  • Clear specific counter: Set ce.packet_count = 0 and call ce.modify().

  • Clear all counters:

    Copy
    Copied!
                

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

Indirect Counters

Indirect counters are accessed by index.

  • List counters: counters

  • Read specific index:

    Copy
    Copied!
                

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

  • Clear specific index: Set ce.byte_count = 0 and ce.packet_count = 0, then call ce.modify().

Direct Meters

  • List meters: direct_meters

  • Read specific meter:

    Copy
    Copied!
                

    me = DirectMeterEntry("<P4_DIRECT_METER_NAME>") me.table_entry.match["<KEY>"] = "<VALUE>" me.read(lambda me: print(me))

  • Configure meter: Set config values (cir, cburst, pir, pburst) and call me.modify().

Indirect Meters

  • List meters: meters

  • Read specific index:

    Copy
    Copied!
                

    me = meter_entry["<P4_INDIRECT_METER_NAME>"] me.index = <INDEX> me.read(lambda me: print(me))

  • Clear meter: Set me.cir = 0 (and other rates) and call me.modify().

Packet In (Sniffing)

To capture packets sent from the DPL Runtime to the Controller:

Copy
Copied!
            

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

Note

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.

Copy
Copied!
            

# 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):

Copy
Copied!
            

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


© Copyright 2025, NVIDIA. Last updated on Nov 20, 2025