User Tools

Site Tools


zededa:eve-kvm:network-instances

This is an old revision of the document!


EVE-KVM: Network Instance lifecycle (Local & Switch)

This page documents exactly what EVE-OS does in KVM (hypervisor) mode when a Network Instance (NI) of type Local and type Switch is created.

Important — this is the EVE-KVM datapath, not EVE-K.
In EVE-K, NIs are wired through Multus + a NetworkAttachmentDefinition calling
the eve-bridge CNI plugin. In EVE-KVM there is no Multus, no NAD, no CNI.
The pillar agent zedrouter programs the Linux network stack directly: Linux
bridges, dnsmasq, iptables, policy routing tables and tc. Everything
below is the bridge/iptables world, not the Kubernetes world.

Source of truth: lf-edge/eve → pkg/pillar (cmd/zedrouter and nireconciler). File/line references are given per section so claims are verifiable.

NI types (enum)

EVE's internal NI type enum (mirrors zconfig.ZNetworkInstType from the controller API):

NetworkInstanceTypeSwitch      = 1   // L2 bridge, no EVE-side L3
NetworkInstanceTypeLocal       = 2   // L3 NAT'd bridge with DHCP/DNS
NetworkInstanceTypeCloud       = 3   // (VPN)
NetworkInstanceTypeHoneyPot    = 5
NetworkInstanceTypeTransparent = 6

pkg/pillar/types/zedroutertypes.go (NetworkInstanceType const block).

Address type drives L2 vs L3:

AddressTypeNone = 0   // Switch NI (no EVE-managed addressing)
AddressTypeIPV4 = 1   // Local NI
AddressTypeIPV6 = 2

Actors

  • controller (ZEDEDA Cloud / zedcontrol) — sends the device config.
  • zedagent — parses controller config, publishes NetworkInstanceConfig over pubsub.
  • zedrouter — subscribes to NetworkInstanceConfig (from zedagent), drives the

reconciler, publishes NetworkInstanceStatus. (cmd/zedrouter/zedrouter.go line ~13: “Zedrouter subscribes specifically for NetworkInstanceConfig (from zedagent)”.)

  • nireconciler (LinuxNIReconciler) — converts the desired NI config into a
    • *dependency graph of Linux config items** and reconciles intended vs current state.
  • NIM (nim) — owns physical device ports (DPC). For certain Switch NIs, NIM (not

zedrouter) owns the bridge.

  • domainmgr — later attaches the app/VM VIFs; zedrouter wires each VIF into the NI bridge.

Control-plane flow (how the config arrives)

controller (ZEDEDA Cloud)
    |  device config (protobuf)
    v
zedagent  --- publishes types.NetworkInstanceConfig --->  (pubsub)
    |
    v
zedrouter  subscribes NetworkInstanceConfig
    |   handleNetworkInstanceCreate()           cmd/zedrouter/pubsubhandlers.go:183
    |   -> parse/validate, allocate BridgeNum, bridge MAC, subnet
    |   -> doActivateNetworkInstance()           cmd/zedrouter/networkinstance.go:526
    |        -> niReconciler.AddNI(config, NIBridge)
    v
LinuxNIReconciler   builds intended dependency graph, reconciles to kernel
    |   creates bridge / dnsmasq / iptables / routes / ip rules / tc
    v
zedrouter  publishes types.NetworkInstanceStatus  (Activated=true)
    |
    v
niStateCollector.StartCollectingForNI()   (metrics, learned IPs, flow logs)

AddNI is the single entry point that turns NI config into kernel state (cmd/zedrouter/networkinstance.go:531).

The reconciler model (dependency graph)

nireconciler does not run imperative “create bridge, then add IP” steps. It builds an intended graph of typed config items and a reconciliation engine makes the kernel match it. The top-level graph (from the big ASCII map at the head of nireconciler/linux_config.go) has:

  • Global — sysctl (bridge-nf), Ports (external device ports), IPSets, BlackHole, ACL root chains, L2-fwd chain, TCP-MSS clamping.
  • NI-<UUID> — one per Network Instance, containing sub-graphs:
    • L2 — Bridge, BridgePort, VLAN items, L2-forward iptables rule.
    • L3 — Routes, IPRules, IPReserve, MASQUERADE rule. (empty for Switch)
    • Mirroring — DummyIf + tc ingress/mirror (used by Switch NI too).
    • Services — metadata HTTPServer, dnsmasq (DHCP+DNS), radvd (IPv6).
  • AppConn-<UUID>-<adapter> — one per VIF, added later when an app attaches:

