Two projects, a clear integration boundary
The companion source lives at thebsdbox/kube-vm-switch, not inside this checkout. This chapter describes its implemented TCX dataplane and how a VM's TAP can serve as a port.
--tap or --nat; it does not register ports with switchd and has no --switch flag. Switch commands below are manual companion-project operations, not an end-to-end VM launch recipe.A conventional learning bridge observes source MACs and floods unknown destinations. This switch instead uses explicit registrations: a controller owns each guest MAC-to-interface mapping. Unknown unicast from the uplink stays on its normal host/wire path; it is not flooded to guests.
Control messages do not carry Ethernet frames
- The local controller registers a stable NIC ID, existing interface and actual guest MAC. switchd validates the port and programs BPF maps and TCX attachments.
- Guest A's frame enters TAP A ingress. After checking source MAC ownership, a destination lookup redirects it to TAP B egress, toward guest B.
- An uplink frame addressed to registered MAC A is redirected to TAP A egress. The VMM reads that TAP and supplies the frame to guest A's virtio RX queue.
- A guest broadcast is cloned to the other registered guests and into host uplink ingress; the original goes to uplink egress. Reserved bridge-control multicast is excluded.
Forwarding is deliberately asymmetric
Guest port ingress
The source MAC must match the port's registration. A known guest destination redirects locally; a frame to the uplink's MAC enters the host stack; other unicast leaves through the uplink. Self-directed hairpin traffic is dropped. Guest VLAN-tagged frames and reserved bridge-control multicast are also dropped.
Uplink ingress and egress
Both hooks can deliver frames addressed to registered guests. Broadcast/multicast is cloned to active guest ports while the original continues its normal path. Frames addressed to the host uplink MAC remain with the host, and unknown unicast is not replicated to guest ports. Tagged uplink traffic stays on its normal host/wire path.
Bounded maps, not a learning FDB
The maps track interface → permitted source MAC, destination MAC → interface, and up to 128 registered ports for replication. A reserved packet-mark bit prevents reinjection/reflood loops. There is no dynamic MAC learning, learned-entry aging or broadcast rate limiter in this design.
Operate the companion switch
Build in an already available kube-vm-switch source directory using its make all target. It produces the daemon, CLI and BPF object. Use a supported Linux kernel with TCX/eBPF and the privileges required to load programs and configure interfaces.
On a disposable or carefully validated host, replace eno1 with an already-UP, unenslaved Ethernet uplink:
sudo ./bin/kube-vm-switchd \
--uplink eno1 \
--bpf-object ./build/switch.bpf.o \
--manage-promisc
--manage-promisc explicitly permits managing uplink promiscuity. Without it the interface must already be promiscuous. Confirm your physical network allows guest MAC addresses behind that uplink.
For an existing, UP, unenslaved TAP whose MTU does not exceed the uplink's, register the actual guest Ethernet MAC:
sudo ./bin/kube-vm-switchctl \
--socket /run/kube-vm-switch/control.sock \
register --id vm123.eth0 --interface tap123 \
--mac 02:00:00:00:01:23
sudo ./bin/kube-vm-switchctl list
sudo ./bin/kube-vm-switchctl remove --id vm123.eth0
These sample names and MAC must match your prepared guest and port. Global --socket goes before the subcommand. Registration does not create TAPs, configure IP addresses, provide DHCP or launch VMs. A host-side veth can also be registered as a port.
The control API exposes GET /healthz, GET /v1/ports, POST /v1/ports and DELETE /v1/ports/{id}. It is HTTP/JSON over a private Unix socket, not a network listener. Duplicate MAC/interface ownership is rejected; identical registration is idempotent.
What VM lifecycle integration still needs
kube-vm's current net device does not expose a guest MAC configuration field. Registering a TAP's host-side MAC is not a substitute for controlling the guest's source MAC. A coordinated integration must establish the guest identity, prepare the TAP, register ownership before guest traffic starts, and unregister before teardown.
It must also reconcile ports after switch restarts and recreate them correctly after VM restore. Registrations and BPF objects are process-owned, not a durable network checkpoint. This is why a manual registration command alone is not a complete integration.
Verified switch dataplanePort validation and TCX attachmentSwitch CLICurrent kube-vm attachment path
Companion behavior verified against the linked switch revision; later revisions may change the API or forwarding policy.