Architecture
Big picture
Cilium has two layers. A user-space Go agent runs on every node (one per node, as a DaemonSet) and decides what the network should do. A kernel-space eBPF datapath, compiled from C in bpf/ and attached to tc and XDP hooks, does the actual packet processing. The agent computes configuration and writes it into eBPF maps; the kernel then handles real traffic without calling back into user space. A cluster-scoped operator handles work that is global rather than per-node.
Components
cilium-agent
The agent is the per-node brain. Its entry point is daemon/main.go, which builds the application with Cilium's own dependency-injection framework, Hive, and runs it as the cilium-agent cobra command (daemon/main.go:13, daemon/cmd/root.go:24). Modules ("cells") are assembled and wired in daemon/cmd/. The agent owns endpoint orchestration, identity allocation, policy resolution, and datapath loading.
cilium-cni plugin
A separate binary that kubelet invokes per pod. It implements the CNI Add and Del operations in plugins/cilium-cni/cmd/cmd.go and talks to the agent over a Unix-socket REST API rather than doing the heavy lifting itself.
operator
A single cluster-scoped Deployment (operator/) that handles work which must not be per-node: IPAM CIDR allocation, identity garbage collection, and CRD management.
datapath and maps
pkg/datapath/ is the datapath abstraction; pkg/datapath/loader/ compiles, templates, and loads eBPF objects. pkg/maps/ wraps the individual eBPF maps in Go: lxcmap for endpoints, policymap for resolved policy, ctmap for connection tracking, and others.
identity, policy, and ipcache
pkg/identity/ and pkg/labels/ map label sets to numeric security identities. pkg/policy/ resolves network policy into per-endpoint MapState, the contents of the eBPF policy map. pkg/ipcache/ maintains the cluster-wide IP-to-identity mapping and syncs it into the eBPF ipcache map.
Hubble
hubble/ and hubble-relay/ form the observability layer, turning eBPF-sourced flow events into queryable visibility.
How a request flows
Tracing a pod from scheduling to a live eBPF datapath:
- CNI ADD. Kubelet invokes
cilium-cni.Cmd.Add(plugins/cilium-cni/cmd/cmd.go:523) allocates an address via IPAM and configures the veth interface inside the container netns. Because the CNI call must not return until the datapath is ready, it setsep.SyncBuildEndpoint = true(plugins/cilium-cni/cmd/cmd.go:838, with a comment referencing GH-4409) and posts the endpoint to the agent withc.EndpointCreate(ep)(plugins/cilium-cni/cmd/cmd.go:842). - API handler. The agent serves
PUT /endpoint/{id}throughEndpointPutEndpointIDHandler.Handle(pkg/endpoint/api/endpoint_api_handler.go:195), reachingendpointAPIManager.CreateEndpoint(pkg/endpoint/api/endpoint_api_manager.go:88). - Endpoint construction.
createEndpoint(pkg/endpoint/endpoint.go:597) builds the Endpoint struct, andendpointManager.AddEndpointassigns a node-unique ID (pkg/endpoint/api/endpoint_api_manager.go:293). With no labels yet, the endpoint gets a provisionalreserved:initidentity (pkg/endpoint/api/endpoint_api_manager.go:284). - Identity resolution and regeneration. For a Kubernetes pod,
ep.RunMetadataResolverfetches pod labels, finalizes the identity, and triggers regeneration (pkg/endpoint/api/endpoint_api_manager.go:303). Otherwise the manager callsep.Regenerateexplicitly at theRegenerateWithDatapathlevel (pkg/endpoint/api/endpoint_api_manager.go:324). - Regeneration pipeline.
Endpoint.Regenerate(pkg/endpoint/policy.go:867) queues the build;regenerateandregeneratePolicyresolve policy intoMapState, thenregenerateBPF(pkg/endpoint/bpf.go:360) takes over. It first waits on<-e.orchestrator.DatapathInitialized()and then takes the compilation read lock, so endpoint builds do not race base-program compilation (pkg/endpoint/bpf.go:375). - Header, compile, load.
writeHeaderfileemits endpoint-specific constants intolxc_config.h(pkg/endpoint/bpf.go:139),realizeBPFState(pkg/endpoint/bpf.go:568) runs, andorchestrator.ReloadDatapath(pkg/endpoint/bpf.go:587) loads the eBPF object and attaches it to tc/XDP, using the ELF template cache inpkg/datapath/loader/cache.go. - Synchronous completion. Because
SyncBuildEndpointis set, the agent blocks onep.WaitForFirstRegeneration(ctx)(pkg/endpoint/api/endpoint_api_manager.go:337) until the first datapath build finishes before returning the REST response. By the time CNI ADD returns, the pod's eBPF datapath is live.
Key design decisions
- Identity-based policy. Policy is keyed on numeric identities derived from label sets, not on IPs. Pods scaling up or down changes IP membership but not the identity, so rule counts do not grow with pod counts.
Identity,IPCache, andMapStatetogether make this model work. - Template-and-substitute datapath. Cilium does not recompile eBPF per endpoint. It compiles one ELF per distinct configuration hash and clones it, substituting endpoint-specific values just before load (
pkg/datapath/loader/cache.go). This avoids a clang invocation per pod. - Push into the kernel. The agent pushes resolved state into eBPF maps and the kernel enforces it inline, rather than processing packets in user space.
- Declarative lifecycle via Hive. The agent is assembled from cells with
hive.New(cmd.Agent)(daemon/main.go:13), so startup order and shutdown are declarative.
Extension points
Cilium exposes Kubernetes CRDs for policy and configuration (CiliumNetworkPolicy and related types), a CNI plugin contract that kubelet drives, and the agent REST API consumed by the CNI plugin and cilium-cli. Hubble exposes flow data to external consumers. Cluster-scoped behavior such as IPAM and CRD handling is owned by the operator (operator/).