VIF + BridgePort + per-VIF ACL chains (+ VLANPort/BPDUGuard for Switch).

Branch point (linux_config.go:600):

if !ni.bridge.IPConflict {
    PutSubGraph(getIntendedNIL2Cfg(niID))   // always
    PutSubGraph(getIntendedNIL3Cfg(niID))   // empty body for Switch
    if ni.config.Type == NetworkInstanceTypeSwitch {
        PutSubGraph(getIntendedNIMirroring(niID))
    }
}

Interface naming

nireconciler/linux_config.go:303-304, generateBridgeIfName() (line ~1678):

  • Bridge for Local NI → bnN (prefix bn + BridgeNum), e.g. bn1.
  • Bridge for Switch NI:
    • If the bridge is created by NIM (single port also used for mgmt or a Local NI) → the bridge is the port itself, e.g. eth1.
    • Otherwise (air-gapped, or multi-port shared label) → zedrouter creates bnN, same as Local.
  • App VIF host side → nbu<vifNum>x<appNum> (prefix nbu), e.g. nbu1x3.
  • Mirror dummy interface → <brIfName>-m.

“Created by NIM” rule (cmd/zedrouter/networkinstance.go:722): only a Switch NI with a single physical port whose Dhcp is Static or Client has its bridge owned by NIM. Local NIs are always bridged by zedrouter.

Type LOCAL (type = 2) — step by step

A Local NI is an L3, NAT'd, EVE-managed network: EVE owns the bridge IP, runs DHCP/DNS, and SNATs app traffic out the uplink port.

1. Bridge (L2 sub-graph)

zedrouter creates a managed Linux bridge bnN, assigns it the gateway IP (the bridge IP = the NI gateway, e.g. 10.10.1.1/24) and a deterministic MAC. (getIntendedNIL2Cfg, linux_config.go:623.) For Local NI the L2 sub-graph returns right after the bridge — no VLAN/STP/BridgePort items (those are Switch-only).

ip link add bn1 type bridge
ip addr add 10.10.1.1/24 dev bn1     # bridge IP == NI gateway
ip link set bn1 up

2. L3 sub-graph (Routes / IP rules / SNAT)

getIntendedNIL3Cfg (linux_config.go:756) — only runs for non-Switch:

  1. IPReserve for the bridge IP so EVE doesn't hand it out via DHCP.
  2. Per-NI routing table = NIBaseRTIndex + BrNum (NIBaseRTIndex = 800). Routes relevant to the NI's uplink port are copied from the main table into this per-NI table.
  3. A final unreachable route (IPv4 + IPv6) at the lowest priority — anything not matched is dropped.
  4. IP rules steering NI subnet traffic to that table:
    • PbrNatOutGatewayPrio = 9999 (to-bridge-IP → local table)
    • PbrNatOutPrio = 10000 (src = NI subnet → NI table)
    • PbrNatInPrio = 11000 (dst = NI subnet → NI table)
  5. SNAT / MASQUERADE (IPv4 only): for each uplink port, an iptables rule in the nat table POSTROUTING (app-specific chain):
# nat POSTROUTING (per uplink port of a Local NI)
-o eth0 -s 10.10.1.0/24 -j MASQUERADE

(linux_config.go:972 — guarded by Type == NetworkInstanceTypeLocal and IPv4 subnet.)

3. Services sub-graph: dnsmasq (DHCP + DNS)

getIntendedDnsmasqCfg (linux_config.go:1185). For Local NI EVE runs a per-NI dnsmasq bound to the bridge IP, serving DHCP + DNS to the apps:

  1. Leases from DhcpRange; advertises gateway = bridge IP.
  2. Advertises a default route unless the NI is air-gapped or all ports are app-shared with no default route.
  3. Propagates host routes to user-configured NTP/DNS servers, static routes, and (if PropagateConnRoutes) connected port subnets — all via DHCP option 121.
# one dnsmasq per Local NI, e.g.
dnsmasq --conf-file=/run/zedrouter/dnsmasq.bn1.conf
#   interface=bn1, listen-address=10.10.1.1, dhcp-range=...

