NetKAT 2014 — 把网络转发写成可以做数学等式变换的代数式
是什么
NetKAT 是一门用代数式来描述网络转发行为的领域语言。日常类比:传统配置一台交换机,像写一份巨长的 if-else 清单——『来自 10.0.0.1 的就转 2 号口;除非目的端口 80,那就转 3 号口;除非……』;NetKAT 让你把这堆清单写成一个代数表达式,然后像因式分解多项式一样去化简、比较两份配置是否等价。
核心一句话:
NetKAT = Kleene Algebra with Tests (KAT) 的网络实例化。包字段匹配 = 测试,包字段修改 = 动作,再用四个算子拼起来:
;(顺序)、+(并行)、*(任意跳数)、dup(记录历史)。
它给 SDN 控制面提供了第一个有完备等式公理化的语义底座——意味着两份配置是否做相同的事,可以用机械算法判定。
为什么重要
不理解 NetKAT,下面这些事都没法解释:
- 为什么 2014 之后所有网络验证论文(Veriflow / NoD / Batfish 等)都把转发表抽成『代数对象』再分析
- 为什么 AWS / Microsoft 内网敢说『我能形式化证明这个 ACL 不会让外网包进数据库』
- 为什么 frenetic-2011 给了语言、却必须等三年才出 NetKAT——前者只是『换个写法』,NetKAT 才把『写法』变成『可证明』
- 为什么 SDN 圈这么爱抄 PL 的工具(KAT、CTL、模型检查)——因为转发等价问题真的就是程序等价问题
一句话:NetKAT 把网络配置等价检查变成了正则表达式等价检查。
核心要点
NetKAT 把策略拆成 6 个原子构件:
- 测试:
f = v,比如dstport = 80,意思是『包的 dstport 字段是不是 80』,返回 0 或 1。 - 动作:
f := v,比如port := 2,意思是『把包的输出端口改成 2』。 - 顺序
;:p ; q表示先做 p 再做 q。比如dstport=80 ; port:=2,读作:测试 dstport=80,通过则把端口改成 2。 - 并行
+:p + q表示两个策略同时生效,包会被复制到所有匹配的分支。两份独立写的策略可以直接+起来。 - Kleene 星
*:p*表示『把 p 重复任意次』。这正好对应『包在拓扑里跳任意多跳』,因此能描述端到端可达性。 - dup:把当前包状态压进包历史。NetKAT 的语义不是『一个包』而是『包+它走过的路径』,这是它能验证路径属性(不只是终点)的关键。
整套语言的语义是:策略 = 函数 (包历史) → (包历史集合)。Anderson 等人证明了这套语义有 sound + complete 等式公理化——任何两个语义相等的程序都能用有限的等式互推。
实践案例
案例 1:用代数式写一条最简单的转发规则
『所有目的端口是 80 的包,从 1 号口转出去到 2 号口』:
dstport = 80 ; port := 2读作:先测试 dstport=80,通过的包,把它的 port 字段改成 2。
如果还要让 22 端口的包走 3 号口,直接『加』上去:
(dstport = 80 ; port := 2) + (dstport = 22 ; port := 3)不像 OpenFlow 流表要纠结优先级——NetKAT 的 + 自动是并行集合并。
案例 2:端到端可达性
问:从 1 号口进的包,能不能走到 9 号口出?写成 NetKAT:
(in = 1) ; (拓扑转移 ; 单跳策略)* ; (out = 9)中间那段 (...)* 是 Kleene 星——任意跳数。判断这条表达式语义是否非空,就回答了可达性。这正是 PL 里的『正则表达式空性测试』。
案例 3:流量隔离的等式验证
要证『A 域包永远不会进 B 域』:
(srcip ∈ A 网段) ; 整网策略 ; (dstip ∈ B 网段) ≡ 00 是『丢弃一切的策略』。如果上式恒等于 0,就证明了隔离。这条等式可以让机器自动判定。
案例 4:把两份独立写的策略合在一起
A 同事写了一份『让所有 SSH 走管理网段』,B 同事写了一份『让所有 HTTP 走业务网段』。在传统 OpenFlow 里,A 和 B 的合并要手动协商优先级;NetKAT 里直接:
policy_A + policy_B如果两条规则的测试条件不相交(SSH ≠ HTTP),代数化简会自动得出『互不影响』;如果有交叠,化简结果会显式暴露交叠区——比让两个工程师在 Slack 里吵清楚优先级要可靠得多。
案例 5:编译器正确性
NetKAT 的实用价值之一是『高层策略 → OpenFlow 流表』的编译器,可以形式化证明两边语义等价。论文给了一个 reference compiler,并证明:对任意 NetKAT 程序 p,编译出的流表语义 [[compile(p)]] ≡ [[p]]。这种『编译器自带证明』在 cakeml 这类工作里也能看到,思路一脉相承。
踩过的坑
- 包历史比命令式语义烧脑:第一次读 dup,会以为它是『复制』,其实是『把当前快照压栈』。理解了它才能解释路径属性为何能被表达。
- Kleene 星在环路拓扑里要小心:
p*在数学上很干净,但物理网络真的有环路时,需要配合 TTL/bookkeeping,否则语义和实现脱节。 - 完备性 ≠ 高效:原始判定算法是 PSPACE 的;POPL 2015 的 coalgebraic decision procedure 才让它工程可用。看到论文别以为可以直接拿来跑。
- NetKAT 不管 QoS / 带宽 / 时序:只描述『包去哪』『字段怎么改』。要表达延迟、丢包、概率,需要后续的 Probabilistic / Temporal NetKAT 扩展。
+不是『或』而是『并集』:第一次读会以为它像 if-else 的另一支,其实它是『包同时被两条规则各处理一份』。误读会让你写出『以为只有一份输出』的策略。
适用 vs 不适用场景
适用:
- SDN 控制面策略的等价/可达/隔离形式化验证
- 多份策略合并时的冲突检测(
p1 + p2化简) - SDN 编译器正确性证明(高层 NetKAT → OpenFlow 流表)
- 网络切片(slice)正确性证明
不适用:
- 需要带宽 / 延迟 / QoS 表达——基础 NetKAT 不支持
- 需要概率行为(链路丢包率)——用 Probabilistic NetKAT
- 需要时序属性(最终一致、活性)——用 Temporal NetKAT
- 直接代替 BGP / OSPF 等分布式协议设计——NetKAT 是控制面策略层语言,不是协议层
历史小故事(可跳过)
- 1997 年:Dexter Kozen 提出 Kleene Algebra with Tests,把『正则表达式 + 布尔测试』做成一套统一的程序代数。当时主要用来证程序循环等价。
- 2011 年:Foster 等发表 frenetic-2011,把 SDN 控制面写成函数式风格——但语义是操作式的,没有等式公理化。
- 2014 年:Anderson、Foster、Guha、Jeannin、Kozen、Schlesinger、Walker 把 KAT 实例化到网络场景,加
dup得到包历史语义,证完备性,发 POPL。KAT 等了 17 年终于找到了它的杀手级应用。 - 2015 之后:coalgebraic decision procedure(POPL 2015)让等价判定真的能跑;Temporal / Probabilistic 扩展接连出现;工业级网络验证工具开始借鉴这套代数。
学到什么
- 网络转发本质上就是程序——它有顺序、分支、循环。用 PL 工具去分析它,比发明新语义更有杠杆。
- 代数式比清单式强在哪:清单只能逐条比较;代数式能因式分解、化简、机器判定等价。
- 包历史是关键创新:把『一个包』升级成『包+路径』,让路径属性(不只是终点)也能形式化。
- 完备性公理化的价值:任何语义相等的两份程序都能用有限步等式互推;这把『验证』从『模型检查』变成『代数演算』。
延伸阅读
- 论文 PDF:NetKAT POPL 2014
- 后续工作:A Coalgebraic Decision Procedure for NetKAT, POPL 2015(让等价判定工程可用)
- 视频教程:Nate Foster 的 NetKAT 系列讲座
- KAT 背景:Kozen, “Kleene Algebra with Tests”, TOPLAS 1997(密度高,看不懂正常)
关联
- frenetic-2011 —— Frenetic 给 SDN 引入函数式抽象;NetKAT 是它的代数化继任者
- lamport-tla-1994 —— TLA+ 也是『把行为抽成数学对象再验证』,思路同源
- hoare-logic —— Hoare 逻辑用断言验证程序;NetKAT 用等式验证策略,殊途同归
- lambda-calculus —— PL 用 λ-演算做底,网络则用 KAT 做底
- boogie-2005 —— Boogie 把验证拆成中间语言;NetKAT 把验证拆成代数式
反向链接
- cakeml —— CakeML — 从源码到机器码每一步都被数学证明的 ML 编译器
- frenetic-2011 —— Frenetic 2011 — 把 OpenFlow 流表换成函数式程序
- hoare-logic —— Hoare Logic — 把”程序对不对”变成”数学证明对不对”
- lambda-calculus —— λ-演算 — 用三条规则表达所有可计算函数
- lamport-tla-1994 —— TLA — 把状态机和时序逻辑捏成一个公式