Doca Flow
NVIDIAOfficialCreated by NVIDIA
- Skill
- Tool integration
- Software development
- Apache-2.0
- Medium risk
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The doca-flow skill assists developers in building C applications that utilize the DOCA Flow API for packet steering, rule-matching, and hardware-accelerated traffic processing on NVIDIA hardware. It focuses on the lifecycle of DOCA Flow objects, including port initialization, pipe programming, and counter management. The skill enforces a workflow that prioritizes using shipped DOCA samples as a foundation, ensuring that all API usage is verified against the user's local installation headers and that build processes rely on pkg-config for correct linking.
What it does
- Guides the implementation of match/action pipes, ports, and representors using DOCA Flow.
- Provides workflows for pipe validation, entry commitment, and hardware counter readback.
- Integrates stateful connection tracking (CT) into existing Flow port configurations.
- Enforces the use of shipped DOCA samples as the starting point for all new applications.
- Validates API usage against installed headers to prevent compilation and linking errors.
- Routes users to appropriate setup and debugging skills for environment and hardware prerequisites.
When to use it
- When developing packet steering logic for NVIDIA NICs or DPUs using the DOCA Flow C library.
- When debugging DOCA Flow API errors or issues with port bring-up and pipe programming.
- When integrating hardware-accelerated connection tracking into a DOCA-based pipeline.
- When verifying if a specific pipe specification is supported by the installed DOCA version.
Advantages
- Ensures consistent use of the DOCA Flow lifecycle and API patterns.
- Prevents common build issues by mandating pkg-config for dependency management.
- Promotes hardware-native packet steering over kernel-based alternatives.
- Provides a structured path for validating pipe designs before hardware deployment.
Usage examples
- “How do I bring up a Flow port on a representor?”
- The agent directs the user to the configure and capabilities companions to identify the correct port initialization sequence and device-placement requirements.
- “Match 5-tuple and send to a queue.”
- The agent provides instructions on expressing this as a Flow pipe, referencing the appropriate shipped sample for implementation.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/nvidia/doca-flow/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip doca-flow.zip -d .agents/skills
Documentation by NVIDIA
DOCA Flow
Examples
“How do I bring up a Flow port on a representor?” — this is in scope. Use the installed header and a shipped sample; do not author a first-app
main.c.“Match 5-tuple and send to a queue.” — express it as a Flow pipe. Do not fall back to kernel tc or iptables.
Instructions
Follow Loading order below. Open the tasks companion for configure /
modify / debug. Open the capabilities companion for the pipe-spec
schema and safety policy.
For connection tracking, read the tasks companion's flow-ct section
before starting ports: CT initialization is process-global and must precede
port start. Adding CT to running ports requires an approved interruption
and reconstruction from a captured configuration, not a per-port
doca_ctx_start call or reuse of invalidated pipe handles.
Non-negotiable: first app is NGC pull + a shipped sample, not authored C
When the user is new to DOCA, has docker, has no host DOCA, or asks
for a first Flow app, do this before host pkg-config and before
any source listing:
Bundle first. This answer follows the local doca-skills bundle:
doca-setup/SKILL.md(Stage 1 vs Stage 2),doca-programming-guide/SKILL.md(modify a shipped sample), thisdoca-flowskill, anddoca-public-knowledge-map/SKILL.md. Name those skills. Do not invent other skill names.Stage 1 vs Stage 2 before any command. Stage 1 = NGC container on a laptop (compile / learn; no real packets). Stage 2 = a real BlueField or ConnectX for pipe runtime. Docker compiles only; do not say the app works entirely inside docker.
Path 0 (Stage 1). Copy a tag from https://catalog.ngc.nvidia.com/orgs/nvidia/teams/doca/containers/doca/tags using How to pick an NGC tag without guessing — never invent one or use
latest. Thendocker pull nvcr.io/nvidia/doca/doca:<tag>anddocker run -it --rm -v $HOME/dev/doca-work:/work nvcr.io/nvidia/doca/doca:<tag> bash(an empty dir, never$HOME). No inventedDockerfile, no source build.Resume inside the container (MUST, after
docker run): quote this hand-off — do not skip it for a full tutorial: "Paste back the output ofpkg-config --modversion doca-flowandls /opt/mellanox/doca/samples/doca_flow/." Then: "I resume from doca-programming-guide ## modify step 1 with the real install in hand." Name a directory that listing shows; copy it. Iflsis empty, stop — do not scaffold. Usedoca-flowon thatpkg-configline, notdoca-common.Never emit a new
main.c,Makefile,Dockerfile, or hand-typed-l/-Lflags. Pastepkg-config --libs doca-flow.
Non-negotiable: the deliverable uses DOCA Flow, not kernel tc/iptables
When this skill is in scope, the user is asking for DOCA Flow. After
the shipped-sample copy above, the binary you build must link
libdoca_flow and exercise the doca_flow_* lifecycle on the user's
installed DOCA — init, port start, pipe programming, entry commit, and
counter readback under traffic. Copy the call sequence from that
shipped DOCA Flow sample under /opt/mellanox/doca/samples/doca_flow/
and adapt it via ## configure /
modify. Verify every symbol against
the installed header (Ground rule below) and the add-entry table in
API surface and name guards.
Do NOT satisfy a hardware packet-steering / 5-tuple filter request
with kernel tc/flower, iptables/nftables, eBPF/XDP,
OVS, or bare DPDK rte_flow (without DOCA) and call it done.
Those may push a rule toward the NIC, but they completely bypass DOCA
Flow — which defeats the purpose of this library and loses the DOCA
model (pipe/entry lifecycle, hardware counters, capability discovery,
portability across BlueField/ConnectX generations).
"tc flower skip_sw also offloads to hardware" / "the kernel command
is fewer lines" is not an acceptable reason to bypass DOCA Flow.
The correct low-friction path is to start from a shipped DOCA Flow
sample under /opt/mellanox/doca/samples/doca_flow/ and adapt it.
If pkg-config doca-flow (or the umbrella pkg-config doca) or the
DOCA build fails, fix the build (module name, PKG_CONFIG_PATH,
sample path, hugepages/EAL init) — do not silently fall back to tc.
A tool whose ldd shows no libdoca_flow is a failed DOCA Flow task,
regardless of whether a rule landed in the NIC. Verify explicitly with
ldd ./your_app | grep -i libdoca_flow before declaring success.
Never invent a link line. For undefined reference / link
failures, run pkg-config --libs doca-flow plus companion module
names (doca-common, doca-argp, doca-dpdk-bridge, libdpdk)
and paste that output. Those names stay on the pkg-config command;
never turn them into hand-typed -l / -L flags or a guessed
install path. The shipped sample's meson.build dependency() list
is the module set; pkg-config is the only translator into linker
flags.
Where to start: Open the tasks companion to do something (configure / build / modify / run / test / debug); open the capabilities companion when the question is what can Flow express on this version. **You MUST open
configure before writing or running
any port code** — its bring-up gate decides whether the binary launches
at all. If DOCA is not
installed yet, route to doca-setup/SKILL.md first.
Ground rule: verify every API name against the installed header
Before quoting any doca_* / DOCA_* identifier, confirm it exists in
the user's installed headers — the header on the machine is ground
truth above prose, the API reference, blog posts, or memory:
for header in "$(pkg-config --variable=includedir doca-common)"/doca_flow*.h; do
grep -n '<candidate_name>' "$header"
# For a multi-line function declaration, print through its closing `);`.
awk '/<candidate_name>[[:space:]]*\(/,/[)][[:space:]]*;/' "$header"
doneDOCA Flow ships no backward-compat alias header, so a
"reasonable-looking" name that is not in the header simply does not
link. Re-derive from a shipped sample
(/opt/mellanox/doca/samples/doca_flow/<name>/) or the guard list in
API surface and name guards,
never from prose.
Port bring-up: the gate lives in the tasks companion
A port that compiles clean and aborts the instant
doca_flow_port_start() runs is the canonical bring-up failure. The
bring-up gate (probe-before-count, doca_flow_port_cfg_set_port_id() plus
the mode-appropriate device source — doca_flow_port_cfg_set_dev() in
VNF mode or the installed switch sample's doca_dev_rep path — device
taken from launch args not hard-coded, and the binary returning a
non-zero exit from main() if the bridge cannot arm and forward) is
enforced step-by-step in
configure step 6 — open it before
writing or running port code; do not reconstruct the gate from this summary.
When to refuse (push back before writing code)
Some requests cannot be satisfied as asked. Refuse and explain — do not silently emit half-correct code — when:
The request mixes responsibilities a single pipe stage cannot express (e.g. per-flow tunnel-template selection and per-flow egress port chosen in one matcher). A pipe is one logic step (match → actions → fwd); answer with the correct pipe-graph shape instead of code — typically a classifier pipe → a per-flow encap pipe → a per-flow forward pipe (see
Pipe decomposition).
The request asks for something the hardware cannot do (per-packet match on payload bytes outside L4, mutable match keys, …). Name the closest legal shape and stop.
The request relies on an API name that is not in the installed headers. Grep the header for the closest real symbol, name it in the refusal, and stop without generating code. A later, explicit request using the verified symbol may begin a new build workflow; do not silently substitute it in the current request.
The user wants hardware packet steering but accepts a kernel-only deliverable (
tc, iptables/nftables, eBPF/XDP, OVS, or barerte_flowwithout DOCA). Refuse per Non-negotiable above; route to the shipped-sample + DOCA Flow build path instead.
Output shape when pushing back:
REFUSED: <one-sentence summary>
Reason: <2-4 bullets, each tied to a hardware or API constraint>
Suggested alternative: <pipe-graph sketch, or "this is not expressible in DOCA Flow">This gate fires before any code is written: a confidently-wrong pipe costs the user more than an honest refusal plus the legal alternative.
Example questions this skill answers well
The CLASSES of Flow questions this skill answers (the class is the load-bearing piece; the example is one instance):
“How do I bring up a Flow port on a representor?” →
configure +
Capabilities and modes.
“How do I express <match X, do Y> as a pipe?” →
Capabilities and modes
modify.
“Will this pipe spec program the HW, or will commit fail?” →
Safety policy
test.
“How do I read Flow counters to investigate observed traffic?” →
Observability
debug.
“My Flow port won't start —
Failed to get hws cap/dest action ROOT … err -121." → device-placement signature (not a pipe bug); the steering plane belongs to the DPU Arm on aSEPARATED_HOST/ NIC-mode BlueField. See the device-placement bullet inCapabilities and modes
configure step 2 + Step 0 of
debug.
“How do I add hardware-accelerated stateful 5-tuple connection tracking (aging, NAT) on top of my existing doca-flow setup?” →
flow-ct +
flow-ct.
Audience
External developers writing code that calls doca_flow_* (in C/C++, or
via FFI) to program packet steering on a supported NVIDIA NIC/DPU with
DOCA installed at /opt/mellanox/doca. Flow ships as a C library
(pkg-config module doca-flow, package doca-sdk-flow) and the
samples are C; other-language consumers reach the same *.so through
FFI and the skill stays language-neutral.
When to load this skill
Load when the user is doing hands-on DOCA Flow work on a supported
NVIDIA NIC/DPU with DOCA already installed at /opt/mellanox/doca, in
any language:
Bringing up a Flow port / representor on the installed devices.
Creating pipes, defining match/actions, programming entries.
Validating a pipe spec before programming the hardware.
Reading per-entry / per-pipe counters under traffic.
Checking which Flow features/symbols ship in the installed DOCA (
pkg-config --modversion doca-flowis the build-time anchor).Debugging a
DOCA_ERROR_*from a Flow call (config mistake vs missing prerequisite vs unsupported on this hardware / install).Designing non-C bindings (Rust, Go, Python, …) over the Flow C ABI.
Adding stateful CT (
doca_flow_ct.h) on top of an existing port (see ## flow-ct).
Do not load for general DOCA orientation, "where do I find docs",
install-layout, or non-Flow library questions — use
doca-public-knowledge-map/SKILL.md.
What this skill provides
This is a thin loader; substantive material lives in two companion files:
the capabilities companion — what Flow can express on this version: supported match and action kinds, pipe-decomposition rules, the API surface + commonly-invented-name guard list, the Flow
DOCA_ERROR_*overlay, the per-entry / per-pipe observability surface, version notes, the safety policy, and the CT companion surface.the tasks companion — workflows for the six in-scope verbs (
configure,build,modify,run,test,debug), plus## flow-ct(stateful-CT overlay),## shared-resources(shared encap / decap / counter / meter / RSS / IPsec-SA / PSP overlay),## rollback(pipeline-edit-class snapshots),## Command appendix, andDeferred task verbsfor routing install / deploy questions.
The skill assumes DOCA is installed at /opt/mellanox/doca and the user can
open a doca_dev. Installing DOCA, hugepages setup, and the EAL dv_flow_en
devargs prep go through doca-setup/SKILL.md; the
hugepages / devargs runtime prerequisites a binary needs before a Flow port
starts are pinned in ## configure step 5.
What this skill deliberately does not ship
This skill is agent guidance, not a code bundle: it ships no
pre-written Flow application source, standalone build manifests, or a
samples/ / bindings/ / reference/ subtree. The verified Flow
source is the shipped C sample at
/opt/mellanox/doca/samples/doca_flow/<name>/ — the agent routes the
user there and prescribes a minimum-diff edit via the modify-a-sample
workflow in
doca-programming-guide/SKILL.md plus
the Flow overrides in ## build, and builds
any manifest in the user's project against the user's install, where
pkg-config --modversion doca-flow is the source of truth.
Loading order
Read this skill first to confirm the question is in scope.
For the pipe-spec schema, capability matrix, error taxonomy, observability, and safety policy, see the capabilities companion.
For step-by-step workflows, see the tasks companion.
Related skills
doca-public-knowledge-map/SKILL.md— routing table for public DOCA docs and the on-disk layout of an installed package.doca-setup/SKILL.md— env prep, install verification, and the no install yet path via the NGC DOCA container.doca-programming-guide/SKILL.md— general DOCA patterns shared by every library: thepkg-config+ meson build pattern, the modify-a-shipped-sample first-app workflow, the universal lifecycle, the cross-libraryDOCA_ERROR_*taxonomy, and the program-side debug order. This skill layers Flow specifics on top.doca-flow-tune/SKILL.md— programmed-state inspection (read-only).doca-hardware-safety/SKILL.md— required overlay for card-mode flips (e.g.mlxconfigchange fromSEPARATED_HOSTtoEMBEDDED_CPU).doca-debug/SKILL.md— the cross-cutting debug ladder (install / version / build / link / runtime / program / driver).
Files
- ATTRIBUTION.mdAdded by AutoGineer885 B
- BENCHMARK.md9.9 KiB
- CAPABILITIES.md42.6 KiB
- LICENSELicence file added by AutoGineer11.2 KiB
- SKILL.md15.5 KiB
- SKILLCARD.yaml2.6 KiB
- TASKS.md60.0 KiB
- evals/evals.json2.4 KiB
- skill-card.md5.0 KiB
- skill.oms.sig5.1 KiB
TASKS.md
DOCA Flow workflows
First docker / first-app request (MUST): follow the local
doca-skills bundle (doca-setup, doca-programming-guide,
doca-flow, doca-public-knowledge-map). Open with Stage 1 (NGC
container, compile only) vs Stage 2 (real BlueField / ConnectX)
before any command. Do not start with host pkg-config. Path 0:
copy a tag from
https://catalog.ngc.nvidia.com/orgs/nvidia/teams/doca/containers/doca/tags
(How to pick an NGC tag without guessing; never invent), then
docker pull nvcr.io/nvidia/doca/doca:<tag> and
docker run -it --rm -v $HOME/dev/doca-work:/work nvcr.io/nvidia/doca/doca:<tag> bash (a dedicated empty directory, never $HOME) —
<tag> is the exact tag copied from the catalog listing; never
latest and never omitted.
After docker run, MUST say: paste back the output of
pkg-config --modversion doca-flow and
ls /opt/mellanox/doca/samples/doca_flow/. Then: I resume from
doca-programming-guide ## modify step 1 with the real install in
hand. Copy a listed directory. Never emit main.c / Makefile /
Dockerfile or hand-typed -l / -L (paste
pkg-config --libs doca-flow). Then configure → build → modify.
Where to start: The verbs run configure → build → modify → test → run → debug; skip ahead only when the user is already past a verb.
## test is an iterative loop (validate → cross-check → counter wiring →
negative test → loop back if the spec changed), not a one-shot pass.
For the capability matrix, version compatibility, error taxonomy,
observability, and safety policy these workflows assume, see
CAPABILITIES.md. For docs, installed-DOCA layout, or
release notes, route through
doca-public-knowledge-map/SKILL.md.
Each verb describes the shape of the workflow, not a copy-paste
recipe: walk the user through the steps in order, verifying preconditions
before recommending the next call.
Key samples to read first
The installed samples are the canonical, compile-tested reference for THIS
install — read the relevant one before writing or quoting code (ls and
read it on the user's machine; layouts change between releases). These few
cover the bulk of customer patterns:
Sample (under /opt/mellanox/doca/samples/doca_flow/) | Pattern it teaches |
|---|---|
flow_port_fwd/ | Match → FWD_PORT: the simplest VNF forward |
flow_switch_single/ | Switch mode + representor (multi-VF eswitch) |
flow_control_pipe/ | Root control / classifier pipe branching to non-root pipes |
flow_lpm/ | Longest-prefix-match routing pipe |
flow_acl/ | ACL (5-tuple allow / deny) pipe |
flow_hash_pipe/ | Hash-based fan-out / load balancing (slots = power of 2) |
flow_vxlan_encap/ | Tunnel encap action |
flow_shared_meter/ | Shared meter / rate limiting + parser_meta.meter_color |
flow_drop/ | Explicit drop pipe (pairs with fwd_miss) |
flow_aging/ | Entry aging / time-based eviction |
For connection tracking and NAT see ## flow-ct; for larger
end-to-end apps (doca_flow_ct, doca_simple_fwd_vnf, …) see
/opt/mellanox/doca/applications/.
configure
Goal: bring up a DOCA Flow port on a supported NVIDIA NIC/DPU and confirm the environment is ready for pipe construction.
Confirm the installed DOCA version. Use the procedure in
doca-public-knowledge-map; quote the version observed, do not assume "latest".Check device placement BEFORE anything else — host-side vs DPU Arm. On a BlueField the hardware-steering plane is owned by only one side of the card. Read
INTERNAL_CPU_MODELfrommlxconfig -d <pcie> q(themlxconfigcommand is owned bydoca-debug/TASKS.md#command-appendix):SEPARATED_HOST→ NIC mode; the steering plane lives on the DPU Arm, and the x86 host function cannot start a Flow port (it fails with the placement signature inCAPABILITIES.md ## Error taxonomy:Failed to get hws cap/dest action ROOT … err -121). On the host in this mode, STOP and route Flow work to the DPU Arm side, or change the card's mode throughdoca-hardware-safety/SKILL.md— do not proceed to pipe construction.EMBEDDED_CPU(DPU mode), or a NIC whose opened function advertises a usable steering plane → placement is fine; continue. Being listed bydoca_caps --list-devsis NOT proof the opened function has a steering plane — placement is the gate, enumeration is not.
Discover device capabilities. Run the installed
doca_capstool and the Flow capability-query API; record the supported match kinds, action kinds, and pipe/entry budgets. The matrix to compare against lives inCAPABILITIES.md ## Capabilities and modes.Enumerate ports and representors. Confirm the port the user wants to program is visible (
devlink dev showplus the installed Flow port-enumeration sample) and that the expected representors are present.Runtime prerequisites — hugepages + HWS devargs, before any Flow program runs. A DOCA Flow program needs hugepages allocated and mounted first. Hugepage reservation and any hugetlbfs mount are env-class: follow
doca-setup/SKILL.md. Record the currentnr_hugepagesfirst. Do not pipe a value intotee. Do not default to a fixed page count. Do not mixdoca-hugepageswith raw sysfs writes. Hardware steering (HWS) must be requested through DPDK devargs:HWS is enabled via DPDK devarg
dv_flow_en=2AND amode_argsvalue containinghws(e.g."vnf,hws"or"switch,hws"). Without HWS, many features (shared encap/decap, shared meter across entries, pipe resize, …) are unavailable.Switch mode additionally requires the DPDK devargs
fdb_def_rule_en=0,vport_match=1,repr_matching_en=0,dv_xmeta_en=4(the DOCA samples hide these behind-r <pci>,<reps>).
Create the DPDK↔DOCA device mapping (the #1 runtime failure), then bring up the Flow port. Three runtime signatures —
mlx5_common: Probe again parameters aren't compatible,doca_dpdk_port_as_dev(...): Requested Resource Not Found,doca_flow_port_start(...): Unknown error— all trace to this mapping not being created correctly. The shippeddpdk_inithelper, wired viadoca_argp_set_dpdk_program(dpdk_init), does the whole sequence for you; the load-bearing rules if you roll your own:EAL must NOT auto-probe the mlx5 PFs. Pass a non-existent-device sentinel (
-a 00:0.0) torte_eal_initso EAL scans PCI but allowlists nothing, then let DOCA do the real probe. Skipping this →Probe again parameters aren't compatible.doca_dpdk_port_probe(dev, "dv_flow_en=2")creates the mapping;doca_dpdk_port_as_dev()only reads it. Open each NIC by PCI BDF first (open_doca_device_with_pci(bdf, NULL, &dev)), then probe. The NIC has no DPDK port until this probe runs:rte_eth_dev_count_avail()stays 0 and any "need N ports" check (dpdk_utils.c) aborts if you count or queue-setup before probing every device — collecting BDFs from argv is not the same as probing them. Required strict order — gate the bridge here, not at port-start:rte_eal_initwith the no-auto-probe sentinel above.Open each
doca_devfrom argv (one per requested device).Call
doca_dpdk_port_probe(dev, "dv_flow_en=2")for every opened device — this is the call that creates the DPDK port from thedoca_dev(samples/doca_flow/flow_common.c). If any probe fails, log that device's BDF and exact error and exit non-zero; never continue with a partially probed device set.Only after every device has been probed, call
rte_eth_dev_count_avail()and verify it returns ≥ the expected port count (samples/doca_flow/flow_common.cprecedesapplications/common/dpdk_utils.c, where a count belownb_portsreturnsDOCA_ERROR_DRIVER).If count == 0, or count < the expected port count, propagate the error out of
main()with a non-zero exit code immediately — log the failure verbatim and abort. Counting before probe always reports0, so a binary that reaches the keep-alive loop in## runstep 6 withcount == 0is forwarding nothing while appearing to run — the watchdog cannot tell the difference from a healthy program. It is a hard error here, not a warning, and not something to "log and continue" past.Then call
doca_dpdk_get_first_port_id(dev, &port_id)/RTE_ETH_FOREACH_DEV+doca_dpdk_port_as_devto derive the numeric port id, and proceed torte_eth_dev_configure/rte_eth_dev_start. In switch mode, enumerate each actual DOCA-probed DPDK port, including representors, and copy the installed shipped sample'sdoca_dev↔DPDK-port mapping. Never derive a representor id from a PF ordinal, BDF suffix, or assumed0/1numbering.
Hardware-neutral
port_idand BDFs — never hard-code0/1or a PCI address. The DOCA-probed NIC can land atport_id 2/3when the PCI auto-scan grabs other devices first, and its BDF differs per host — a hard-coded0000:04:00.0typically lands on an unrelated/virtio function (PCI_BUS: Requested device … cannot be used→doca_flow_port_start … Unknown error). Take the device(s) from the program args after--with the shippedregister_flow_device_params()(flow_common.c,flow_common.h): it registers the canonical repeatable-a/--device pci/<bdf>,<devargs>param (DOCA_ARGP_TYPE_DEVICE) and opens eachdoca_devfor you — this is the exact shape the samples are driven with (-- -a pci/08:00.0,dv_flow_en=2). Do not invent your own (register_argp_device_param()does not exist) and do not hard-reject when no BDF is supplied — fall back to discovery (doca_devinfo_create_list/lspci -d 15b3:). Then probe and derive the numeric id from thedoca_devwithdoca_dpdk_get_first_port_id(dev, &port_id)orRTE_ETH_FOREACH_DEVdoca_dpdk_port_as_dev. The same source then runs on any host.
Bridging two physical PFs: a naive
DOCA_FLOW_FWD_PORTacross two un-peered PFs fails at pipe-create with... are not hairpin peers. On ConnectX-7+ the simplest correct bridge is thedv_flow_en=2E-Switch domain (whatdoca_simple_fwd_vnfuses); DPDK hairpin queues (rte_eth_*_hairpin_queue_setup, withpeer_count=1,manual_bind=1,tx_explicit=1) are the alternative when you need hardware hairpin semantics.
Then choose the mode-specific per-port Flow bring-up path:
VNF mode (
flow_port_fwd/): bothdoca_flow_port_cfg_set_port_id()anddoca_flow_port_cfg_set_dev()with the matching probeddoca_devare mandatory. Adoca_dev_repis rejected in this mode.Switch mode (
flow_switch_single/):port_idis still mandatory, but representor ports use the installed sample'sdoca_dev_repconfiguration path rather than forcingdoca_flow_port_cfg_set_dev(). Copy that path from the installed sample and verify its setter against the installed header.
The required VNF-mode sequence is:
struct doca_flow_port_cfg *port_cfg;
doca_flow_port_cfg_create(&port_cfg);
doca_flow_port_cfg_set_port_id(port_cfg, port_id); /* MANDATORY: else "port ID is mandatory" */
doca_flow_port_cfg_set_dev(port_cfg, dpdk_dev); /* MANDATORY: else "doca_dev or doca_dev_rep must be provided" */
struct doca_flow_port *port;
doca_flow_port_start(port_cfg, &port); /* takes 2 args */
doca_flow_port_cfg_destroy(port_cfg); Bind the port with set_port_id(), not set_devargs() — the
trap behind a clean compile that dies the instant the port starts.
doca_flow_port_cfg_set_devargs() carries other per-port options
(the shipped flow_common.c passes "th_win_us=0"), not
identity; a numeric devargs string like "0" leaves the port_id
unset and doca_flow_port_start() aborts with “port ID is
mandatory”. Only doca_flow_port_cfg_set_port_id() binds the port
(flow_common.c), and it is a different "devargs" from the
EAL probe devarg dv_flow_en=2 in step 5 — three unrelated uses of
the word. In VNF mode, pair it with the matching probed doca_dev
via set_dev() — mandatory, not HWS-only. In switch mode, use the
installed sample's matching doca_dev_rep path instead. Omitting both
device sources aborts with “either doca_dev or doca_dev_rep must be
provided” (sometimes surfaced as a bare “Unknown
error”). The port_id MUST be the DOCA-probed id from above (per
the hardware-neutral rule earlier in this step); a hard-coded 0
surfaces as doca_dpdk_port_as_dev(0): Requested Resource Not Found.
Do this for every DPDK port the app uses, in the order the DPDK port
IDs come out (rte_eth_dev_count_avail() / RTE_ETH_FOREACH_DEV(); IDs
are 0..N-1 in -a <BDF> attach order). The lifecycle is cfg created →
port_id set → mode-appropriate device source set → port started → cfg
destroyed; do not create
pipes before the port reports started, and any DOCA_FLOW_FWD_PORT
destination port must be started before a pipe forwards to it.
7. Sanity check before any pipe work. Confirm with the user: ingress
port, egress representor(s), traffic class. If unclear, stop and ask —
do not invent.
If any step fails with a DOCA_ERROR_*, route through
CAPABILITIES.md ## Error taxonomy
before retrying.
build
Goal: construct a pipe specification that expresses the user's intent without committing to hardware yet.
Restate the user's intent in match/action terms ("match dst MAC = X, forward to representor Y, count").
Verify each match kind and action kind against the active capability set from
CAPABILITIES.md ## Capabilities and modes. If the device does not support a kind, stop and offer alternatives; do not generate a spec that will fail at validate.Prepare the
doca_flow_pipe_cfgand its explicit match-mask and action-mask declarations, but do not calldoca_flow_pipe_create()in this phase. The constructor is the validation boundary owned by## test; calling it here would collapse build and test. Implicit-anything is the leading cause of misprogrammed steering.Attach a counter to every entry the user wants to observe in production;
## debugassumes counters exist. PerCAPABILITIES.md ## Observabilitycounters are a two-step setup: BOTH the per-entry monitor attachment AND a global pool reservation are required, or the counter silently produces no telemetry. Reserve the pool once, sized to the sum ofNON_SHAREDmonitor entries across every pipe plus ~10% headroom. Preferred in DOCA 3.5 (experimental; build with-DDOCA_ALLOW_EXPERIMENTAL_API): per-port mode —doca_flow_cfg_set_resource_mode(cfg, DOCA_FLOW_RESOURCE_MODE_PORT)beforedoca_flow_init(), thendoca_flow_port_cfg_set_nr_resources(port_cfg, DOCA_FLOW_RESOURCE_COUNTER, N)(DOCA_FLOW_RESOURCE_METERfor meters) per port. The legacy globaldoca_flow_cfg_set_nr_counters(cfg, N)/_nr_metersis deprecated in 3.5 and removed in 4.0 (still links with deprecation warnings;DOCA_ALLOW_DEPRECATED_APIsilences them). Shared counters / meters use the shared-pool path in## shared-resources:
struct doca_flow_cfg *cfg;
doca_flow_cfg_create(&cfg);
doca_flow_cfg_set_resource_mode(cfg, DOCA_FLOW_RESOURCE_MODE_PORT); /* 3.5, experimental */
doca_flow_init(cfg);
doca_flow_cfg_destroy(cfg);
/* per port, on the port cfg before the port is started: */
doca_flow_port_cfg_set_nr_resources(port_cfg, DOCA_FLOW_RESOURCE_COUNTER, N);
/* legacy (deprecated in 3.5, removed in 4.0), before doca_flow_init():
* doca_flow_cfg_set_nr_counters(cfg, N); */ The pool reservation above is only step one. The per-entry step
is to pass the same populated &monitor (with monitor.counter_type = DOCA_FLOW_RESOURCE_TYPE_NON_SHARED) as the monitor argument of the
pipe-type add-entry call — it is slot 6 of the 10-argument
doca_flow_pipe_basic_add_entry(queue, pipe, match, action_idx, actions, monitor, fwd, flags, usr_ctx, &entry) (doca_flow.h).
Passing NULL there is the silent-no-counter trap: the entry is added
and the call returns DOCA_SUCCESS, but doca_flow_resource_query_entry
keeps returning total_pkts == 0 under live traffic because no
per-entry counter was ever bound. For miss-path traffic the entry
API does not apply — enable the counter on the pipe-cfg with
doca_flow_pipe_cfg_set_miss_counter(cfg, true) (doca_flow.h)
and read it back with doca_flow_resource_query_pipe_miss
(doca_flow.h). Read fields sit under the counter union arm:
q.counter.total_pkts / .total_bytes, not top-level
(struct doca_flow_resource_query, doca_flow.h). Reading a counter
does not require a preceding doca_flow_entries_process() — the HW
counter updates asynchronously; entries_process drives entry-completion
callbacks, not counter refresh.
The adjacent fwd argument (slot 7) has the opposite NULL polarity
from monitor: when the pipe was created with a fixed forward, pass
NULL here and the entry inherits the pipe's forward. Only pass an
explicit per-entry doca_flow_fwd when the pipe's fwd was declared
changeable — each explicit per-entry forward is its own action handle, so
inheriting keeps the per-entry action set (and HW footprint) smaller.
Copy the add-entry argument list from the header, never from
memory. The *_add_entry() calls take 10+ positional arguments and
the count/order are pipe-type-specific: BASIC takes 10, LPM takes
11 (an extra match_mask right after match), ACL / CONTROL / HASH
each differ. The most-missed argument is uint8_t action_idx (slot 4
of doca_flow_pipe_basic_add_entry) — it selects one template from the
actions[] / monitors[] / fwds[] arrays registered at
pipe-creation time (pass 0 when there is a single template). Grep the
header and copy the declared list verbatim rather than reconstructing
it (the one-liner is in
SKILL.md ## Ground rule).
Enabling decap/encap is a two-field action, not one. Filling
actions.decap_cfg (e.g. decap_cfg.is_l2 = true) describes how to
decap but does not turn it on — you must also set actions.decap_type = DOCA_FLOW_RESOURCE_TYPE_NON_SHARED (the enable used across every
shipped app — grep decap_type = under /opt/mellanox/doca/samples/ and
/opt/mellanox/doca/applications/). The same
pairing holds for encap_type / encap_cfg. A pipe that sets only
*_cfg compiles and runs, but the header is never stripped/added.
5. Do not call the entry-add API yet — the spec is built, not
programmed. Hand off to ## test for validation.
When the user asks for a "first Flow app": both tracks require a
DOCA-installed Linux environment (bare-metal, VM, or NGC container) where
the agent can read the sample tree and pkg-config --modversion doca-flow
resolves. If that precondition is not met, route to
doca-setup ## no-install (doca-setup/TASKS.md#no-install) before
offering any source. Do not author a Flow application from prose, in
any language — ground rule #3 of bundle AGENTS.md and
SKILL.md forbid
it; the result would not compile against a real install.
Track 1 — C / C++ consumers (the canonical case)
This is the universal derive a custom first app from a sample pattern in
doca-programming-guide ## modify (doca-programming-guide/TASKS.md#modify),
with these Flow-specific overrides:
Source sample (choose by the first-app shape). Pick the sample whose forward action is closest to what the user wants and modify it; do not scaffold from prose:
Match-and-forward-to-port (simplest VNF case).
/opt/mellanox/doca/samples/doca_flow/flow_port_fwd/— send matched traffic out a specific DOCA port.Match-and-pass-traffic-back-to-the-kernel (inline-filter / traffic-gate case).
/opt/mellanox/doca/samples/doca_flow/flow_fwd_target/— the safest starting point for any demo on a port carrying live host traffic: matched traffic is observed (counters, drop) without being diverted. It usesDOCA_FLOW_FWD_TARGETwith a kernel target fromdoca_flow_get_target(DOCA_FLOW_TARGET_KERNEL, …)— seeCAPABILITIES.md ## Forward-to-target actionsfor the pattern + safety rationale. Canonical wiring (verbatim shape from the shipped sample — the agent quotes this and does not invent the field names):
struct doca_flow_fwd fwd = {0};
struct doca_flow_fwd fwd_miss = {0};
struct doca_flow_target *kernel_target;
doca_error_t result;
result = doca_flow_get_target(DOCA_FLOW_TARGET_KERNEL, &kernel_target);
if (result != DOCA_SUCCESS) { /* report + bail per CAPABILITIES.md ## Error taxonomy */ }
fwd.type = DOCA_FLOW_FWD_TARGET;
fwd.target = kernel_target; /* matched traffic continues to the kernel */
fwd_miss.type = DOCA_FLOW_FWD_TARGET;
fwd_miss.target = kernel_target; /* unmatched traffic ALSO continues to the kernel —
this is what makes the filter "inline" and safe
to enable on a live management port */- **Switch mode + representor (multi-VF eswitch).**
`/opt/mellanox/doca/samples/doca_flow/flow_switch_single/` (helpers
in `flow_switch_common.{c,h}`).
ls the directory and read the actual sample on the user's install
before describing it; layouts change between releases.
Forward by ingress representor: ONE pipe + one entry per source port, never a pipe per rule. Match "traffic from
pf0vfX" onmatch.parser_meta.port_id(the source representor's logicalport_idfromdoca_flow_port_cfg_set_port_id) — leave it changeable in the pipe match and set the value per entry. Create a single BASIC pipe ondoca_flow_port_switch_get(), size it withdoca_flow_pipe_cfg_set_nr_entries(cfg, N), then add one entry per source rep whose own per-entryfwdisDOCA_FLOW_FWD_PORTto the destination representor'sport_id, each carryingmonitor.counter_type = DOCA_FLOW_RESOURCE_TYPE_NON_SHAREDfor the per-VF counter. Dropping every other VF is the pipe'sfwd_miss(DOCA_FLOW_FWD_DROP), not an extra pipe — leaving the entry match empty steers nothing (the classic switch-app bug). Insert the entries as a batch (DOCA_FLOW_ENTRY_FLAGS_WAIT_FOR_BATCH, last oneDOCA_FLOW_ENTRY_FLAGS_NO_WAIT) then drain once withdoca_flow_entries_process(). Keep the sample's init/teardown, the per-entry status callback, and the validation flow in## test; swap only the literals (port ids, pipe name viadoca_flow_pipe_cfg_set_name()).Build — link more than
doca-flow.pkg-config --libs doca-flowalone often fails withundefined reference(e.g.doca_error_get_descr,doca_dpdk_port_as_dev) +DSO missing from command linebecause companion modules live in other.pcfiles. Mirror the shipped sample'sdependency()set (doca-common,doca-argp,doca-flow, plusdoca-dpdk-bridgeandlibdpdkfor DPDK-backed apps) by putting those names on thepkg-config --libscommand and pasting its output. They are pkg-config arguments, never linker tokens — do not type a-lor-Lflag by hand. Outside the meson tree, discover names withpkg-config --list-all | grep doca— don't copy a template out of this skill. Compile with-D DOCA_ALLOW_EXPERIMENTAL_APIby default: most ofdoca_flow.his experimental-tagged, so omitting it is a common first-build failure; it's a compile-time switch, not runtime code.
Track 2 — Other languages (Rust, Go, Python, …)
NVIDIA ships no verified non-C first-app sample and neither does this skill, so the agent does not author the wrapper from prose — it makes the consumer understand the C ABI their wrapper calls and routes the FFI work back to them:
API surface = the public C ABI. Cite the DOCA Flow Programming Guide for behavior/lifecycle and the installed headers (
$(pkg-config --variable=includedir doca-common)/doca_flow*.h) for signatures. A wrapper honors the same lifecycle, capability-gate, and validation rules inCAPABILITIES.md/## test— library properties, not language properties.Bindings / FFI. Point at a community binding only after verifying it exists (fetch its repo — never invent a name); otherwise it is direct FFI (
bindgen,cgo,cffi/ctypes) againstpkg-config --cflags --libs doca-flow. Read the C samples regardless — the order of API calls (e.g.flow_port_fwd/) is language-independent, and## test/## run/## debugapply unchanged.
Route tables — IP routing / longest-prefix match (LPM)
For destination-prefix routing — a route table with mixed prefix lengths
(10.0.0.0/8, 192.168.0.0/16, …) — use an LPM pipe (flow_lpm/
sample). It is purpose-built for longest-prefix match, supports per-entry
counters, and forwards each prefix to its own destination.
Add routes at runtime with
doca_flow_pipe_lpm_add_entry()and remove them withdoca_flow_pipe_remove_entry()— no pipe rebuild and no app restart. Unmatched destinations follow the pipe's miss behaviour; set it toDOCA_FLOW_FWD_DROPfor a default-drop router.LPM cannot be a root pipe — front it with a basic or control pipe.
Batch route changes. The LPM table is recomputed on each flush, so flushing every route separately is slow for a large or rapidly-changing table. Apply route changes as a batch and commit them with a single
doca_flow_entries_process()(see## runstep 3). For a small, static table this is not a concern.
modify
Goal: change an existing pipe — adding, removing, or rewriting entries — without taking the steering plane offline.
Read current pipe statistics and counters before any change; the diff after is what tells the user whether the change did what they meant.
Re-run capability discovery if the action set changes. A new action kind (e.g.
encap) requires re-checking the capability matrix and re-validating against the new shape — see item 3 inCAPABILITIES.md ## Safety policy.Construct a delta spec, not a full re-spec — removing and re-adding a pipe is much heavier than adding/removing entries.
Validate the modified spec via the same path as
## test.Commit in the smallest unit (one entry at a time for live pipes carrying production traffic).
Re-read counters and statistics; confirm the diff matches intent.
run
Goal: program the validated spec into the hardware and observe that traffic does what it should.
Confirm
## testhas passed; do not enterrunfrom an un-validated spec.Confirm the port was started via
doca_flow_port_start(port_cfg, &port)during## configureand the pipe was created (validated) during## test. Pipes have no separate start operation. Lifecycle is port started → pipe created (validated) → entries added; out-of-order calls produceDOCA_ERROR_BAD_STATE.Add entries in the order the user's intent implies (most specific match first when declared priority is not honored; otherwise the priority field). After adding, call
doca_flow_entries_process()to push them to hardware. Its return value is a count, not a success flag:n >= 0is the number of entries whose status callback fired — HWS often completes the install synchronously, son == 0is normal and healthy; onlyn < 0is an error. Treating0as fatal is a common bug.When adding entries in bulk, drive this loop for insertion rate, not just correctness: give each core its own
queueid (sharing one across cores is undefined behavior), add withDOCA_FLOW_ENTRY_FLAGS_WAIT_FOR_BATCHand calldoca_flow_entries_process()once per batch (reserve0/_NO_WAITfor small or latency-sensitive inserts), and spread entries evenly across queues — skew can fragment the action-handle cache and shrink effective pipe capacity.Stage entries on a single representor before widening to all — safety policy item 2 in
CAPABILITIES.md ## Safety policyfor hairpin pipes; the same discipline applies to any high-fanout pipe carrying production traffic.Read counters under expected traffic. If they do not move, jump to
## debug.Keep
main()alive — but only after the datapath is armed. A DOCA Flow program offloads steering to hardware and must stay running — ifmain()returns shortly after init (or sleeps for a fixed time), the rules are torn down and traffic stops. Enter a long-running, signal-aware loop after setup (the shipped sample shape:while (!force_quit) { doca_flow_entries_process(...); sleep(1); }withSIGINT/SIGTERMhandlers), and confirm the loop is reachable frommain()'s actual call graph before the ordered teardown. Before entering it, drivedoca_flow_entries_process()only until every expected status callback fires or the finite deadline copied from the installed shipped sample/application expires. Missing or failed callbacks at that deadline are a hard abort. This bounded callback-drain is not the keep-alive loop and is never retried after its deadline.Keeping
main()alive is NOT success. The keep-alive loop is the last gate, not a substitute for the probe-before-count gate in## configurestep 6 — that gate (probe every device before counting,rte_eth_dev_count_avail()≥ expected, mandatoryport_idplus the mode-appropriate device source) must already have passed, and the binary must exit non-zero rather than loop over a bridge that cannot forward. Two run-time preconditions the loop additionally requires:when two PFs are bridged, the
doca_flow_port_paircalls in BOTH directions returnedDOCA_SUCCESS(doca_flow.h);every
DOCA_FLOW_FWD_PORTentry's status callback fired withDOCA_FLOW_ENTRY_STATUS_SUCCESS(doca_flow.h), and the per-entry counter from## teststarts incrementing under controlled traffic (doca_flow_resource_query_entry,doca_flow.h). A binary that reaches the loop with the gate unmet — count 0, a missingset_port_id/ mode-appropriate device source, a faileddoca_flow_port_pair, or no entry-status confirmation — is forwarding nothing while appearing to run, exactly the shape that lets a watchdog mark a broken binary "alive". It is a hard error; the program must abort. If only onedoca_flow_port_pairdirection succeeds, treat the pair as failed: do not add entries or enter the keep-alive loop, perform ordered teardown, and report both directional return values.
test
Goal: validate a pipe spec — and the system context around it — before hardware programming.
## test is a bounded validation loop, not a one-shot pass. Run the
4 steps in order and, when a cross-check or counter-wiring finding mutates
the spec, loop back to step 1 once — every mutation re-opens validate.
If that second pass finds another required mutation, stop and surface the
unresolved spec/capability mismatch using the loader's REFUSED shape:
name the conflicting capability, the fields changed on each pass, and
the closest legal spec. Do not iterate again. Skipping the
re-validate after the first mutation is exactly the failure mode
CAPABILITIES.md ## Safety policy
validate-before-commit exists to prevent.
Pipe spec validation. Validation is constructor-time: there is no separate
doca_flow_pipe_validateAPI —doca_flow_pipe_createitself rejects an inconsistent spec (DOCA_ERROR_INVALID_VALUE/DOCA_ERROR_NOT_SUPPORTED). Build the pipe with the staged-entry / dry-run pattern from a shipped sample and confirm the constructor returnsDOCA_SUCCESSbefore any entry-add call. This is the "validate before commit" rule fromCAPABILITIES.md ## Safety policy.Capability cross-check. Re-confirm every match kind, action kind, and tunnel header in the spec is supported by the active configuration and firmware. Validation answers "is the spec internally consistent"; this answers "will the hardware accept it". A change here loops back to step 1.
Counter wiring check. Walk the spec and confirm every entry the user wants to observe has a counter attached (
## debugassumes they exist). Adding one loops back to step 1.Negative test. Construct one deliberately failing pipe spec (wrong match kind, unsupported action) and confirm
doca_flow_pipe_createrejects it — the cheapest way to detect a wrong-version Flow library before going live. If it does not reject, escalate to## debug.
debug
Goal: investigate "traffic is not doing what I asked" and arrive at a root cause that is either fixable in the spec or escalatable.
Routing summary. This anchor is the Flow-specific debug overlay: counters, pipe statistics, programmed-state inspection, Flow's
DOCA_ERROR_*mapping. For the cross-cutting debug ladder (install / version / build / link / runtime / program / driver) and its tooling (gdb,valgrind,--sdk-log-level, container-vs-native debug, core dumps, Developer Forum escalation), seedoca-debug ## debug(doca-debug/TASKS.md#debug). Walk the cross-cutting ladder first when the symptom layer is unknown; this Flow overlay layers on top once the symptom is confirmed inside the Flow API.
Step 0 — did the port even start? (placement gate before the counter
ladder). The counter-first ladder below assumes a started port. If
doca_flow_port_start — or the first switch-port doca_flow_pipe_create
— fails with the Failed to get hws cap / dest action ROOT … err -121
signature, stop: this is not a counter / spec / data-plane bug and
none of the steps below apply. It is the device-placement signature (full
details in CAPABILITIES.md ## Error taxonomy):
the opened function has no hardware-steering plane, almost always host-side
on a BlueField in SEPARATED_HOST / NIC mode, and it reproduces on
every device in both vnf and switch modes. Route back to
## configure step 2; the fix is to run Flow on the DPU Arm
side or change the card's mode via
doca-hardware-safety/SKILL.md — never a
pipe-spec edit.
Walk in this order — do not skip steps:
Counters first. Read the entry-level counters (built in
## buildstep 4). If the counter for the suspected entry is zero, the entry is not matching. Stop blaming the data plane; the spec is wrong.Pipe statistics second. If counters are non-zero but behavior is still wrong, read the pipe-level statistics from
CAPABILITIES.md ## Observabilityto determine whether the pipe itself is healthy.What's actually programmed / verbose logs third. To inspect what the hardware actually has programmed, route to the
doca-flow-tune/SKILL.mdsibling skill, which owns the programmed-state surface. For verbose runtime logging during active debugging, build/link the trace flavor (pkg-config doca-flow-trace) and run with--sdk-log-level 70; the runtime mechanics live indoca-debug CAPABILITIES.md ## Observability(doca-debug/CAPABILITIES.md#observability).Error code mapping. Any
DOCA_ERROR_*returned during the investigation routes through the taxonomy inCAPABILITIES.md ## Error taxonomy. The cross-library taxonomy (which family routes to which debug layer) is owned bydoca-programming-guide CAPABILITIES.md ## Error taxonomy(doca-programming-guide/CAPABILITIES.md#error-taxonomy); this Flow file only adds the Flow-specific overlay.Version sanity. If a spec fails or behaves differently across sessions, confirm the installed DOCA version did not change. The four-source version-coherence check (
pkg-config --modversion doca-common,cat /opt/mellanox/doca/applications/VERSION,doca_caps --version, BFB version) is owned bydoca-debug ## debug(doca-debug/TASKS.md#debug) Layer 2; the version-detection mechanics live indoca-public-knowledge-map ## Where to find the version(doca-public-knowledge-map/SKILL.md#where-to-find-the-version). A library upgrade between sessions is a common and easy-to-miss cause.Escalation criteria. If counters move correctly but observed behavior is still wrong AND what's programmed (step 3) matches the spec AND the version is unchanged, the bug is below the Flow API surface (driver or firmware). Stop attempting Flow-spec changes; capture state per
doca-debug ## test(doca-debug/TASKS.md#test) (the read-only triple) and escalate viadoca-debug ## debugWhere to ask for help (doca-debug/TASKS.md#debug) to the public DOCA Developer Forum.
flow-ct
The DOCA-Flow-CT-specific overlay on the parent verbs. Read this section
before the port-start step in ## configure, even when
the requested change is described as adding CT to existing stateless pipes.
For device support, sizing, and safety, read
CAPABILITIES.md ## flow-ct.
Lifecycle to explain before applying the change: doca_flow_init →
doca_flow_ct_cfg_create and the verified doca_flow_ct_cfg_set_* setters →
doca_flow_ct_init(cfg) once per process → port start → CT-aware pipes →
entries. CT is not a per-port doca_ctx; do not call doca_ctx_start for CT.
If ports are already running, this is not an in-place attachment. The
agent does not perform the restart itself; it hands the operator this
sequence and stops until the operator confirms:
(1) snapshot — the pipe definitions are the application's own source
(the doca_flow_pipe_cfg_* / doca_flow_pipe_create calls), so record the
source revision plus doca_flow_query_pipe_miss / doca_flow_query_entry
counter baselines; there is no runtime pipe-dump API to rely on;
(2) approval — the operator explicitly confirms a maintenance window,
because stopping ports drops all traffic on them (follow
doca-hardware-safety ## configure (doca-hardware-safety/TASKS.md#configure));
with no human operator available, return requires_human_decision instead
of proceeding; (3) stop the affected ports, initialize CT, restart, and
recreate the configuration from the recorded source revision and shipped
sample. Do not assume old pipe handles survive.
On shutdown, stop ports before doca_flow_ct_destroy, then
doca_flow_destroy. Verify matching and unrelated traffic after restart;
if the smoke fails, follow this section's rollback overlay.
configure overlay.
Slot CT init BEFORE port start. The order is
doca_flow_init→doca_flow_ct_init(global, one-time) → port start (layering rule inCAPABILITIES.md ## flow-ct). If doca-flow isn't initialized yet, route back to## configureFIRST — CT extends, it does not replace. CT cannot be bolted onto an already-running port: if ports are started, CT init must move earlier.Device-support check for CT. Call
doca_flow_ct_cap_is_dev_supported(devinfo)against the activedoca_devinfo. There is no per-axisdoca_flow_ct_cap_*family for flow count, aging range, NAT variants, or overlays; do not invent one. DOCA 3.5 adds the experimental port capabilities API (doca_flow_port_capabilities_create/_get_ct_ipv6/_destroy, afterdoca_flow_init()and before port start) for CT IPv6 support. If the device does not support CT, surface that and stop.Size the CT module to the user's expected peak, not average. CT entries persist until aging evicts them or policy removes them. Since DOCA 3.4 sizing is per CT pipe:
doca_flow_pipe_cfg_set_ct_connections(),doca_flow_pipe_cfg_set_ct_max_connections_per_zone(), anddoca_flow_pipe_cfg_set_ct_dup_filter_size()on the CT pipe cfg (notdoca_flow_ct_cfg_set_*), sized to the peak concurrent-flow estimate; oversubscription surfaces asDOCA_ERROR_FULL/_NO_MEMORYat runtime.Build and init the CT cfg. Create
struct doca_flow_ct_cfgviadoca_flow_ct_cfg_create+ thedoca_flow_ct_cfg_set_*setters, then calldoca_flow_ct_init(cfg)once for the process (ordering per step 1; there is NO per-port CT context).Configure NAT direction / actions through
doca_flow_ct_cfg_set_directionand the CT actions. There is no per-variant cap query, so confirm SNAT / DNAT / combined behavior only on a dedicated test representor/VF or isolated ports carrying no production traffic. Snapshot counters first and stage one entry; an unsupported request surfaces as_NOT_SUPPORTEDat entry add. Never probe a NAT variant on a live production port.Start the ports, then attach CT-aware pipes. With the global CT module initialized, start the doca-flow ports and wrap existing doca-flow pipes with their CT-aware versions. The original stateless pipes remain valid; CT-aware pipes are added on top. CT is not a
doca_ctx; there is nodoca_ctx_starton a CT object.
build overlay.
| Slot | Value |
|---|---|
| pkg-config modules | CT adds no separate module: keep every companion module required by the parent Flow/DPDK build and include doca-flow; there is no doca-flow-ct module |
| Version anchor | pkg-config --modversion doca-flow MUST equal doca_caps --version. Mismatch → escalate to doca-version TASKS.md ## debug (doca-version/TASKS.md#debug) layer 2 BEFORE diagnosing the CT layer itself |
| Header includes | Add doca_flow_ct.h to the doca-flow headers the parent ## build prescribes; it ships in the same doca-sdk-flow devel package |
.pc discovery | `pkg-config --list-all |
modify overlay. Take the closest shipped CT sample (an installed CT sample in the public DOCA samples bundle whose 5- tuple shape, NAT requirement, and overlay encapsulation match the user's workload), apply a minimum-diff onto the user's existing doca-flow setup:
Do NOT recreate the user's pipe scheme from scratch — port the sample's CT bookkeeping (context creation, aging-table sizing, CT pipe-builder wrap calls) onto the existing flow.
There is no per-variant or per-overlay CT cap-query — the only CT cap symbol is
doca_flow_ct_cap_is_dev_supported(devinfo). Confirm new NAT variants (SNAT, DNAT, combined) and overlay shapes empirically against the shipped CT sample, not via fabricateddoca_flow_ct_cap_*axes.After modify: rely on
doca_flow_pipe_create's constructor-time validation (any spec inconsistency surfaces as a constructor failure withDOCA_ERROR_INVALID_VALUEorDOCA_ERROR_NOT_SUPPORTED) plus the staged-entry / dry-run pattern from the shipped CT sample. There is no separatedoca_flow_pipe_validatesymbol in the public surface — do not invent it.
run / test overlay. Per
CAPABILITIES.md ## flow-ct:
Single-flow CT smoke. ONE 5-tuple, ONE CT entry add, ONE matching traffic flow, ONE counter increment. The parent stateless pipe must already pass constructor-time validation (
## teststep 1) and its counter smoke (## runstep 5) first.Multi-flow smoke ONLY after single-flow is green: a small set of distinct 5-tuples (e.g. 16), one entry per flow, confirm per-CT-entry counters increment in lockstep with the matching traffic. All CT smokes below use the isolated test path selected in configure step 5, never production traffic.
Aging smoke with a deliberately short aging timer accepted by the installed CT configuration setter/sample: add a CT entry, send one matching packet, stop traffic, wait at least the aging period plus granularity, confirm the entry is evicted via the per-CT-entry counter query.
NAT-aware smoke (one requested variant at a time): add an entry whose SNAT, DNAT, or combined action is accepted by the installed sample/API, confirm the outbound traffic carries the rewritten 5-tuple, confirm reverse traffic is matched on the original tuple.
Negative tests the agent should propose explicitly: pass one value documented as invalid by the installed CT header/sample and expect
_INVALID_VALUE; request one action shape rejected by that installed API and expect_NOT_SUPPORTED. Exercise_FULLonly in an isolated test sized to the configured table, never by guessing a device maximum or filling a live table.Sustained-run loop ONLY after all four smokes are green: stream traffic that exercises CT entry add / aging eviction in a continuous loop while watching per-CT-entry counters, per-entry aging timestamps, and connection-state transitions per
CAPABILITIES.md ## flow-ct. This is the only stage where the agent should propose running the user's full workload.
debug overlay. Layered on the parent's ## debug
ladder:
DOCA_ERROR_BAD_STATEfrom a CT-layer call is always a layering / lifecycle violation:doca_flow_ct_initrun after a port was already started (it must precede port start), OR a CT entry add before the ports and wrapped pipes are up, ORdoca_flow_ct_destroycalled out of order. Walk the lifecycle in this order — doca_flow_init → doca_flow_ct_init → port-start → ct-entry-add → … → port-stop → doca_flow_ct_destroy → doca_flow_destroy — BEFORE inspecting any individual CT call.DOCA_ERROR_FULLon entry add is always a table-sizing / aging-pressure mismatch with the workload. Read the per-CT- entry counters to identify stale entries; either wait for one aging interval, then evict one confirmed-stale entry if policy permits. Retry the failed add once. If it is still full, stop and surface the configured-table/device-fit gap; do not continue an eviction/retry loop.Traffic not matching a freshly-added CT entry is almost always a 5-tuple-shape disagreement between the entry add and the traffic on the wire. Read both sides verbatim — the cap-query is NOT the right diagnostic here; the entry shape vs traffic shape comparison is. Same shape as the parent “pipe matches nothing” diag in
## debug, with CT's 5-tuple match instead of an arbitrary pipe match.NAT translation conflicts surface as
DOCA_ERROR_INVALID_VALUEon entry add. PerCAPABILITIES.md ## flow-ct, do NOT invent a translation to resolve the conflict — surface the policy conflict to the user.An aging value rejected by the installed CT configuration setter surfaces as
DOCA_ERROR_INVALID_VALUEat context configure (NOT at entry add). Quote the accepted constraints from the installed header/sample and the setter return; there is no separate numeric cap-range query.
rollback overlay. A CT add is a pipeline-edit-class mutation (it extends
an already-up stateless port; it does not touch firmware or eswitch mode), so
the doca-hardware-safety/SKILL.md overlay does
NOT fire — CT follows the same snapshot-first rollback discipline as
## rollback, with a CT-specific reversal. Snapshot before
mutate: record the stateless pipe scheme the agent authored (names, root
status, match/action specs, monitor/counter attachment, miss-pipe linkage)
plus the pre-CT per-entry / per-pipe-miss counter baseline
(doca_flow_resource_query_entry / doca_flow_resource_query_pipe_miss);
that record IS the rollback target.
Document the reversal in the same answer that recommends the CT add: (a)
doca_flow_ct_rm_entryon every CT entry added since the snapshot, in reverse-add order; (b)doca_flow_pipe_destroyon every CT-aware pipe wrapped on top of the stateless pipes, in reverse-create order; (c) once the ports are stopped,doca_flow_ct_destroy()(global, no arguments) BEFOREdoca_flow_destroy. There is no per-port CT context and nodoca_ctx_stopfor CT. The original stateless pipes are untouched by entry / CT-pipe teardown and must remain valid; a stateless-pipe edit made in addition to the CT add needs its own rollback step first.Trigger and re-verify per
## rollbacksteps 2–3, with the single-flow CT smoke (run/test overlay step 1) as the contract's smoke probe: walk the rollback if it is not green within the bounded debug-loop iteration (mandatory on the second non-green), then re-verify the restored stateless pipe under traffic against the snapshot shape.
shared-resources
The verbatim API call sequence for using DOCA Flow's shared-resource
pools (encap / decap / counter / meter / RSS / IPsec-SA / PSP). For the
concept — when to reach for shared, the SHARED-vs-NON_SHARED
decision, the kinds table, the broken-hybrid scaling failure, and the
many-flows / few-distinct-tunnels sizing rule — see
CAPABILITIES.md ## Shareable resources.
This section is the implementation overlay; the layering rule is (a)
decide via that section first, (b) walk the four steps below verbatim,
(c) pkg-config --modversion doca-flow matches the install per
## configure step 1.
The 4-step pattern (identical for every kind — the example uses
ENCAP; substitute the matching kind from the table):
/* 1. Reserve K slots BEFORE doca_flow_init() — count first, then type.
* DEPRECATED in DOCA 3.5 (removed in 4.0; links with deprecation warnings,
* DOCA_ALLOW_DEPRECATED_API silences them). For counters / meters prefer
* per-port resource mode (experimental): see ## build step above. */
doca_flow_cfg_set_nr_shared_resource(cfg, K, DOCA_FLOW_SHARED_RESOURCE_ENCAP);
/* 2. Populate each slot after init (one call per distinct config). */
doca_flow_shared_resource_set_cfg(DOCA_FLOW_SHARED_RESOURCE_ENCAP, id, &res_cfg);
/* (port-scoped form: doca_flow_port_shared_resource_set_cfg(port, type, id, &res_cfg)) */
/* 3. Bind the slot range to the port once at startup.
* DEPRECATED in 3.5 (removed in 4.0); in per-port resource mode use
* doca_flow_port_shared_resource_get/put() instead (experimental). */
uint32_t ids[K] = { 0, 1, /* … */ K - 1 };
doca_flow_shared_resources_bind(DOCA_FLOW_SHARED_RESOURCE_ENCAP, ids, K, port);
/* 4. Reference from each entry: set the type marker AND the id.
* The id field is kind-specific (confirm in doca_flow.h): */
actions.encap_type = DOCA_FLOW_RESOURCE_TYPE_SHARED;
actions.shared_encap_id = slot_id; /* encap */
/* decap: actions.decap_type = ...SHARED; actions.shared_decap_id = slot_id; */
/* counter: monitor.counter_type = ...SHARED; monitor.shared_counter.shared_counter_id = slot_id; */
/* meter: monitor.meter_type = ...SHARED; monitor.shared_meter.shared_meter_id = slot_id; */Reference samples: flow_vxlan_shared_encap/, flow_shared_meter/,
flow_shared_counter/ under /opt/mellanox/doca/samples/doca_flow/ —
the agent reads the relevant sample before quoting the per-entry field
names (the actions.shared_encap_id slot is kind-specific and the
header is the source of truth, per
SKILL.md ## Ground rule).
Per-entry HW-cost reminder. With this overlay the HW resource cost
scales with K distinct configs, not N entries. Skipping any of
the four steps but writing the entry-side *_type = DOCA_FLOW_RESOURCE_TYPE_SHARED line compiles cleanly and dies at the
first entry-add (the broken-hybrid failure mode in
CAPABILITIES.md ## Shareable resources).
rollback
Every Flow pipe / entry / action add that modifies an already-up port
is pipeline-edit-class and needs the same rollback discipline as CT.
This anchor covers stateless-pipe edits: VLAN push/pop, encap/decap,
modify-header, NAT-without-CT, hairpin attach. (CT
additions route through ## flow-ct rollback overlay
instead.)
The
universal verification contract (doca-setup/CAPABILITIES.md#universal-verification-contract)
requires “the rollback path is documented” on every change-recommending
answer.
Snapshot before mutate. Before any change-recommending edit on an
already-up port, record (a) the pipe scheme the agent authored for every
affected pipe (root status, match / action specs, monitor/counter
attachment, miss-pipe linkage), (b) the per-pipe counter baseline
(doca_flow_resource_query_pipe_miss + doca_flow_resource_query_entry),
and (c) the device cap snapshot the edit gated on (doca_caps --list-devs
Flow cap query results). For an independent view of what is actually programmed, route to
doca-flow-tune/SKILL.md. That record IS the rollback target.
Document the reverse-edit verb in the same answer that recommends the edit, by action kind: (a) entry add → matching
doca_flow_pipe_remove_entryin reverse-add order; (b) action add/modify (VLAN push, encap, modify) →doca_flow_pipe_destroyon the modified pipe + recreate from the recorded pipe spec; (c) pipe add →doca_flow_pipe_destroyin reverse-create order; (d) monitor / counter attach —doca_flowexposes no publicdoca_flow_monitor_destroy, so the reversal isdoca_flow_pipe_destroydoca_flow_pipe_createwith adoca_flow_pipe_cfgthat does NOT calldoca_flow_pipe_cfg_set_monitor.
Trigger from the deploy-loop bridge (
doca-setup/CAPABILITIES.md#deploy-loop-bridge--step-5-not-green-is-the-debug-loop-trigger). The contract's smoke probe is constructor-time validation (## teststep 1) plus a counter read under traffic (## runstep 5). If it does not return green (counter increment equal to the controlled generator's successfully transmitted packet count on the isolated lossless path + cleandoca_flow_pipe_createreconstruction) within the bounded debug-loop iteration, walk the rollback — on the second non-green iteration, rollback is mandatory.After rollback, re-verify the restored pipe — constructor-time validation (
## teststep 1) plus a counter re-read under traffic (## runstep 5). If counters do not resume the snapshot shape, the rollback itself is suspect — surface the residual gap instead of recommending another pipeline edit.
Hardware changes (mode flip, SR-IOV, firmware) are NOT pipeline edits and
route through
doca-hardware-safety/SKILL.md's rollback
discipline instead.
Command appendix
Flow-specific commands the verbs above reach for, grouped by purpose
so the agent picks the right family without searching prose. Every row
is a class — the agent must not invent flags beyond what the row
names; the flag-discovery rule is --help against the installed
binary or pkg-config against the installed .pc, not prose recall.
| Purpose | Command | Owning step | Reads as healthy when … |
|---|---|---|---|
| Discover installed Flow version | pkg-config --modversion doca-flow | ## configure step 1 | Matches the version pinned in other places (doca_caps --version, applications/VERSION). |
| Discover Flow link flags (release flavor) | pkg-config --cflags --libs doca-flow doca-common doca-dpdk-bridge libdpdk | ## build | Returns the canonical -l list. doca-flow alone omits the companion DSOs → undefined reference / DSO missing from command line; pass every module the app calls. Hand-typed -l lines are the failure mode. |
| Discover device + steering capabilities | doca_caps --list-devs | ## configure step 2 + doca-caps/SKILL.md | Lists every device DOCA sees with the active configuration and supported match / action kinds. |
| Enumerate ports / representors | devlink dev show + the installed Flow port-enumeration sample | ## configure step 3 | Shows the port and every representor the user expects to forward to. |
| Validate a pipe spec (read-only) | doca_flow_pipe_create itself performs constructor-time validation; treat its DOCA_ERROR_INVALID_VALUE / DOCA_ERROR_NOT_SUPPORTED return as the validate signal. There is no separate doca_flow_pipe_validate public symbol in the current release. Use the dry-run / staged-entry pattern from the shipped sample for a fuller validation surface. | ## test step 1 | A successful return from doca_flow_pipe_create means the spec is internally consistent against the current device caps; an error means the spec is invalid — do not retry without changing the spec. |
| Raise log verbosity for a Flow run | --sdk-log-level 70 on the program command line | ## run + doca-debug CAPABILITIES.md ## Observability (doca-debug/CAPABILITIES.md#observability) | TRACE / DEBUG lines appear in stderr; the Flow lifecycle calls are visible. |
| Read per-entry / per-pipe counters | doca_flow_resource_query_entry() / doca_flow_resource_query_pipe_miss() | ## debug step 1 | Counter for the suspected entry is non-zero under expected traffic. |
| Inspect what's actually programmed | route to doca-flow-tune/SKILL.md | ## debug step 3 | The programmed state matches the user's mental model of the pipe. Diff = bug. |
Three cross-cutting rules for this appendix:
Never invent Flow flags. The Flow API is large and version-gated;
--helpon the installed binary andpkg-config --list-all | grep docaare the only safe sources.Never paraphrase a Flow
DOCA_ERROR_*. Quotedoca_error_get_descr()verbatim — the layer-classifier indoca-debug ## debug(doca-debug/TASKS.md#debug) layer 5 needs the exact text.Cross-cutting commands live in doca-debug. The read-only triple,
dmesg, andmlxconfig -d <pcie> qare indoca-debug TASKS.md ## Command appendix(doca-debug/TASKS.md#command-appendix); reach there for them rather than restating them in this appendix.
Deferred task verbs
The following verbs are out of scope for this skill but are commonly asked in the same conversations. Route them as follows so the agent does not invent guidance:
install. Installing DOCA, choosing packages, post-install verification,
pkg-configwiring — defer to doca-public-knowledge-map ## Layout of an installed DOCA package (doca-public-knowledge-map/SKILL.md#layout-of-an-installed-doca-package). This skill assumes DOCA is already installed.deploy. Deploying BlueField images, provisioning DPUs at scale, Kubernetes operator workflows — out of scope for this skill. Point the user at the DOCA Platform Framework entry in
doca-public-knowledge-mapand stop there.rollback. Coordinated steering-plane rollback across multiple DPUs and host nodes — out of scope for this skill. For single-DPU spec rollback within a session, use the snapshot-first
## rollbackworkflow. Its reverse edit may be a## modifydelta, but only against the captured baseline and with the bounded re-verification defined there.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
The author says: Requires DOCA SDK installed at /opt/mellanox/doca on Linux (Ubuntu 22.04/24.04 or RHEL/SLES) with a supported NVIDIA NIC/DPU attached. Reads the user's local install via `pkg-config doca-flow` and inspects /opt/mellanox/doca/{lib,include,samples,applications}. The validation steps require a live DOCA install.
Security
What our scanner noticed· Medium risk
- Large base64-encoded blobs
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- NVIDIA
- Publisher
- NVIDIA (Official)
- Source
- NVIDIA/skills · skills/doca-flow · commit fce3cca
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE-APACHE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by NVIDIA or NVIDIA.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download doca-flow.zip