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 realised (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() #
Pull the cable: take the VM's TAP off the bridge and issue a QMP set_link(false) against the matching netdev, so the guest sees its carrier go and nothing reaches it. The TAP itself is kept, because QEMU holds it open as the NIC's backend, and the VM stays a member of the bridge. Without a VM userdata at construction time (bare-string bridge:nic("name") form), the QMP step is skipped. On a bridge that is not realised yet there is no TAP, and the call is graph-state only: the VM is detached from the bridge.
Use bridge:detach(vm) to take a VM off a bridge for good: it deletes the TAP, so the NIC has nothing to talk through for the rest of the VM's life.
nic:reconnect() #
Reverse of :disconnect(). Plugs the TAP back into the bridge, brings it up, and issues a set_link(true), so the guest sees its carrier return.
nic:vm_name() / nic:bridge() #
Accessors. :vm_name() returns the VM's name; :bridge() returns the bridge's name.
Example: link-down recovery #
test
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.