These docs are under active development and cover the v0.20 Kobicha security model.
On this page
reference 2 min read

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.

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.