TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

Cilium 源码导读:代理如何填充 BPF 映射,内核钩子如何仅凭映射作出判断的架构

状态:草稿(2026-10-17),部分经过实际测量。 基准版本 cilium/cilium v1.20.1(提交 7d68cfb394f2)。由于小型集群正在运行此版本(agent、 operator-generic、hubble-relay v1.20.1,cilium-envoy 1.37 系列),实际测量于 2026-10-17 在该集群中 仅通过读取 完成(对单个 agent Pod 执行 cilium-dbg·bpftool·hubble observe 读取命令、读取 ConfigMap 及策略数量、读取 relay 日志)。 DNS 模式的连接结果引用了为贡献做准备而在 2026-10-16 至 17 之间记录的日志(PR 草稿)。 编写规则:以实测数据为准,未测量的标记为「未实测」,未能确认的标记为「未确认」。

目录

1. 一行总结

如果把 Cilium 浓缩为一句话,那就是: 「每个节点上的办公室(代理)修改规则表(BPF 映射),内核各处的检查站(BPF 程序)仅凭规则表来决定放行、丢弃或地址转换」 。检查站不会给办公室打电话。由于每个数据包都上送到用户空间会降低性能,因此判断所需的所有信息都已提前写入映射中。因此,在阅读 Cilium 代码时,首先要问的问题永远是: 这些信息是由谁写入哪个映射的, ,以及哪个钩子在读取它。」

组件 职责 运行位置
cilium-agent 接收 Kubernetes 状态以计算策略、服务和 identity,并写入 BPF 映射。编译并加载 BPF 程序。DNS 代理和 Hubble 观察器也在此进程中 每个节点(DaemonSet)
BPF 程序(bpf/) 挂载于 Pod veth、主机设备、隧道设备和 socket cgroup,处理数据包和 connect() ) 内核内部
cilium-operator 注册 CRD、IPAM(根据模式)、清理遗留的 identity 和 CiliumEndpoint、LB-IPAM 集群中一个(领导者选举)
cilium-envoy 处理 HTTP 等 L7 策略以及 Ingress 和 Gateway 的代理。通过 xDS 从代理接收配置 每个节点(独立 DaemonSet)
Hubble 代理内的观察器将 BPF 生成的事件解释为流并放入环形缓冲区,由 relay 汇聚各节点的流 观察器在代理内,relay 在集群中有一个

2. 架构图

Cilium 구성요소와 BPF 프로그램이 붙는 자리 kube-apiserver Service·CNP·CRD 를 감시 cilium-operator 클러스터에 1개 · IPAM·CRD·GC 노드마다 (DaemonSet 두 개) cilium-agent — hive 셀 묶음 infra · controlplane · datapath 정책·셀렉터 캐시 로드밸런서 표 DNS 프록시 loader (clang) Hubble 관찰기 IPAM·ipcache cilium-envoy L7 프록시 (HTTP·gRPC) 에이전트에게 xDS 로 설정 받음 BPF 맵 (/sys/fs/bpf 에 고정) 쓰기 events 서비스 정책 ipcache conntrack 훅 자리 → 붙는 프로그램 (bpf/ 의 파일) 읽기 cgroup 루트(소켓) bpf_sock.c → cil_sock4_connect 등 파드 veth (lxc*) bpf_lxc.c → cil_from_container cilium_host·net bpf_host.c → cil_from/to_host cilium_vxlan bpf_overlay.c → cil_from/to_overlay 물리 NIC (tc) bpf_host.c → cil_from/to_netdev 물리 NIC (XDP) bpf_xdp.c → cil_xdp_entry (꺼짐) hubble-relay · hubble-ui (클러스터에 1개) 에이전트마다 :4244(TLS)로 흐름을 모은다
图:Cilium 组件及 BPF 程序的挂载位置

3. 代码导读

仓库虽然庞大,但初次浏览时需要关注的地方是固定的。

路径 内容 首先阅读的文件
daemon/ 代理入口点和 cell 集合 daemon/cmd/cells.go
bpf/ 加载到内核中的 C 程序。入口文件 bpf_*.c 及公共头文件 bpf/lib/ bpf/bpf_lxc.c
pkg/datapath/ BPF 编译与加载(loader)、设备、路由与 sysctl 调整、映射定义 pkg/datapath/loader/endpoint.go
pkg/loadbalancer/ v1.18 之后重构的服务负载均衡控制平面(StateDB 表 + reconciler) pkg/loadbalancer/README.md
pkg/policy/ 策略存储、selector cache、每个端点的策略计算 pkg/policy/selectorcache.go
pkg/fqdn/ DNS 代理、名称到 IP 缓存、 matchPattern 正则表达式转换 pkg/fqdn/matchpattern/matchpattern.go
pkg/hubble/, hubble-relay/ 流解析、环形缓冲区、gRPC 服务器以及 relay pkg/hubble/observer/local_observer.go
operator/ operator 入口点和 cell operator/cmd/root.go

