KubeVirt Bridge Networking#
In bridge binding mode, KubeVirt creates a Linux bridge on the pod's primary network interface and connects the guest NIC to it. The pod interface's IP configuration is not visible inside the guest directly — instead, a per-VM DHCP server runs inside the virt-launcher pod to hand the pod's IP/route/DNS settings to the guest over DHCP.
This mode is distinct from masquerade (NAT-based, fully migration-safe) and is used when the guest needs to appear on the pod network directly with its own MAC address. Bridge binding has notable constraints around live migration and IPv6 that do not apply to masquerade.
Key source locations#
| Area | File |
|---|---|
| Migration eligibility check | pkg/network/vmispec/interface.go |
| Bridge DHCP config generation | pkg/network/dhcp/bridge.go |
| DHCP configurator & lifecycle | pkg/network/dhcp/configurator.go |
| Single-client DHCP server | pkg/network/dhcp/server/server.go |
| Bridge network discovery / DHCP data population | pkg/network/setup/netpod/discoverbridge.go |
| Annotation constant definition | staging/src/kubevirt.io/api/core/v1/types.go |
Live Migration Eligibility#
Bridge binding on the pod network is not migration-safe by default. VerifyVMIMigratable in pkg/network/vmispec/interface.go blocks migration for any VMI whose primary interface uses bridge binding unless an explicit opt-in annotation is present.
kubevirt.io/allow-pod-bridge-network-live-migration#
Adding this annotation to a VMI bypasses the migration guard :
metadata:
annotations:
kubevirt.io/allow-pod-bridge-network-live-migration: "true"
The constant is defined at . All other primary interface types that permit migration without annotation are: Masquerade, PasstBinding, and migratable binding plugins (those with a non-nil Migration field).
Why it's opt-in: After migration the guest lands on a new pod with a different network namespace and potentially a different pod interface MAC. Without additional handling (see MAC preservation below), the in-pod DHCP server may hand the guest a stale or incorrect MAC, causing DHCP and IP continuity failures after a guest reboot.
MAC Preservation After Live Migration#
The Problem#
When a VM with bridge binding migrates, the destination pod may present a new pod interface MAC (reported by nmstate). If the DHCP server naively uses that new MAC, a guest reboot causes a DHCP failure because the guest NIC still uses the original MAC that the DHCP server no longer recognizes. This bug was tracked in issue #16696.
The Fix (PR #16697)#
PR #16697 introduced a MAC resolution priority chain in storeBridgeBindingDHCPInterfaceData. The resolveMacAddress function now accepts three inputs and returns the first non-empty one in priority order:
- VMI Spec MAC (
vmiSpecIface.MacAddress) — highest priority; user-specified, always stable - VMI Status MAC — looked up via
vmispec.LookupInterfaceStatusByName(n.vmiIfaceStatuses, vmiSpecIface.Name); captures the MAC used on the source pod before migration - Pod interface MAC (
podIfaceStatus.MacAddress) — lowest priority; used only on first boot before any status exists
This ensures the DHCP server on the migration target always offers the MAC the guest already knows, preventing post-migration DHCP failures.
Single-client DHCP server MAC enforcement#
The server's DHCPHandler.ServeDHCP method silently drops all DHCP requests whose CHAddr does not match the configured clientMAC. The MAC written by resolveMacAddress into the cache becomes the single MAC the server accepts — making MAC consistency between the cache and the guest NIC a hard requirement.
Built-in Single-Client DHCP Server#
Both bridge and masquerade bindings use the same in-process DHCP server; the server is not specific to bridge networking.
Lifecycle#
EnsureDHCPServerStarted in pkg/network/dhcp/configurator.go manages idempotent startup:
- Skips start if
IPAMDisabledis set in the cached DHCP config . - Checks for a marker file at
/var/run/kubevirt-private/dhcp_started-<ifaceName>. If the file exists, the server was already started in a prior call and the function returns immediately. - Calls
startDHCP, which reads DNS info fromresolv.conf, prepares search domains, then launches the server in a goroutine . A panic is triggered if the server exits with an error. - Creates the marker file to record successful startup .
Both NewBridgeConfigurator and NewMasqueradeConfigurator wire in the same startDHCP function; the configurator is differentiated only by its ConfigGenerator .
Server behavior#
SingleClientDHCPServer in pkg/network/dhcp/server/server.go binds to UDP port 67 on the advertising interface and responds only to DHCP Discover and DHCP Request packets from the single pre-configured client MAC . It issues leases with an effectively infinite duration of 999 days. DHCP options advertised include: subnet mask, router, DNS servers, MTU, classless static routes (RFC 3442), search domains, hostname, domain name, and optionally TFTP server name, boot file name, NTP servers, and private options 224–254 .
DHCP config generation (bridge-specific)#
BridgeConfigGenerator.Generate reads the cached DHCP config written by storeBridgeBindingDHCPInterfaceData, sets a fake bridge IP as the advertising address (via virtnetlink.GetFakeBridgeIP), resolves the renamed pod NIC for MTU, and returns the assembled config. The fake bridge IP is distinct from the pod IP so the DHCP server IP does not collide with the guest IP offered.
Known Limitations#
- DHCPv6 is not supported for bridge binding. The discovery code explicitly drops the pod's IPv6 addresses before storing interface data , so guests with bridge binding cannot receive IPv6 configuration via DHCP.
- DHCP failure on guest reboot post-migration was a known issue when the pod MAC changed during migration (fixed by PR #16697, ). VMs using bridge binding without a static
spec.macAddressand the annotation should use the version containing that fix. - Live migration is opt-in and carries networking continuity risks that masquerade avoids. For production live migration, masquerade or a migratable binding plugin is preferred unless the VM explicitly needs bridge semantics.