4. Services sub-graph: metadata server

getIntendedMetadataSrvCfg (linux_config.go:1123). EVE starts an HTTP server on the bridge IPv4:80 and DNATs the well-known metadata IP to it:

# nat PREROUTING
-i bn1 -p tcp -d 169.254.169.254/32 --dport 80 -j DNAT --to-destination 10.10.1.1:80

This is the EVE app-metadata endpoint (169.254.169.254, metadataSrvIP).

5. App attach (later, when a VM is deployed)

When domainmgr starts the VM, zedrouter adds an AppConn-<UUID>-<adapter> sub-graph: a VIF nbuNxM enslaved to bnN as a BridgePort, plus the per-VIF ACL chains (iptables rules derived from the app's ACLs). The VM then gets its lease from the NI's dnsmasq.

Resulting kernel state (Local NI)

ip -br link show type bridge          # bn1 ... UP
ip addr show dev bn1                   # 10.10.1.1/24
bridge link show                       # nbu1x3 master bn1
iptables -t nat -S | grep -i masquerade
ip rule show | grep -E '9999|10000|11000'
ip route show table 801                # 800 + BrNum
ps aux | grep dnsmasq.bn1

Type SWITCH (type = 1) — step by step

A Switch NI is an L2 bridge. EVE provides Layer-2 connectivity between the app VIFs and a physical uplink port; EVE does not provide IP, DHCP, DNS, NAT or routing. The app gets its address from an external DHCP server on the wire.

1. Bridge ownership

niBridgeIsCreatedByNIM (networkinstance.go:722):

  • Single port also used for EVE mgmt or a Local NI (Dhcp = Static/Client) → NIM owns the bridge; the bridge is the port (e.g. eth1). zedrouter just bridges into it.
  • Air-gapped (PortLabel == “”) or multi-port (shared label) → zedrouter creates bnN, exactly like a Local NI bridge.

2. L2 sub-graph (the whole story for Switch)

getIntendedNIL2Cfg (linux_config.go:623). This is where Switch-specific items live:

  1. Bridge (bnN or the NIM-owned port).
  2. BridgeFwdMask (controls LLDP forwarding).
  3. STP enabled only when the Switch NI has > 1 port (withSTP).
  4. For each uplink port:
    • BridgePort = the physical device port enslaved to the bridge.
    • VLANBridge / VLANPort (trunk or access) when VLAN filtering is configured (VlanAccessPorts).
    • VLANSubIf for tagged sub-interfaces.
    • BPDUGuard on designated ports when STP is on.
  5. L2-forward iptables rule — allows forwarding between bridge ports (getIntendedL2FwdRules).
ip link add eth1 type bridge          # (or bn1 if zedrouter-owned)
ip link set eth0 master eth1          # physical uplink enslaved
# VLAN filtering / trunk / access programmed via `bridge vlan` when configured

3. L3 sub-graph — EMPTY

getIntendedNIL3Cfg returns immediately for Switch (linux_config.go:762):

if ni.config.Type == NetworkInstanceTypeSwitch {
    // No L3 config for switch network instance.
    return intendedL3Cfg
}

Consequence: no bridge gateway IP, no MASQUERADE/SNAT, no per-NI routing table, no NAT IP rules.

4. Services — NO dnsmasq

getIntendedDnsmasqCfg returns immediately for Switch (linux_config.go:1187):

if ni.config.Type == NetworkInstanceTypeSwitch {
    // Not running DHCP and DNS servers inside EVE for Switch network instances.
    return
}

The app must obtain its IP from an external DHCP server reachable over the bridged uplink.

5. Metadata server on a Switch NI

The metadata HTTP server only starts if the bridge has an IPv4 address; a pure Switch NI normally has none, so there is typically no metadata service. Where a bridge IP does exist, EVE adds a guard rule that DROPs metadata access arriving from the external physical port, so outside endpoints can't reach it through the L2 segment (linux_config.go, filter INPUT with physdev –physdev-in <port>).

6. Mirroring sub-graph (Switch NI)

Unlike Local NI, Switch NI explicitly adds the Mirroring sub-graph (getIntendedNIMirroring, linux_config.go:996): a DummyIf plus tc ingress + mirror rules that copy a small slice of traffic (DHCP replies, ARP, ICMPv6 NS, and DNS if flow-logging is on) so the niStateCollector can learn app IPs and log flows even though EVE isn't the DHCP/DNS server.

7. App attach

Same AppConn-<UUID> mechanism: a VIF nbuNxM enslaved to the bridge as a BridgePort. For Switch NI the per-VIF sub-graph also gets VLANPort and BPDUGuard items when VLAN/STP are configured. The VM sees a plain bridged L2 interface and behaves as if cabled directly to the uplink.

Resulting kernel state (Switch NI)

ip -br link show                       # eth1 (or bn1) UP, eth0 enslaved
bridge link show                       # eth0 + nbu1x3 master eth1
bridge vlan show                       # trunk/access VLANs if configured
iptables -t nat -S | grep -i masquerade   # (empty for Switch)
ps aux | grep dnsmasq                       # no dnsmasq for this NI

Side-by-side: Local vs Switch on EVE-KVM

Aspect Local (type 2) Switch (type 1)
OSI layer L3 (routed/NAT) L2 (bridged)
Bridge owner zedrouter (always), bnN NIM if single mgmt/Local port (= ethX); else zedrouter bnN
Bridge IP / gateway Yes — EVE owns gateway IP None (EVE assigns no L3)
DHCP/DNS (dnsmasq) Yes, per-NI on bridge IP No — external DHCP on the wire
SNAT / MASQUERADE Yes, per uplink port (IPv4) No
Per-NI routing table Yes (800 + BrNum) No
NAT IP rules (pbr) Yes (9999/10000/11000) No
VLAN / STP / BPDUGuard No Yes (STP when >1 port)
Metadata server (.169.254) Yes (DNAT to bridge IP:80) Usually none; guarded if bridge IP exists
Mirroring sub-graph Implicit via state collector Explicit (tc ingress/mirror)
App addressing Lease from EVE dnsmasq Lease from external DHCP

Inspecting a live EVE-KVM node

Via EdgeView / device debug shell:

# Which NIs exist and their bridges/types (from zedrouter status)
cat /run/zedrouter/NetworkInstanceStatus/*.json | jq '{name:.DisplayName,type:.Type,br:.BrIfName,ip:.BridgeIPAddr}'
 
# Bridges and enslaved interfaces
ip -br link show type bridge
bridge link show
 
# Local NI plumbing
ip addr show dev bn1
ip rule show
ip route show table 801                 # 800 + BridgeNum
iptables -t nat -S | grep -E 'MASQUERADE|169.254.169.254'
ls /run/zedrouter/dnsmasq.*.conf
 
# Switch NI plumbing
bridge vlan show
iptables -S | grep -i physdev           # metadata guard / L2 fwd rules
 
# VIFs of running apps
ip -br link show | grep nbu

Source references (lf-edge/eve, pkg/pillar)

  • NI type & config struct — types/zedroutertypes.go (NetworkInstanceType, NetworkInstanceConfig).
  • Routing/PBR constants — types/pbr.go (NIBaseRTIndex=800, PbrNatOutGatewayPrio=9999, PbrNatOutPrio=10000, PbrNatInPrio=11000).
  • Config subscription — cmd/zedrouter/zedrouter.go (subscribes NetworkInstanceConfig from zedagent).
  • Create/modify handlers — cmd/zedrouter/pubsubhandlers.go:183 / :294.
  • Activate / AddNI — cmd/zedrouter/networkinstance.go:526 (doActivateNetworkInstance → niReconciler.AddNI).
  • Bridge-owner rule — cmd/zedrouter/networkinstance.go:722 (niBridgeIsCreatedByNIM).
  • Dependency-graph map + all item builders — nireconciler/linux_config.go:
    • getIntendedNIL2Cfg (:623), getIntendedNIL3Cfg (:756), MASQUERADE (:972),

mirroring (:996), metadata (:1123), dnsmasq (:1185), interface naming (:303, :1678).

  • Linux item types — nireconciler/linuxitems/ (bridge, bridgeport, vlanbridge, vlanport, route, iprule, ipset, tcingress, tcmirror, bpduguard, sysctl…) and genericitems/ (dnsmasq, radvd, httpsrv, port).
zededa/eve-kvm/network-instances.1782392007.txt.gz · Last modified: (external edit)