| name | fabric-programmable-networking |
| description | Provision P4 Tofino switches, DPDK SmartNICs, BlueField DPUs, and Open vSwitch on FABRIC |
| allowed-tools | ["Read","Grep","Glob","Write","Edit","Bash"] |
Instructions
When invoked, generate code for programmable networking on FABRIC. Four main capabilities:
- P4 Tofino — Barefoot Tofino programmable switches with SDE compilation and table population
- DPDK SmartNIC — High-performance packet processing with Mellanox ConnectX NICs
- BlueField DPU — NVIDIA BlueField-3 Data Processing Units with RShim management
- Open vSwitch (OVS) — Software-defined switching with STP support
Help the user choose the right approach and generate complete setup code.
API Reference
P4 Switch
switch = slice.add_switch(name="p4-switch", site=site)
iface1 = switch.get_interfaces()[0]
iface2 = switch.get_interfaces()[1]
switch.execute(command=[
("command", r"expected_prompt_regex", timeout_seconds),
])
port = switch.get_interfaces()[0].get_device_name()
NIC Models for Programmable Networking
model = 'NIC_Basic'
model = 'NIC_ConnectX_5'
model = 'NIC_ConnectX_6'
model = 'NIC_ConnectX_7_100'
model = 'NIC_ConnectX_7_400'
Hardware Info Methods
pci_addr = iface.get_component().get_pci_addr()[0]
mac_addr = iface.get_mac()
os_iface = iface.get_physical_os_interface_name()
dev_name = iface.get_device_name()
BlueField DPU Configuration
bluefield = node.get_component(name='nic1')
output = bluefield.configure()
output = bluefield.configure(commands=['cmd1', 'cmd2'])
CPU/NUMA Tuning
node.pin_cpu(component_name='nic1')
node.numa_tune()
Patterns
P4 Tofino Switch with L2 Network
from fabrictestbed_extensions.fablib.fablib import FablibManager
fablib = FablibManager()
site = fablib.get_random_site()
slice = fablib.new_slice(name="p4-experiment")
net1 = slice.add_l2network(name="net1")
net2 = slice.add_l2network(name="net2")
node1 = slice.add_node(name="host1", site=site, cores=4, ram=16, disk=50)
iface1 = node1.add_component(model="NIC_Basic", name="nic1").get_interfaces()[0]
iface1.set_mode("auto")
net1.add_interface(iface1)
node2 = slice.add_node(name="host2", site=site, cores=4, ram=16, disk=50)
iface4 = node2.add_component(model="NIC_Basic", name="nic1").get_interfaces()[0]
iface4.set_mode("auto")
net2.add_interface(iface4)
switch = slice.add_switch(name="p4-switch", site=site)
sw_iface1 = switch.get_interfaces()[0]
sw_iface2 = switch.get_interfaces()[1]
net1.add_interface(sw_iface1)
net2.add_interface(sw_iface2)
slice.submit()
P4 Compile and Run
switch = slice.get_node(name="p4-switch")
switch.upload_directory("P4_labs", ".")
stdout, stderr = switch.execute(command=[
("sde-env-9.13.3", r"\[nix\-shell\(SDE\-9.13.3\):.*\$ ", 10),
("p4_build.sh P4_labs/lab1/p4src/basic.p4", r"\[nix\-shell\(SDE-9.13.3\):.*\$ ", 20),
("exit", r"\[nix\-shell\(SDE-9.13.3\):.*\$ ", 10),
])
thread = switch.execute_thread(command=[
("sde-env-9.13.3", r"\[nix\-shell\(SDE\-9.13.3\):.*\$ ", 10),
("sudo $SDE_INSTALL/bin/./bf_kdrv_mod_load $SDE_INSTALL", r"\[nix\-shell\(SDE-9.13.3\):.*\$ ", 20),
("run_switchd.sh -p basic", r"bfshell>", 30),
("ucli", r"bf-sde>", 10),
("pm port-add 1/- 100G NONE", r"bf-sde>", 10),
("pm port-add 2/- 100G NONE", r"bf-sde>", 10),
("pm port-enb 1/-", r"bf-sde>", 10),
("pm port-enb 2/-", r"bf-sde>", 10),
("pm show", r"bf-sde>", 10),
("sleep infinity", r"bf-sde>", 300),
], output_file="run_switchd.log")
P4 Table Population via bfrt-python
INGRESS = switch.get_interfaces()[0].get_device_name()
EGRESS = switch.get_interfaces()[1].get_device_name()
switch.execute(
f"sed -i 's/ingress_logical *= *[0-9]\\+/ingress_logical = {INGRESS}/' "
f"P4_labs/lab1/bfrt_python/setup.py"
)
switch.execute(
f"sed -i 's/egress_logical *= *[0-9]\\+/egress_logical = {EGRESS}/' "
f"P4_labs/lab1/bfrt_python/setup.py"
)
stdout, stderr = switch.execute(command=[
("sde-env-9.13.3", r"\[nix\-shell\(SDE\-9.13.3\):.*\$ ", 10),
("run_bfshell.sh --no-status-srv -b ~/P4_labs/lab1/bfrt_python/setup.py",
r"\[nix\-shell\(SDE\-9.13.3\):.*\$ ", 10),
])
DPDK SmartNIC Setup
site = fablib.get_random_site(
filter_function=lambda x: x['nic_connectx_6_available'] > 0
)
slice = fablib.new_slice(name="dpdk-experiment")
net = slice.add_l3network(name="data-net", type="IPv4")
l3fwd = slice.add_node(name="l3fwd", site=site, cores=8, ram=32, disk=50)
l3fwd.add_fabnet()
iface1 = l3fwd.add_component(model="NIC_ConnectX_6", name="nic1").get_interfaces()[0]
iface1.set_mode("auto")
net.add_interface(iface1)
pktgen = slice.add_node(name="pktgen", site=site, cores=8, ram=32, disk=50)
pktgen.add_fabnet()
iface2 = pktgen.add_component(model="NIC_ConnectX_6", name="nic1").get_interfaces()[0]
iface2.set_mode("auto")
net.add_interface(iface2)
for n in [l3fwd, pktgen]:
n.add_post_boot_upload_directory('node_tools', '.')
n.add_post_boot_execute('chmod +x node_tools/*')
n.add_post_boot_execute('sudo node_tools/install.sh')
slice.submit()
DPDK IOMMU and Hugepages
for n in slice.get_nodes():
n.execute(
f"sudo node_tools/grub.sh {n.get_ram()}",
quiet=True,
output_file=f"logs/{n.get_name()}-grub.log",
)
n.execute(
"sudo node_tools/apply_vfio_settings.sh",
quiet=True,
output_file=f"logs/{n.get_name()}-vfio.log",
)
for n in slice.get_nodes():
n.execute("sudo reboot")
slice.wait_ssh(timeout=360, interval=10, progress=True)
DPDK Build and Run
execute_threads = {}
for n in slice.get_nodes():
execute_threads[n] = n.execute_thread(
"node_tools/dpdk.sh",
output_file=f"logs/{n.get_name()}-dpdk.log",
)
for n, thread in execute_threads.items():
stdout, stderr = thread.result()
l3fwd_iface = l3fwd.get_interface(network_name="data-net")
pktgen_iface = pktgen.get_interface(network_name="data-net")
print(
f"cd dpdk-23.07 && sudo ./build/examples/dpdk-l3fwd "
f"-a {l3fwd_iface.get_component().get_pci_addr()[0]} -c 0x1 -n 4 "
f"-- -p 0x1 --config=\"(0,0,0)\" "
f"--eth-dest=0,{pktgen_iface.get_mac().lower()}"
)
print(
f"sudo ./Builddir/app/pktgen "
f"-a {pktgen_iface.get_component().get_pci_addr()[0]} -l 0-4 -n 3 -- -P -m [1:3].0"
)
BlueField-3 DPU
site = fablib.get_random_site(
filter_function=lambda x: x['nic_connectx_7_available'] > 0
)
slice = fablib.new_slice(name="bluefield-experiment")
node = slice.add_node(
name="bf-node", site=site, cores=8, ram=32, disk=100, image="dpu_ubuntu_24"
)
dpu = node.add_component(model="NIC_ConnectX_7_400", name="nic1")
iface1 = dpu.get_interfaces()[0]
iface2 = dpu.get_interfaces()[1]
net = slice.add_l2network(name="data-net")
iface1.set_mode("auto")
net.add_interface(iface1)
slice.submit()
node = slice.get_node(name="bf-node")
bluefield = node.get_component(name="nic1")
output = bluefield.configure()
node.execute("sudo ip addr add 192.168.100.1/24 dev tmfifo_net0")
node.execute("sudo ip link set tmfifo_net0 up")
import ipaddress
ip = ipaddress.ip_address(node.get_management_ip())
if ip.version == 4:
node.execute("sudo ./node_tools/bf3_rshim.sh --mode ipv4")
else:
node.execute("sudo ./node_tools/bf3_rshim.sh --mode ipv6")
Open vSwitch Deployment
site = fablib.get_random_site()
slice = fablib.new_slice(name="ovs-experiment")
bridge = slice.add_node(
name="bridge",
site=site,
cores=4,
ram=8,
disk=50,
host=f"{site.lower()}-w1.fabric-testbed.net",
)
net1 = slice.add_l2network(name="net1")
net2 = slice.add_l2network(name="net2")
br_iface1 = bridge.add_component(model="NIC_Basic", name="nic1").get_interfaces()[0]
net1.add_interface(br_iface1)
br_iface2 = bridge.add_component(model="NIC_Basic", name="nic2").get_interfaces()[0]
net2.add_interface(br_iface2)
host1 = slice.add_node(
name="host1",
site=site,
cores=4,
ram=8,
disk=50,
host=f"{site.lower()}-w2.fabric-testbed.net",
)
h1_iface = host1.add_component(model="NIC_Basic", name="nic1").get_interfaces()[0]
net1.add_interface(h1_iface)
host2 = slice.add_node(
name="host2",
site=site,
cores=4,
ram=8,
disk=50,
host=f"{site.lower()}-w2.fabric-testbed.net",
)
h2_iface = host2.add_component(model="NIC_Basic", name="nic1").get_interfaces()[0]
net2.add_interface(h2_iface)
slice.submit()
bridge = slice.get_node(name="bridge")
bridge.execute("sudo apt-get update && sudo apt-get -y install openvswitch-switch openvswitch-common net-tools")
bridge.execute("sudo ovs-vsctl add-br br0")
for iface in bridge.get_interfaces():
os_name = iface.get_physical_os_interface_name()
bridge.execute(f"sudo ovs-vsctl add-port br0 {os_name}")
bridge.execute(f"sudo ifconfig {os_name} 0")
bridge.execute("sudo ifconfig br0 up")
bridge.execute("sudo ovs-vsctl set bridge br0 stp_enable=true")
stdout, _ = bridge.execute("sudo ovs-appctl stp/show")
print(stdout)
host1 = slice.get_node(name="host1")
os_iface = host1.get_interfaces()[0].get_physical_os_interface_name()
host1.execute(f"sudo ip link set dev {os_iface} up")
host1.execute(f"sudo ip addr add 10.10.10.1/24 dev {os_iface}")
host2 = slice.get_node(name="host2")
os_iface2 = host2.get_interfaces()[0].get_physical_os_interface_name()
host2.execute(f"sudo ip link set dev {os_iface2} up")
host2.execute(f"sudo ip addr add 10.10.10.2/24 dev {os_iface2}")
stdout, _ = host1.execute("ping -c 5 10.10.10.2")
print(stdout)
Important Notes
- P4 Tofino: Uses interactive
execute(command=[...]) with regex prompt matching
- DPDK: Requires IOMMU and hugepages (needs reboot after GRUB changes)
- BlueField DPU: Use
dpu_ubuntu_24 image; RShim interface at 192.168.100.1/24
- OVS with NIC_Basic: Bridge and host nodes must be on different physical hosts (SR-IOV constraint)
- CPU pinning: Use
node.pin_cpu() and node.numa_tune() for DPDK workloads
- Parallel execution: Use
node.execute_thread() for long-running installs