代理和 operator 都通过名为 hive 的依赖注入框架进行组装。一个功能即是一个「cell」,cell 仅声明它提供什么以及需要什么。代理 main 在 daemon/main.go:11 中只有一行,而该 hive.New(cmd.Agent) 在 Agent 中分为 daemon/cmd/cells.go:103 三个部分。 Infrastructure·ControlPlane·datapath.Cell 注释直接说明了划分的原因。控制平面属于「纯业务逻辑」,旨在无需内核权限即可运行集成测试。而实际操作 BPF 的工作则集中到了 ControlPlane(cells.go:194的 pkg/datapath/cells.go:52 模块中。 datapath 实测的 cilium-dbg status --verbose 模块树也分成了 controlplane / datapath / infra 这三个部分(第 6 节)。

4.核心路径追踪

4.1 代理编译与加载 BPF 的路径

Cilium 不会将 BPF 对象预先打包到镜像中,而是 在节点上使用 clang 进行编译(代理镜像中包含 clang 19.1.7,经实测)。由于每个节点的内核功能和配置(是否启用隧道、伪装、IPv6 等)各不相同,因此必须通过 #ifdef 将未使用的功能彻底剔除,以通过验证器并避免触及指令数上限。

  1. 基于的数据路径。 pkg/datapath/orchestrator/orchestrator.go:167 reconciler 在节点配置每次发生变化时调用 Reinitialize , pkg/datapath/loader/base.go:298 Reinitialize 为隧道设备编译 bpf_overlay.c、XDP,如果启用了 socket LB,则编译 bpf_sock.c 并将其挂载到base.go:406cgroup 中(pkg/socketlb/socketlb.go:83 Enable).
  2. 针对每个端点(Pod)。 pkg/datapath/loader/endpoint.go:83 ReloadDatapath 首先调用 cache.go:176 fetchOrCompile 。这正是设计的关键所在。它使用端点配置中 除每个 Pod 不同的值(端点 ID、IP、ifindex 等)之外的其余部分来生成哈希,如果相同哈希的对象已存在,则直接复制复用而不进行编译。实际编译由 compile.go:160 compile 执行 clang 完成。
  3. 赋值与挂载。 endpoint.go:195 reloadEndpoint 将 Pod 特定的值作为 常量覆盖写入(Constants: endpointConfiguration(...)模板中并加载,先将策略程序放入 tail call 映射中,然后将 cil_from_container 挂载到 veth 的 ingress 上。顺序代码中有注释说明:如果先打开入口,数据包将进入半准备就绪的程序。Pod 端的 egress 程序(cil_to_container)仅在 RequireEgressProg() 为真时挂载。

实测数据说明了原因。单个节点上的 13 个端点共享了 一个模板哈希,并且只有 bpf_lxc.o(约 1.0MB)和 bpf_host.o(约 1.4MB)两个模板。编译仅进行一次,其余通过修改常量加载,从而将 clang 从 Pod 创建延迟中剥离。

4.2 替代 kube-proxy:Service 如何在 connect() 中转变为后端

kube-proxy 将 Service 转换为 iptables(或 IPVS)规则。Cilium 将相同的信息放入 BPF 哈希映射中,并 尽可能在数据包甚至尚未生成之前 就修改 socket 的目的地址。在 v1.18 中,该控制平面基于 StateDB 进行了重构,其结构正如 pkg/loadbalancer/README.md 中的图示。

  1. 反射(reflect)。 pkg/loadbalancer/reflectors/k8s.go:208 runServiceEndpointsReflector 将 Service 和 EndpointSlice 的变更短暂汇聚后作为单个事务写入(避免在大规模滚动更新时数千次单独修改映射)。
  2. 写入表。 pkg/loadbalancer/writer/writer.go:326 UpsertServiceAndFrontends 等模块对齐服务、前端和后端三张表的引用。即使有多个数据源,也只会通过此 Writer 写入。
  3. 与映射对齐。 pkg/loadbalancer/reconciler/bpf_reconciler.go:725 Update 监视前端表并在 updateFrontend(第 842 行) 中写入映射。映射 cilium_lb4_services_v2(bpf/lib/lb.h:284的键是 (주소, 포트, 프로토콜, 슬롯) ,槽 0 是「头部」(后端数量、标志),槽 1..n 是后端 ID。重试由 reconciler 负责,因此数据源无需关心映射错误。
  4. 在 socket 中解析。 bpf/bpf_sock.c:467 cil_sock4_connect 在 cgroup connect4 钩子中调用 __sock4_xlate_fwd(第 298 行) ,并在其中使用 lb4_lookup_service 查找头部,然后通过 sock_select_slot() % count + 1 选择槽位将 socket 地址更改为后端。应用程序相信自己连接到了 ClusterIP,但内核 socket 从一开始就绑定到了后端,因此每个数据包无需进行 NAT 操作。

然而,第 4 步中存在一个分支。

	if (is_defined(ENABLE_SOCKET_LB_HOST_ONLY) && !in_hostns)
		return -ENXIO;

	if (!udp_only && !sock_proto_enabled(protocol))
		return -ENOTSUP;

	/* In case a direct match fails, we try to look-up surrogate
	 * service entries via wildcarded lookup for NodePort and
	 * HostPort services.
	 */
	svc = lb4_lookup_service(&key, true);
	if (!svc)
		svc = sock4_wildcard_lookup_full(&key, in_hostns);
	if (!svc)
		return -ENXIO;

来源:cilium v1.20.1 bpf/bpf_sock.c 第 322–335 行,GPL-2.0-only OR BSD-2-Clause

socketLB.hostNamespaceOnly 如果启用,Pod 命名空间中的 socket 将直接在此处返回。那么服务地址转换由谁来做呢? bpf_lxc.c 在编译时自行判断。

#if !defined(ENABLE_SOCKET_LB_FULL) || \
    defined(ENABLE_SOCKET_LB_HOST_ONLY) || \
    defined(ENABLE_L7_LB)               || \
    defined(ENABLE_SCTP)                || \
    defined(ENABLE_CLUSTER_AWARE_ADDRESSING)
# define ENABLE_PER_PACKET_LB 1
#endif

来源:cilium v1.20.1 bpf/bpf_lxc.c 第 90–96 行,GPL-2.0-only OR BSD-2-Clause

如果存在任何无法在 socket 中完全解析的情况,Pod veth 的 tc 程序中就会引入基于数据包的 LB(__per_packet_lb_svc_xlate_4,第 152 行)。官方文档解释称,这种绕过是为了原本需要 ClusterIP 的情况(如 Istio Sidecar),以及 socket LB 无效的 Pod(如 KubeVirt、Kata、gVisor)(kubeproxy-free 文档「Socket LoadBalancer Bypass in Pod Namespace」)。实测的小型集群正是这种模式(Socket LB Coverage: Hostns-only)。由于 TT Lab 集群运行 KubeVirt VM,因此这一选择是有原因的(KubeVirt 实践 VM 系列)。结果,从 Pod 到服务的连接在 cil_from_container 内部解析,socket 反向转换映射(cilium_lb4_reverse_sk)中只有 13 个主机命名空间 socket。「kube-proxy 替代 = socket LB」是一个容易产生误解的误区。

4.3 toFQDNs 策略通过 DNS 代理实现「允许此 IP」的路径

BPF 仅识别 IP 和端口。然而,人类倾向于使用诸如 *.docker.com 之类的名称编写策略。Cilium 的解决方案是 即拦截 DNS 响应,学习 名称→IP,并为学到的 IP 附加来自名称的 identity。名称不会下沉到 BPF,BPF 仍然照常只询问「是否允许此 identity?」。

toFQDNs 가 「이 IP 허용」 이 되기까지 강조한 두 칸(③·⑤)이 matchpattern 정규식을 쓰는 곳 ① 정책이 FQDN 셀렉터를 등록 toFQDNs → NameManager.RegisterFQDNSelector ② 파드의 DNS 질의를 프록시로 돌린다 bpf_lxc 가 53번을 노드의 DNS 프록시로(투명 프록시) ③ 질의 이름 검사 — rules.dns ServeDNS → CheckAllowed, 안 맞으면 REFUSED ④ 위로 보내고 응답을 받는다 DNSClients.Exchange → 응답의 이름·IP·TTL ⑤ 이름이 어느 toFQDNs 셀렉터에 맞나 UpdateGenerateDNS → deriveLabelsForName ⑥ IP 에 fqdn: 라벨 → identity → 정책 맵 ipcache 갱신 → SelectorCache.UpdateIdentities ⑦ 반영을 기다린 뒤 응답을 돌려준다 WaitForRevision(최대 100ms) → WriteMsg ⑧ 파드가 그 IP 로 연결 → bpf_lxc 가 정책 맵에서 허용을 찾는다 matchpattern 변환 (escapeRegexpCharacters) *.docker.com — * 는 점을 넘지 않는다 ^[-a-zA-Z0-9_]*[.]docker[.]com[.]$ **.docker.com — 앞머리 **. 만 여러 칸 ^(라벨([.]라벨)*)[.]docker[.]com[.]$ 질의 이름 * 패턴 **. 패턴 hub.docker.com 맞음 맞음 production.cloudflare.docker.com 아님 맞음 docker.com 아님 아님
图:toFQDNs 允许 IP 的过程
  1. 注册选择器。 如果策略中包含 toFQDNs ,则 pkg/fqdn/namemanager/manager.go:140 RegisterFQDNSelector 会接收选择器,如果缓存中的现有名称有匹配项,则会立即显示 IP。选择器的正则表达式由 pkg/policy/api/fqdn.go:115 ToRegex 通过 matchpattern 创建。
  2. 拦截查询。 由于相同策略的 rules.dns(端口 53), bpf_lxc 会将该 Pod 的 DNS 数据包重定向到节点的 DNS 代理。 pkg/fqdn/dnsproxy/proxy.go:895 ServeDNS 通过 CheckAllowed(第 763 行) 检查查询名称。 该正则表达式由 GeneratePattern(第 1439 行) 将 rules.dns 的模式用 | 组合而成。如果不匹配,则返回拒绝响应(小型集群配置为 refused).
  3. 响应 → 名称缓存。 如果允许则发送到上游,在接收到响应并将其返回给客户端 之前 NotifyOnDNSMsg ,会调用 pkg/fqdn/messagehandler/message_handler.go:277 UpdateOnDNSMsg 。这会经过 namemanager/manager.go:214 UpdateGenerateDNS 到达
  4. 。 manager.go:422 deriveLabelsForName 名称匹配。 fqdn:<패턴> 将响应名称与所有 FQDN 选择器的正则表达式进行比对,为每个匹配的选择器收集
  5. 标签。 manager.go:315 updateMetadata 为 IP 分配 identity。 pkg/policy/selectorcache.go:768 UpdateIdentities 将响应中每个 IP 的这些标签上传到 ipcache,ipcache 为标签组合分配匹配的 identity。新 identity 通过
  6. 进入选择器缓存,并且使用该选择器的端点策略映射中将出现允许条目。 UpdateGenerateDNS 等待并返回。WaitForRevision会返回一个通道,用于等待 ipcache 将该更改反映到映射中( tofqdns-proxy-response-max-delay)。处理程序仅等待一段时间(小型集群为 100ms),然后将响应写入 Pod。这样做的顺序是为了确保 Pod 一收到响应就进行连接时,策略映射已经打开。

这里重要的一点是 matchpattern 被使用了两次(图中的 ③ 和 ⑤)。转换规则很简短。

func escapeRegexpCharacters(pattern string) string {
	// Convert '.' in the match pattern as literal '.' for regex pattern.
	pattern = strings.ReplaceAll(pattern, ".", "[.]")

	// '**.' in match pattern prefix is a subdomain wildcard specifier which matches one ore more
	// entire labels.
	pattern = subdomainWildcardSpecifierPrefix.ReplaceAllString(pattern, dnsWildcardREGroup)

	// Base case: * becomes .*, but only for DNS valid characters
	// `*` wildcard matches all DNS characters within the subdomain boundary(doesn't include '.' literal)
	pattern = wildcardSpecifier.ReplaceAllString(pattern, allowedDNSCharsREGroup+"*")

	return pattern
}

来源:cilium v1.20.1 pkg/fqdn/matchpattern/matchpattern.go 第 131–144 行,Apache-2.0

* 变为 [-a-zA-Z0-9_]*(第 17 行), 且不能跨越点号。 只有最前面的 **. 可以接收多个字段(第 37 行的 ^[*]{2,}\[\.\],第 23 行的 dnsWildcardREGroup),而中间的 ** 在最后一行的表现就像 * 一样。在末尾添加点号的规范化由 Sanitize(第 87 行) 完成。

实测案例。 这是在准备贡献时在小型集群中测量的记录(2026-10-16~17,PR 草案)。镜像仓库的策略是 toFQDNs: matchPattern: "*.docker.com" ,DNS 是 * 在打开的 Pod 中对三个名称分别进行了两次连接测试。

名称 DNS 解析 连接 原因
hub.docker.com 成功 连接 (HTTP 200) 单格 → *.docker.com 匹配
production.cloudflare.docker.com 成功 超时 (curl 28) 两格 → * 无法跨越点号
docker.com 成功 超时 *. 前面必须有一个空格

第二行是用于下载 Docker Hub 镜像层的 CDN 名称,策略看起来是匹配的,但表现出来的症状是仅镜像拉取失败。在同一条记录中, 将 main 的 matchpattern 用 Go 直接运行的(Validate(Sanitize(p))) 结果也和表格相同(**.docker.com 匹配第二行),在撰写本文时将转换规则 迁移到 Python 并重新运行的结果也是如此(图下方表格)。由于 DNS 都已解析成功,因此受阻的地方不是 ③ 而是 ⑤。相同的性质 在实战 Pod 的 DNS 出口策略中引发了什么问题,我写在了 堵住 DNS 泄露之路 中。 本文仅探讨该性质究竟源自代码的哪一行。

4.4 Hubble:从 perf 环形缓冲区到 relay

  1. BPF 生成事件。 当数据包被转发、阻止或进行策略判定时, bpf/lib/trace.h:210 _send_trace_notify 等会写入简短的记录( cilium_events(bpf/lib/events.h:13, PERF_EVENT_ARRAY)。由于记录所有数据包代价高昂,因此会通过 MONITOR_AGGREGATION 进行缩减。实测端点的 ep_config.h 是 MONITOR_AGGREGATION 3(简化活跃连接的追踪,ConfigMap 的 monitor-aggregation: medium)。
  2. 代理读取。 pkg/monitor/agent/agent.go:324 handleEvents 打开 perf 读取器,并针对每条记录通过 notifyPerfEventLocked(第 428 行) 分发给消费者。Hubble 就是其中一个消费者(pkg/hubble/monitor/consumer.go:73).
  3. 解析并存入环形缓冲区。 pkg/hubble/observer/local_observer.go:116 Start 的循环将事件 Decode 转换为 Flow(流),经由指标钩子放入 container/ring.go:168 Ring.Write 的固定大小环形缓冲区中。其大小为 2 的幂减 1(默认 4095),因此通过掩码代替除法来获取索引。 GetFlows(第 260 行)只负责读取这个环形缓冲区。
  4. relay 收集。 relay 连接到各个代理的 Hubble 服务器(:4244, TLS)(pkg/hubble/relay/pool/manager.go:51)。 当请求到来时, pkg/hubble/relay/observer/server.go:62 GetFlows 向所有对端发送相同的请求(observer.go:37), 将返回的流按时间顺序重新排序(sortFlows,第 67 行)后输出。

该设计的核心理念是“不进行持久化存储”。流仅存在于节点内存的环形缓冲区中,relay 也不做保存。实测节点上的环形缓冲区在 4095/4095 满载且以每秒 58.5 个的速度写入的情况下,单个节点能记住的历史大约是 约 70 秒。如果需要长期保留,请单独使用指标或 exporter。

5. 与用户贡献的关联

作者已准备好将 4.3 的相关性质修正到文档中的更改(正文第 5 节“仅做好了准备”)。目前尚 未发布。 Cilium 不接收很大程度上由 AI 编写的沟通内容(如 PR 正文等),因此正文必须由作者亲自撰写。所以这里也只写到“仅做好了准备”。

文档位置(v1.20.1) 当前内容 代码依据
Documentation/security/policy/layer7.rst:159 L7 DNS 的 matchPattern 说明中 **. 缺失 GeneratePattern 也 ToUnAnchoredRegexp → 类似 escapeRegexpCharacters
layer7.rst:166, :227 仅匹配一格的示例被称为「any subdomains」 * → [-a-zA-Z0-9_]*(第 141 行)
layer3.rst:548 **. 有说明,但缺少「仅限开头」 第 37 行正则表达式固定为 ^ ,测试的 test.**.cilium.io. 案例

依据是 4.3 的实测表和 Go 复现结果。在准备阶段已确认三份文档文件与 matchpattern.go 的 v1.20.1 和 main 分支相同,草案中如是写道。

6. 实测

2026-10-17,在一个小型集群(8 个节点,无 kube-proxy DaemonSet)中,仅向其中一个控制平面节点的代理 Pod 发送了读取命令。原始数据已另行保存,以下是去除名称和地址后的值。

命令 观察结果
cilium-dbg status KubeProxyReplacement: True, Routing: Network: Tunnel [vxlan] Host: BPF, Attach Mode: TCX, Device Mode: veth, Masquerading: BPF, Host firewall: Enabled,控制器 90/90,模块 125 OK,集群健康状况 8/8
cilium-dbg status --verbose Socket LB: Enabled, Socket LB Coverage: Hostns-only, Backend Selection: Random, XDP Acceleration: Disabled,IPAM 在每个节点 /24 中分配了 15 个
cilium-dbg service list 211 个前端(ClusterIP 171,NodePort 20,LoadBalancer 20)
cilium-dbg bpf lb list, map list cilium_lb4_services_v2 573 格 = 头部 358 格 + 后端槽位 215 格。头部中 127 格为端口 0/ANY 通配符格(具体用途未确认)。后端映射 163,反向映射 231,套接字反向映射 13
bpftool net show 13 个 lxc 设备全部只有 tcx/ingress cil_from_container ,无 egress。物理 NIC cil_from/to_netdev, cilium_host·cilium_net cil_to_host·cil_from_host, cilium_vxlan cil_from/to_overlay,XDP 为空
bpftool prog show 231 个 Cilium 程序(tc 218,cgroup sock_addr 8,cgroup sock 3,用于套接字关闭的 tracing 2)。在 tc 侧,每个端点大约有 3 个入口 + 10 个 tail call
bpftool cgroup tree cgroup 根目录下有 cil_sock4_connect·sendmsg4·recvmsg4·getpeername4·post_bind4·sock_release 等 11 个
状态目录 ls 2 个模板(bpf_lxc.o 1,086,040 字节, bpf_host.o 1,452,768 字节)。13 个端点的 template.txt 哈希值完全相同
hubble observe --last 500 500 个对应 10.5 秒的时长,FORWARDED 489 · DROPPED 11(全部为 UNSUPPORTED_L3_PROTOCOL)。环形缓冲区 4095/4095,58.5 流/秒
cilium-dbg fqdn cache list, fqdn names 该节点为空(FQDN 选择器 0)——该节点上没有绑定 DNS 规则的 Pod
策略数量(整个集群) 22 个 CNP 中包含 rules.dns 的有 5 个,包含 toFQDNs 的有 2 个
relay 日志 通过 :4244、TLS 连接到 8 个对端

有三项内容可读。

7. 从哪里开始阅读

  1. daemon/cmd/cells.go:103 — 浏览单元列表,绘制代理内部包含哪些内容的地图。注释很友好。
  2. pkg/loadbalancer/README.md 和 bpf_reconciler.go:842 — 这是最短路径,用于了解 Cilium 的新型控制平面语法:“表 → reconciler → 映射”。
  3. bpf/bpf_sock.c:298 — 读取映射的端。对照查看第 2 部分中使用的格子是如何被读取的。
  4. pkg/datapath/loader/endpoint.go:195 — 模板、常量、tcx 附加顺序。
  5. pkg/fqdn/dnsproxy/proxy.go:895 从 namemanager/manager.go:214 到
  6. bpf/bpf_lxc.c:1780 cil_from_container — 用户空间代理与 BPF 策略交汇的地方。 #ifdef — 最后阅读。由于存在大量的 tail call 和

,必须先掌握前面五个部分才不会迷失方向。

8. 适合首次贡献的地方

9. 局限性

TT 的一句话总结。 Cilium 的检查站是不会接电话的,它只看提前拿到的规则表。所以如果觉得奇怪,与其去找检查站,不如去找 是谁写的这张表。还有,别忘了 * 是个连一个点都过不去的胆小鬼。

参考

所有资料均于 2026-10-17 查阅。源码为 cilium/cilium v1.20.1 标签(提交 7d68cfb394f2)。

引用的代码: bpf/bpf_sock.c·bpf/bpf_lxc.c 采用 GPL-2.0-only OR BSD-2-Clause, pkg/fqdn/matchpattern/matchpattern.go 采用 Apache-2.0 (Copyright Authors of Cilium)。

登录后即可点赞

评论

还没有评论。

登录后即可发表评论