On this page
Nic
A Nic represents one virtual NIC: the binding between a VM and a Bridge. You get one from either side of that pair (vm:nic(bridge_name) or bridge:nic(vm)), and it gives you per-NIC observability and link control.
Constructing
| Source | Returns |
|---|---|
vm:nic("bridge_name") |
NIC for the named bridge attachment. |
vm:nic("eth0") |
NIC for the Nth attached bridge sorted by bridge name (eth<N>, enp0s<N>, ens<N>). |
bridge:nic(vm) |
NIC for the (bridge, vm) pair. When vm is a userdata, the link-state ops drive QMP. |
The guest-name lookup (eth0) is deterministic per build but may not match the kernel's actual device-probe order on every distro. For portable tests, prefer vm:nic("bridge_name") and bridge:nic(vm).
Methods
nic:counters()
Returns a table of per-NIC counters read from /sys/class/net/<tap>/statistics/. The counters are presented from the guest's perspective: the host's tx_bytes on the TAP is what the guest received, so it appears as rx_bytes in the table.
| Field | Description |
|---|---|
rx_bytes |
Bytes the guest received (host TAP's tx_bytes). |
tx_bytes |
Bytes the guest sent (host TAP's rx_bytes). |
rx_packets |
Packets received. |
tx_packets |
Packets sent. |
errors |
Sum of host TAP's rx_errors and tx_errors. |
Returns zeros when the host TAP isn't yet realized (VM not booted). If you need to assert a NIC has produced traffic, gate the read on vm:state() == "booted" or wait_until on a non-zero counter.
nic:capture()
Spawn tcpdump -i <tap> on this NIC's host TAP interface. Returns a Capture stream. Errors if the VM hasn't been booted yet (the per-VM TAP doesn't exist):
nic:capture: vm `a` has no TAP on bridge `lan` (not booted?). Call lab:boot() / vm:boot() first.
The capture pins the bridge's active_captures counter, so vm:snapshot() can detect a live capture and refuse the snapshot (no half-captured pcap).
Compare with bridge:capture() which captures the whole bridge, not a single NIC.
nic:disconnect()
Detach the NIC from the bridge and issue a QMP set_link(false) against the matching netdev so the guest sees a link-down event. Without a VM userdata at construction time (bare-string bridge:nic("name") form), the QMP step is skipped — the call is graph-state only.
nic:reconnect()
Reverse of :disconnect(). Re-attaches the NIC and issues a set_link(true).
nic:vm_name() / nic:bridge()
Accessors. :vm_name() returns the VM's name; :bridge() returns the bridge's name.
Example: link-down recovery
test("guest reconnects after link flap", function(t)
local lan = provium:bridge("lan")
local a = provium:vm("a", "peios"):boot()
local b = provium:vm("b", "peios"):boot()
lan:attach({a, b})
-- Baseline.
a:run("ping -c 1 -W 1 b.lan"):assert_ok()
local nic = a:nic("lan")
nic:disconnect()
-- Pings should fail now.
local r = a:run("ping -c 1 -W 1 b.lan")
t:assert_eq(r.exit_code, 1)
nic:reconnect()
-- And succeed after reconnect.
a:run("ping -c 1 -W 1 b.lan"):assert_ok()
end)
See also
- Bridge — bridge-wide controls (partition, impairments, capture).
- Streams — what
nic:capture()returns. - Bridges and impairments — patterns for using NIC and bridge handles together.