一、为什么需要 SDN
1.1 传统方式的三个问题
回顾第 17 讲:传统网络中,每台路由器自己运行路由协议、自己算转发表。
问题 1:垂直集成,厂商锁定
路由器 = 专用硬件 + 厂商私有操作系统 + 厂商私有协议实现
⭐ 三者捆绑销售,无法拆开
你不能在 Cisco 的硬件上跑 Juniper 的软件。创新只能等厂商。
问题 2:难以实现"策略"
传统路由协议的目标是「找最短路」。但运营者真正想要的往往是:
「让视频流量走 A 路径,备份流量走 B 路径」
「客户 X 的流量必须经过安全审计设备」
「链路利用率超过 70% 时自动重新分流」
⚠️ 这些需求在传统架构下只能靠在每台设备上手工配置、拼凑各种协议特性(策略路由、路由映射、访问控制列表)来实现。脆弱、易错、难以验证。
问题 3:没有全局视角
每台路由器只根据自己算出的最短路做决策。没有任何实体拥有全网的实时视图,也就无法做全局最优的流量工程。
1.2 SDN 的核心思想
⭐ 把控制平面从设备里剥离出来,集中到一个(逻辑上的)控制器上。
传统: SDN:
┌──────────────┐ ┌──────────────────────┐
│ 控制 + 数据 │ │ ⭐ 控制器(软件) │
│ 控制 + 数据 │ └─┬──────┬──────┬──────┘
│ 控制 + 数据 │ │ │ │
└──────────────┘ ┌──▼──┐┌──▼──┐┌──▼──┐
每台设备各自决策 │数据 ││数据 ││数据 │ ← "哑"转发设备
└─────┘└─────┘└─────┘
二、SDN 的四层架构
┌───────────────────────────────────────────────────┐
│ ④ 网络控制应用 │
│ 路由 · 负载均衡 · 访问控制 · 流量工程 │
└──────────────────────┬────────────────────────────┘
│ ⭐ 北向接口(REST API)
┌──────────────────────▼────────────────────────────┐
│ ③ SDN 控制器(网络操作系统) │
│ 维护网络状态信息 · 提供统一抽象 │
│ (ONOS、OpenDaylight、Ryu) │
└──────────────────────┬────────────────────────────┘
│ ⭐ 南向接口(OpenFlow / P4Runtime / NETCONF)
┌──────────────────────▼────────────────────────────┐
│ ② SDN 控制的交换机 │
│ 只做「匹配 + 动作」(第 19 讲) │
└───────────────────────────────────────────────────┘
│ ① 物理网络:链路与设备 │
└───────────────────────────────────────────────────┘
关键概念
「逻辑上集中,物理上分布」 ⭐
⚠️ 控制器是【逻辑上】集中的,但【物理上】必须是分布式的集群
否则它就是单点故障,而且性能会成为瓶颈
实际实现用 Raft 等共识算法在多个控制器实例间同步状态
北向接口 vs 南向接口
| 方向 | 作用 | 典型协议 | |
|---|---|---|---|
| 北向 | 控制器 ↔ 应用 | 让应用能描述"我想要什么" | REST API |
| 南向 | 控制器 ↔ 交换机 | 下发流表、收集状态 | OpenFlow、P4Runtime、NETCONF |
📌 注意这个架构的抽象层次:应用说「我要让流量避开链路 X」,控制器把它翻译成几百条流表规则下发。**这与操作系统把 open() 翻译成磁盘 IO 是同一种抽象。**这就是控制器被称为「网络操作系统」的原因。
三、OpenFlow 协议
控制器与交换机之间跑在 TCP(通常 6653 端口) 上,可选 TLS 加密。
控制器 → 交换机
| 报文 | 作用 |
|---|---|
| FEATURES | 查询交换机能力 |
| CONFIGURE | 设置配置参数 |
| ⭐ MODIFY-STATE | 增删改流表项(最核心的一个) |
| PACKET-OUT | 让交换机从某端口发出一个指定的分组 |
| READ-STATE | 读取统计计数器 |
交换机 → 控制器
| 报文 | 作用 |
|---|---|
| ⭐ PACKET-IN | 「有个分组我不知道怎么处理」,上送控制器决定 |
| FLOW-REMOVED | 某流表项超时或被删除 |
| PORT-STATUS | 端口状态变化(链路 up/down) |
⭐ PACKET-IN 是 SDN 的关键交互:
分组到达 → 查流表 → 没有匹配项
→ ⭐ 封装进 PACKET-IN 送给控制器
→ 控制器决策,通过 MODIFY-STATE 下发新流表项
→ 后续同类分组直接在数据平面转发(不再麻烦控制器)
⚠️ 这里有一个性能陷阱:如果流表设计不当导致大量分组上送控制器,控制器会成为瓶颈,且引入巨大时延。这也是 DDoS 攻击 SDN 网络的一个途径(构造大量不匹配的流)。
P4:可编程数据平面
OpenFlow 的局限:它只能匹配预先定义好的那些字段。若出现一个新协议,硬件不认识它,OpenFlow 也无能为力。
P4(Programming Protocol-independent Packet Processors) 更进一步:
⭐ 用一种语言【描述交换机应该如何解析报文头部】,
然后编译下发到可编程 ASIC
→ 交换机可以理解任意自定义的协议格式
→ "协议无关" 的转发
📌 从 OpenFlow 到 P4 的演进,是从「可配置」走向「可编程」。
四、ICMP:网络层的信使
4.1 定位
⚠️ ICMP 的层次归属是一道经典的考题。
ICMP 报文【封装在 IP 数据报中】(IP 协议号 = 1)
→ 从封装看,它像是 IP 的上层
但它传递的是【网络层自身的控制信息】
→ 从功能看,它是网络层的一部分
⭐ 标准答案:ICMP 是网络层协议,但在 IP 之上传输。
4.2 报文格式
┌────────┬────────┬─────────────────┐
│ 类型(8) │ 代码(8) │ 校验和 (16) │
├────────┴────────┴─────────────────┤
│ (类型相关字段) │
├───────────────────────────────────┤
│ ⭐ 出错的 IP 数据报头部 + 前 8 字节数据 │
└───────────────────────────────────┘
⭐ 为什么要携带"前 8 字节数据"?因为 TCP/UDP 头部的前 8 字节包含源端口和目的端口——这让主机能知道是哪个进程的哪条连接出了问题,从而把错误报告给正确的应用。
这是一个跨层设计的小巧思:网络层的错误报告,需要传输层的信息才能被正确投递。
4.3 常见类型
| 类型 | 代码 | 含义 |
|---|---|---|
| 0 | 0 | ⭐ Echo Reply(ping 的回复) |
| 3 | 0 | 目的网络不可达 |
| 3 | 1 | 目的主机不可达 |
| 3 | 3 | ⭐ 目的端口不可达(没有进程在监听) |
| 3 | 4 | ⭐ 需要分片但设置了 DF(Path MTU Discovery 靠它) |
| 4 | 0 | 源抑制(已废弃) |
| 8 | 0 | ⭐ Echo Request(ping) |
| 11 | 0 | ⭐ TTL 超时(traceroute 靠它) |
| 12 | 0 | 头部参数错误 |
4.4 traceroute 的完整原理 ⭐
回顾第 3 讲,现在可以讲清全部细节:
① 发送方向目的地发送 UDP 报文段,⭐ 目的端口是一个【极不可能被使用】的值
(传统实现用 33434+)
② 第一批 3 个报文的 ⭐ TTL = 1
③ 第一跳路由器把 TTL 减为 0 → 丢弃 → ⭐ 回送 ICMP【类型 11】超时报文
④ 发送方记录:第 1 跳是谁、往返用了多久(发 3 个所以有 3 个时间)
⑤ TTL = 2,重复 …… 直到 TTL = n 时到达目的主机
⑥ ⭐ 目的主机没有进程监听那个端口 → 回送 ICMP【类型 3 代码 3】端口不可达
⑦ 发送方看到"端口不可达"而不是"超时",知道已经到达终点,停止
两个巧妙之处:
- 用 TTL 当"探测深度"的旋钮——一个为防环路而设计的字段被用来做拓扑发现
- 用"端口不可达"当终止信号——故意用一个不存在的端口,把错误变成信息
⚠️ 常见现象的解释:
| 现象 | 原因 |
|---|---|
* * * |
该跳配置为不回 ICMP,或对 ICMP 限速。不代表不通 |
| 中间某跳时延比后面的跳还大 | 该跳的控制平面 CPU 忙(生成 ICMP 是软件路径),不代表转发慢 |
| 往返路径不对称 | 热土豆路由(第 21 讲),traceroute 只能看到去程 |
📌 Windows 的 tracert 用 ICMP Echo Request 而不是 UDP,因此在某些只放行 ICMP 的网络里表现不同。
4.5 ICMP 的安全考量
⚠️ ICMP 被广泛用于攻击与侦察:
- Smurf 攻击:向广播地址发 ping、伪造源地址为受害者 → 放大攻击
- ICMP 隧道:把数据藏在 ICMP 载荷里绕过防火墙(数据外泄)
- 网络侦察:ping 扫描发现存活主机
但完全屏蔽 ICMP 是错误的:
⚠️ 屏蔽类型 3 代码 4 → ⭐ Path MTU Discovery 失效
→ 大分组被静默丢弃 → 连接建立成功但传数据时"黑洞"
⭐ 这是最难排查的网络故障之一
⚠️ 屏蔽 ICMPv6 → IPv6 完全无法工作(NDP 依赖它,第 19 讲)
📌 正确做法是限速与选择性放行,而不是一律丢弃。
五、网络管理
5.1 要管什么
故障管理(Fault)· 配置管理(Configuration)· 计费(Accounting)
性能管理(Performance)· 安全管理(Security)
⭐ 合称 FCAPS
5.2 SNMP(简单网络管理协议)
三个组成部分:
| 组件 | 说明 |
|---|---|
| MIB(管理信息库) | 被管设备上的一组对象,如「接口 3 的入字节数」 |
| SMI(管理信息结构) | 定义 MIB 对象的数据类型与命名规则 |
| SNMP 协议 | 在管理服务器与被管设备之间传递信息 |
两种操作模式:
⭐ 请求-响应(拉):
管理服务器 ──GetRequest──▶ 被管设备
◀──Response──
⭐ Trap(推):
被管设备 ──Trap──▶ 管理服务器 「我出问题了!」
(不需要等轮询,事件驱动)
⚠️ SNMP 的问题:
- v1/v2c 用明文的 “community string” 当密码(默认还常常是
public)——安全性极差 - v3 加了认证和加密,但配置复杂,部署率不高
- 读容易,写难:SNMP 在监控上很成功,在配置上几乎没人用
5.3 NETCONF / YANG:现代范式
**SNMP 只解决了"看",没解决"改"。**运维人员实际上还是在用 SSH + CLI 脚本改配置——脆弱、无事务、无回滚。
NETCONF(RFC 6241):
运行在 ⭐ SSH 之上,用 XML 编码
提供操作: get-config / edit-config / copy-config / delete-config
⭐ lock / unlock / commit / ⭐ rollback
⭐ 关键改进:事务语义。
配置可以先【提交到候选配置】,验证通过后再【commit】
出错可以【rollback】
多台设备的配置变更可以作为一个整体成功或整体失败
YANG:一种数据建模语言,用来精确描述配置和状态数据的结构。
YANG 定义"配置长什么样" → NETCONF/RESTCONF 负责"怎么传输和应用"
📌 **从 SNMP 到 NETCONF/YANG 的转变,本质上是网络运维从「手工操作 + 监控」走向「基础设施即代码」。**这与软件工程从手工部署走向 CI/CD 是同一场变革。
5.4 实用工具速查
| 工具 | 用途 |
|---|---|
ping |
ICMP Echo,测连通性与 RTT |
traceroute / mtr |
逐跳路径与时延(mtr 持续采样,更实用) |
dig / nslookup |
DNS 查询(第 7 讲) |
ss / netstat |
查看本机连接与端口状态 |
tcpdump / Wireshark |
抓包(Lab 1) |
ip route / ip addr |
查看路由表与接口地址 |
iperf3 |
吞吐量测试 |
curl -v |
查看完整 HTTP 交互 |
六、MPLS:在分组网上模拟电路
6.1 动机
回顾第 2 讲的电路交换 vs 分组交换。MPLS 是一个混合体。
IP 转发的两个痛点:
- 最长前缀匹配相对昂贵(虽然 TCAM 已经很快)
- ⭐ 无法做流量工程——IP 只按目的地址转发,无法让两条去往同一目的地的流走不同路径
6.2 机制
在 IP 头部之前插入一个 MPLS 标签:
┌──────────────────────────────┬─────────┬──────────┐
│ MPLS 头(20 位标签 + 8 位 TTL 等)│ IP 头 │ 数据 │
└──────────────────────────────┴─────────┴──────────┘
⭐ 位于链路层与网络层之间 → 常称"2.5 层"
转发方式:
⭐ 路由器只看【标签】,做一次简单的表查询(不是最长前缀匹配)
然后【交换标签】并转发
→ 这叫"标签交换"(Label Switching)
LSP(Label Switched Path):由一系列标签构成的、预先建立的端到端路径。
📌 这就是"电路"的复活:LSP 本质上是一条虚电路,只不过建在分组网络上。
6.3 用途
| 用途 | 说明 |
|---|---|
| ⭐ 流量工程(TE) | 可以让流量走非最短路径,均衡链路利用率 |
| ⭐ VPN(L3VPN) | 用标签隔离不同客户的流量,这是 MPLS 最大的商业应用 |
| 快速重路由(FRR) | 预先建好备份 LSP,故障时毫秒级切换 |
| QoS | 不同 LSP 给不同的服务等级 |
⚠️ MPLS 需要一个额外的信令协议来分发标签(LDP 或 RSVP-TE),增加了复杂度。
📌 今天的地位:MPLS 在运营商核心网和企业专线(MPLS VPN)中仍然是主流,但在数据中心内部已大量被 VXLAN 等基于 IP 的隧道方案取代,在广域网接入侧则面临 SD-WAN 的竞争。
七、例题(Worked Example)
题目:某工程师执行 traceroute example.com,得到如下输出:
1 192.168.1.1 1.2 ms 1.1 ms 1.1 ms
2 10.10.0.1 8.4 ms 9.1 ms 8.7 ms
3 * * *
4 203.0.113.9 45.2 ms 44.8 ms 45.1 ms
5 198.51.100.2 22.3 ms 22.1 ms 22.5 ms
6 93.184.216.34 23.0 ms 22.8 ms 23.1 ms
(a) 第 3 跳的 * * * 说明该路由器故障了吗?
(b) 第 4 跳的 45 ms 比第 5 跳的 22 ms 还大,这可能吗?为什么?
(c) traceroute 如何知道第 6 跳是终点,从而停止?
(d) 若该工程师在防火墙上屏蔽了所有 ICMP,会发生什么?
解答:
(a) 不能说明故障。
判据很明确:第 4、5、6 跳都正常返回了,说明探测分组确实穿过了第 3 跳。它只是被配置为不回送 ICMP 超时报文(出于安全策略),或对 ICMP 的生成做了速率限制。
(b) 完全可能,而且很常见。
两个原因:
- ⭐ **生成 ICMP 报文走的是路由器的控制平面(软件路径),而转发走的是数据平面(硬件路径)。**如果第 4 跳的 CPU 正忙,它生成 ICMP 响应的时间就会很长——这反映的是该路由器 CPU 的繁忙程度,不是它的转发性能。
- ⭐ **返回路径可能不同。**由于热土豆路由(第 21 讲),第 4 跳回送 ICMP 时走的返程路径,可能与第 5 跳的返程路径完全不同,且更长。
📌 重要结论:traceroute 显示的是「到该跳的往返时延」,不是「到该跳的单向时延」,也不是路径上的累积时延。中间某跳数值偏高,通常不构成性能问题的证据。
(c) 因为第 6 跳返回的是 ICMP 类型 3 代码 3(端口不可达),而不是类型 11(TTL 超时)。
traceroute 故意把 UDP 目的端口设成一个极不可能有进程监听的值(如 33434+)。当分组终于到达目的主机时,主机的传输层发现没有进程在那个端口上,就回送"端口不可达"——traceroute 把这个错误当作"我到了"的信号。
(d) 会造成多重故障:
① traceroute 完全失效(收不到类型 11 和类型 3)
② ping 失效(收不到类型 0)
③ ⭐ 最严重:Path MTU Discovery 失效
屏蔽类型 3 代码 4 后,发送方永远收不到"需要分片"的通知
→ 大分组被静默丢弃
→ 症状:TCP 三次握手成功、小请求正常,但一传大数据就【卡死】
⭐ 这就是俗称的"PMTUD 黑洞",是最难排查的网络故障之一
④ 若是 IPv6,屏蔽 ICMPv6 会让 NDP 失效 → 网络完全不通(第 19 讲)
正确做法:对 ICMP 限速并选择性放行必要类型(尤其是类型 3 代码 4 和 ICMPv6 的 NDP 相关类型),而不是一律丢弃。
八、随堂自测
- 传统每路由器控制平面的三个问题是什么?
- SDN 控制器为什么必须「逻辑集中、物理分布」?
- PACKET-IN 报文在什么时候产生?它可能带来什么性能问题?
- P4 相对 OpenFlow 的进步是什么?
- ICMP 属于第几层?为什么这个问题有争议?
- ICMP 报文为什么要携带出错数据报的前 8 字节数据?
- 完整描述 traceroute 的工作原理,包括它如何判断到达终点。
- SNMP 的三个组成部分是什么?它为什么在配置管理上失败了?
- NETCONF 相对 SNMP 的关键改进是什么?
- MPLS 解决了 IP 转发的什么问题?为什么说它是"2.5 层"?
九、本讲要点回顾
- 传统控制平面的三个问题:垂直集成锁定、难以实现策略、缺乏全局视角。
- SDN 四层:物理网络 → SDN 交换机 → 控制器 → 网络应用;南向 OpenFlow,北向 REST。
- ⭐ 控制器逻辑集中、物理分布,否则就是单点故障。
- PACKET-IN:流表未命中时上送控制器——也是性能瓶颈与攻击面。
- P4 = 从可配置走向可编程(协议无关的报文解析)。
- ICMP 是网络层协议,但封装在 IP 之上;携带出错数据报头部 + 前 8 字节(含端口号)。
- ⭐ traceroute = 递增 TTL 触发类型 11,用"端口不可达"(类型 3 代码 3)判断终点。
- ⚠️ 一律屏蔽 ICMP 会导致 PMTUD 黑洞和 IPv6 完全瘫痪。
- SNMP 擅长监控,不擅长配置;NETCONF/YANG 带来了事务、回滚与「基础设施即代码」。
- MPLS 是"2.5 层"标签交换,主要价值在流量工程与 L3VPN。
十、自测答案
1. ① 垂直集成:硬件、操作系统、协议实现由同一厂商捆绑,无法混搭,创新受制于厂商节奏。② 难以实现策略:路由协议只会算最短路,运营者真正想要的策略(按业务分流、强制经过某设备、按利用率调度)只能靠在每台设备上手工拼凑配置来近似,脆弱且难以验证。③ 缺乏全局视角:没有任何实体拥有全网实时状态,无法做全局最优的流量工程。
2. 逻辑集中是为了给应用提供「整个网络是一台设备」的统一抽象,从而能做全局决策;物理分布是必需的,因为单个控制器实例会成为单点故障(它挂了整个网络失去控制能力)和性能瓶颈(成千上万台交换机的事件都要它处理)。实际部署用 Raft 等共识算法在多个控制器实例间同步状态。
3. 当一个分组到达交换机、在流表中找不到匹配项时产生——交换机把该分组(或其头部)封装进 PACKET-IN 送给控制器请求决策。性能问题:若流表设计不当(规则过于具体、通配不足),大量分组会上送控制器,导致 ① 控制器 CPU 与控制通道成为瓶颈;② 这些分组经历巨大的额外时延。这也构成一种攻击面——攻击者构造大量互不相同的流,故意制造 PACKET-IN 风暴。
4. OpenFlow 只能匹配预先在标准中定义好的固定字段集合,遇到新协议或自定义头部就无能为力。P4 允许用一种语言描述交换机应当如何解析报文头部,并编译下发到可编程 ASIC,因而可以支持任意自定义的协议格式——从「可配置」走向「可编程」。
5. 是网络层协议。争议来源于:从封装上看,ICMP 报文装在 IP 数据报里(协议号 1),位置像是 IP 的上层用户;但从功能上看,它传递的是 IP 自身的差错与控制信息,是网络层不可分割的一部分(IPv6 中 NDP 甚至依赖它才能工作)。标准表述是:ICMP 是网络层协议,但其报文在 IP 之上传输。
6. 因为 TCP/UDP 头部的前 8 字节恰好包含源端口号和目的端口号。有了它们,收到 ICMP 差错报文的主机才能确定是哪个进程的哪条连接触发了错误,从而把差错正确地报告给对应的应用(例如把"端口不可达"转换成应用层的 Connection refused)。这是一个跨层设计:网络层的错误报告需要传输层的信息才能被正确投递。
7. 发送方向目的地发送探测分组(传统实现用 UDP,目的端口设为一个极不可能被监听的值如 33434+),TTL 从 1 开始逐次递增,每个 TTL 值发 3 个探测。TTL 为 n 的分组在第 n 跳被减到 0 而丢弃,该路由器回送 ICMP 类型 11(TTL 超时),发送方据此记录第 n 跳的地址与往返时延。当 TTL 足够大、分组终于到达目的主机时,目的主机发现没有进程监听那个端口,回送 ICMP 类型 3 代码 3(端口不可达)——收到的是"端口不可达"而非"超时",就意味着已经到达终点,traceroute 随即停止。
8. ① MIB(管理信息库:被管设备上的对象集合);② SMI(管理信息结构:定义 MIB 对象的数据类型与命名规则);③ SNMP 协议本身(传递 Get/Set 请求与 Trap 通知)。它在配置管理上失败,原因是:安全模型薄弱(v1/v2c 用明文 community string,因此没人敢开放写权限)、缺乏事务语义(没有 commit/rollback,一半成功一半失败无法处理)、以及缺少跨设备的一致性保证。结果是运维人员宁愿用 SSH + CLI 脚本,SNMP 沦为纯监控协议。
9. 关键改进是⭐ 事务语义:配置可以先写入候选配置、经验证后 commit,出错可 rollback,并支持 lock 防止并发修改冲突;多台设备的变更可以作为一个整体成功或整体失败。此外 NETCONF 跑在 SSH 上(安全性远好于 SNMPv2c),用 YANG 精确建模配置结构(使配置可校验、可自动生成代码),这共同支撑了「网络基础设施即代码」的运维范式。
10. MPLS 解决了 ① 最长前缀匹配的开销(改为简单的标签表查询)和 ② ⭐ IP 无法做流量工程的问题——IP 只按目的地址转发,两条去往同一目的地的流必然走同一路径,而 MPLS 可以为它们建立不同的 LSP,从而实现负载均衡和非最短路径调度。称它"2.5 层",是因为 MPLS 头部插在链路层头部之后、IP 头部之前,既不属于链路层也不属于网络层,介于两者之间。