vault backup: 2026-05-17 22:27:07

This commit is contained in:
hhs
2026-05-17 22:27:07 +08:00
parent 64e34e6871
commit bbea71f62b
45 changed files with 8983 additions and 0 deletions
@@ -0,0 +1,187 @@
---
tags: [计算机网络, IPv4, IP首部, TTL, MTU, 分片]
create time: 2026-05-17 23:40
---
# IPv4 首部与分段重组
## 概述
IPv4 数据包是互联网的基本传输单元。理解其首部结构,有助于排查 MTU 问题、分析抓包工具输出、以及理解 IP 级故障恢复机制。
## IPv4 首部格式(32-bit fields)
```
Version(4b) │ IHL(4b) │ DSCP/ECN(8b) │ Total Length(16b) │
├─────────────────────────────────────────────────────────────────┤
│ Identification(16b) │DF│ MF │Offset(13b)│
├─────────────────────────────────────────────────────────────────┤
│ TTL(8b) │ Protocol(8b) │ Header Checksum │
├─────────────────────────────────────────────────────────────────┤
│ Source Address (32b) │
├─────────────────────────────────────────────────────────────────┤
│ Destination Address (32b) │
├─────────────────────────────────────────────────────────────────┤
│ Options (variable) │
│ ...padding... │
└─────────────────────────────────────────────────────────────────┘
```
### 各字段详解
| 字段 | 大小 | 说明 |
|------|------|------|
| **Version** | 4 bit | 版本号,IPv4 = `0100` |
| **IHL** (Internet Header Length) | 4 bit | 首部长度,单位 4 bytes。**最小值 5**(20 bytes),最大 15(60 bytes) |
| **DSCP** (Differentiated Services Code Point) | 6 bit | QoS 优先级标记;前 3 bit 为 ECN(Explicit Congestion Notification)|
| **Total Length** | 16 bit | 整个 IP 包总长(含首部),最大 65535 字节 |
| **Identification** | 16 bit | 唯一标识符。同一原始包的分片共享此 ID |
| **DF** (Don't Fragment) | 1 bit | 置 1 则禁止分片;路由器不能分片的包如果 DF=1,直接丢弃并返回 ICMP "Fragmentation Needed" |
| **MF** (More Fragments) | 1 bit | 置 1 表示后面还有分片;最后一个分片 MF=0 |
| **Fragment Offset** | 13 bit | 该分片在原始数据中的偏移量(以 8 字节为单位) |
| **TTL** (Time To Live) | 8 bit | 生存时间,每经过一个路由器减 1,归零时丢弃并发送 ICMP Timeout |
| **Protocol** | 8 bit | 上层协议编号:TCP=6, UDP=17, ICMP=1, IGMP=2 |
| **Header Checksum** | 16 bit | 仅校验首部的 CRC,**不校验 payload** |
| **Source/Dest Address** | 各 32 bit | 源和目的 IP 地址 |
### 最小 vs 最大 IP 包
```
最小 IP 包: 20B 首部 + 0B 数据 = 20 bytes
最小以太网帧承载: 20B 首部 + 8B ICMP Echo = 28 bytes
→ 加上 14B EthHdr + 4B FCS = 46 bytes ≥ MTU 下限 ✅
最大 IP 包: 20B 首部 + 65515B 数据 = 65535 bytes
受限于链路 MTU: 通常 1500B Payload → 最大常用包 1520 bytes
```
## TTL 深度解析
### TTL 的常见误解
很多人以为 TTL 的单位是秒。其实在 IPv4 中它是**跳数计数器**——每经过一个路由器(三层转发节点)减 1,不是按时间递减。
```mermaid
flowchart LR
A["Source<br/>TTL=64"] -->|"路由器减1"| B["TTL=63"]
B -->|"路由器减1"| C["TTL=62"]
C -->|"路由器减1"| D["TTL=61"]
D -->|"到目的地"| E["TTL=60<br/>继续处理"]
style A fill:#DDA0DD,color:#000
style E fill:#98FB98,color:#000
```
### 查看 TTL 的实践
```bash
# ping 默认从 64 或 128 开始,减去到达时的 TTL ≈ 经过了跳数
$ ping -c 1 example.com
PING example.com (93.184.216.34): 56 data bytes
64 bytes from ... icmp_seq=0 ttl=55 time=23.1 ms
# 64 - 55 = 9 跳
# Windows 默认 TTL=128
$ tracert example.com
1 1 ms 1 ms 1 ms 192.168.1.1
2 2 ms 2 ms 2 ms 10.0.0.1
...
9 23 ms 23 ms 23 ms 93.184.216.34
```
### Linux 系统初始 TTL
```bash
$ sysctl net.ipv4.ip_default_ttl
net.ipv4.ip_default_ttl = 64
# macOS / Windows 默认 128
# 某些嵌入式设备可能用 32 或 60
```
## MTU 与分片(Fragmentation)
### 什么是 MTU?
MTU(Maximum Transmission Unit)是链路层允许的最大 IP 包载荷大小。超出 MTU 的包需要被**分片**。
| 链路类型 | 典型 MTU | Jumbo Frame |
|----------|---------|-------------|
| Ethernet | 1500 bytes | 9000 bytes |
| Wi-Fi (802.11n/ac/ax) | 1500 | ❌ 不支持 |
| PPPoE (宽带拨号) | 1492 (预留 8B PPPoE头) | — |
| IPv6 | 1280 (硬性最小) | 可选 |
| GRE 隧道 | 1476 (预留 24B GRE头) | — |
| VXLAN | 1450 (预留 50B VxLAN头) | — |
### 分片规则
当 IP 包 > MTU 且 DF=0 时,路由器将其切分为多个分片:
```
原始 IP 包 (Payload = 3000 bytes) ── MTU=1500 ──→
分片 1: Headers + [Bytes 0..1479] Offset=0 MF=1
分片 2: Headers + [Bytes 1480..2959] Offset=185*8=1480 MF=1
分片 3: Headers + [Bytes 2960..2999] Offset=370*8=2960 MF=0 (最后一片)
↑ offset × 8 = 实际偏移
```
**关键限制:** 除最后一个分片外,每个分片的 payload 必须是 **8 字节的整数倍**。这就是为什么 offset 字段以 8 字节为单位。
### 分片的优缺点
| 优点 | 缺点 |
|------|------|
| 允许大应用层数据穿越小 MTU 链路 | 增加路由器的 CPU 负担 |
| 透明(对应用层无感知) | 任一丢包导致整个原始包失效 |
| — | 攻击者可以利用分片做碎片攻击(Teardrop 等) |
| — | IPv6 已取消中间路由器分片,改由端到端 PMTUD |
### PMTUD(Path MTU Discovery)
现代网络推荐使用 **路径 MTU 发现**而非分片:
```mermaid
sequenceDiagram
participant Src as 源主机
participant R as 瓶颈路由器(MTU=1400)
participant Dst as 目标服务器
Src->>Dst: IP Packet (size=1500, DF=1)
Note over Src: DF(Do Not Fragment)=1
R->>R: MTU=1400 < 1500, DF=1 → 无法分片!
R->>Src: ICMP Type 3 Code 4 "Fragmentation Needed"
Note over Src: Path MTU = 1400 - IP头(20) - Eth(14) = 1366
Src->>Dst: IP Packet (size=1366, DF=1) ✅
Dst-->>Src: ACK ✅
```
```bash
# 检查当前 PMTUD 状态
$ ip route show | grep mtu
default via 192.168.1.1 dev eth0 mtu 1500
# Linux 内核 PMTUD 控制
$ sysctl net.ipv4.tcp_mtu_probing
net.ipv4.tcp_mtu_probing = 0 # 0=关闭, 1=低速率, 2=始终开启
```
## 协议编号对照表
| Protocol 值 | 名称 | 用途 |
|------------|------|------|
| 1 | ICMP | 网络诊断(ping、traceroute) |
| 2 | IGMP | 组播管理 |
| 6 | TCP | 可靠面向连接传输 |
| 17 | UDP | 无连接不可靠传输 |
| 47 | GRE | 虚拟私有隧道 |
| 50 | ESP | IPsec 加密封装安全载荷 |
| 51 | AH | IPsec 认证头 |
| 89 | OSPF | 动态路由协议 |
## 关联笔记
- [[hhs/NETWORK/CIDR与子网划分]] — IP 地址的子网分配方法
- [[hhs/NETWORK/NAT原理与应用]] — NAT 如何处理 IP 包的 Checksum
- [[hhs/NETWORK/ICMP与Ping-Traceroute]] — ICMP 协议细节
@@ -0,0 +1,182 @@
---
tags: [计算机网络, CIDR, 子网划分, IP计算]
create time: 2026-05-18 00:00
---
# CIDR 与子网划分
## 概述
CIDR(Classless Inter-Domain Routing,无类别域间路由)是现代 IP 寻址的核心机制。它摒弃了 A/B/C 类地址的固定边界,允许任意长度的前缀——让网络管理员可以按需精确分配 IP 空间。
> [!QUESTION] 为什么需要子网划分?
> 想象一个有 5000 台主机的公司,如果全放在 /12 大网络里:每秒数千个 ARP 广播、巨大的 MAC 表、单点故障影响面极大。子网划分将大问题拆小,既缩小了广播域,又加强了安全性隔离。
## CIDR 表示法
```
192.168.1.0/24 ← /24 表示前 24 位是网络号
```
即 `192.168.1.0` + 子网掩码 `255.255.255.0`
### 常见子网对照表
| CIDR | 子网掩码 | 主机数 ( usable) | 典型用途 |
|------|---------|-----------------|---------|
| /30 | 255.255.255.252 | 2 | 点对点链路 |
| /29 | 255.255.255.248 | 6 | 小型办公室 |
| /28 | 255.255.255.240 | 14 | VLAN 极小 |
| /27 | 255.255.255.224 | 30 | 小组 VLAN |
| /26 | 255.255.255.192 | 62 | 小型部门 |
| /25 | 255.255.255.128 | 126 | 中型部门 |
| /24 | 255.255.255.0 | 254 | **最常用**,标准办公室 VLAN |
| /23 | 255.255.255.254 | 510 | 大型部门 |
| /22 | 255.255.255.252 | 1022 | 数据中心租户 |
| /21 | 255.255.255.248 | 2046 | 大规模部署 |
| /16 | 255.255.255.0 | 65534 | 大型企业内网 |
> [!tip] 快速计算可用主机数
> $N_{usable} = 2^h - 2$,其中 $h$ 是主机位数量($h = 32 - \text{prefix}$)
> - `-2` 因为网络地址和广播地址不可用
> - 对于 /31(点对点链路),RFC 3021 允许使用 2 个地址(无网络/广播位)
## 子网划分的核心算法
### 示例:从 /24 划分为 4 个子网
原始:`192.168.1.0/24`(254 台主机)
目标:分成 4 个子网 → 需借 2 位主机位 → `/26`
```
原始 /24 的网络位:
192.168.1.0 = 11000000.10101000.00000001.00000000
借 2 位给子网:
192.168.1.XX = XX 是子网位(00, 01, 10, 11)
四个子网:
Subnet 0: 192.168.1.0/26 — 范围: .0 ~ .63 — 可用: .1 ~ .62 — Bcast: .63
Subnet 1: 192.168.1.64/26 — 范围: .64 ~ .127 — 可用: .65 ~ .126 — Bcast: .127
Subnet 2: 192.168.1.128/26 — 范围: .128~.191 — 可用: .129~.190 — Bcast: .191
Subnet 3: 192.168.1.192/26 — 范围: .192~.255 — 可用: .193~.254 — Bcast: .255
```
```mermaid
flowchart LR
S0["192.168.1.0/26<br/>.1-.62"] -->|"PCs / Servers"| SW0[Switch-A]
S1["192.168.1.64/26<br/>.65-.126"] -->|"IP Phones"| SW1[Switch-B]
S2["192.168.1.128/26<br/>.129-.190"] -->|"Guest WiFi"| AP1[AP-C]
S3["192.168.1.192/26<br/>.193-.254"] -->|"IoT Devices"| GW1[Gateway .1]
style S0 fill:#DDA0DD,color:#000
style S1 fill:#FFD700,color:#000
style S2 fill:#98FB98,color:#000
style S3 fill:#B0C4DE,color:#000
```
### VLSM(可变长子网掩码)
不同子网可以用不同长度的掩码——这就是**可变长子掩码**。
场景:公司有 4 个部门,需要不同规模:
- 研发部:200 台 → 需要至少 202 → /24(254 台)
- 市场部:50 台 → 需要至少 52 → /26(62 台)
- 行政部:10 台 → 需要至少 12 → /28(14 台)
- 互联链:2 台 → 需要 2 → /30(2 台)
总计借用:32 - 24 = 8 位主机位
- 研发 /24: 借回 0 位子网位 → 用掉 1 个大块
- 剩余给其他 3 个 /26, /28, /30...
## 实用计算工具
### Linux ipcalc
```bash
$ ipcalc 192.168.10.50/26
Network: 192.168.10.0/26
Netmask: 255.255.255.192 = 26
Wildcard: 0.0.0.63
Broadcast: 192.168.10.63
HostMin: 192.168.10.1
HostMax: 192.168.10.62
Hosts/Net: 62
# Python 中内置 ipaddress 模块
$ python3 -c "import ipaddress; print(list(ipaddress.ip_network('192.168.10.0/26').hosts()))"
[IPv4Address('192.168.10.1'), ..., IPv4Address('192.168.10.62')]
```
### 快速手算方法
| 步骤 | 操作 |
|------|------|
| 1 | 将最后一个 octet 转换为二进制 |
| 2 | 前 prefix%32 位为网络位,其余为主机位 |
| 3 | 网络地址 = 全置 0;广播地址 = 全置 1 |
| 4 | 可用范围 = 网络地址+1 ~ 广播地址-1 |
## 私有地址空间(RFC 1918)
以下地址段在公网不可路由,专供内部网络使用:
| 地址段 | 数量 | 默认掩码 | 适用场景 |
|--------|------|---------|---------|
| `10.0.0.0 – 10.255.255.255` | 16,777,216 | /8 | 大型企业、运营商 |
| `172.16.0.0 – 172.31.255.255` | 1,048,576 | /12 | 中型企业 |
| `192.168.0.0 – 192.168.255.255` | 65,536 | /16 | 家庭/小型办公室 |
> [!tip] 如何选择私有地址段?
> - 单机房、<1000 台主机 → `192.168.0.0/16` 够用
> - 多机房、分布式 → `10.0.0.0/8` 留有余量
> - Docker 默认使用 `172.17.0.0/16`
## 超网聚合(Supernetting / CIDR Aggregation)
将多个连续的小网合并为一个更大的网:
```
原始: 192.168.1.0/24, 192.168.2.0/24, 192.168.3.0/24
= 11000000.10101000.00000001.0/24
+ 11000000.10101000.00000010.0/24
+ 11000000.10101000.00000011.0/24
共同前缀: 11000000.10101000.000000XX → /22
聚合后: 192.168.0.0/22 包含 1024 个 IP
(注意:实际只用了其中两个 /24 块,浪费了中间两个)
```
路由聚合减少了全球 BGP 路由表的大小。没有 CIDR 聚合,BGP 路由表会膨胀到百万级别。
## Go 中的子网判断
```go
import "net"
ip := net.ParseIP("192.168.1.50")
_, cidr, _ := net.ParseCIDR("192.168.1.0/26")
if cidr.Contains(ip) {
fmt.Println("IP 在子网范围内 ✅")
}
// 遍历所有可用 IP
for ip := cidr.IP.Mask(cidr.Mask); cidr.Contains(ip); inc(ip) {
if ip.Equal(cidr.IP) || ip.Equal(lastIP) {
continue // 跳过网络地址和广播地址
}
// 处理每个可用 IP
}
func inc(ip net.IP) { for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] > 0 { break }
}}
```
## 关联笔记
- [[hhs/NETWORK/IPv4首部与分段重组]] — IP 地址在 IPv4 包中的位置
- [[hhs/NETWORK/IPv6地址与扩展头部]] — IPv6 同样使用 CIDR 表示法
- [[hhs/NETWORK/NAT原理与应用]] — NAT 通常映射到私有地址空间
@@ -0,0 +1,165 @@
---
tags: [计算机网络, IPv6, SLAAC, 地址空间]
create time: 2026-05-18 00:20
---
# IPv6 地址与扩展头部
## 概述
IPv6 是为解决 IPv4 地址枯竭而设计的下一代互联网协议。但它的改进远不止于地址空间——简化首部、内置安全支持、无分片(by design)、自动配置等特性使其更适合现代网络架构。
## IPv6 地址表示法
### 十六进制冒号分隔格式
```
2001:0db8:0000:0000:0000:ff00:0042:8329
```
### 缩写规则
| 规则 | 说明 |
|------|------|
| 前导零省略 | `0db8` → `db8`;`0042` → `42` |
| 连续全零段压缩为 `::` | 只能出现**一次**(否则歧义) |
示例:
```
完整: 2001:0db8:0000:0000:0000:ff00:0042:8329
缩写: 2001:db8::ff00:42:8329 ← 最简形式
loopback: 0000:0000:0000:0000:0000:0000:0000:0001
::1 ← 仅一个字符
IPv4映射: 0000:0000:0000:0000:0000:ffff:c0a8:0101
::ffff:192.168.1.1 ← 兼容旧系统
```
### 常见 IPv6 地址类型
| 前缀 | 类型 | 示例 | 范围 |
|------|------|------|------|
| `2000::/3` | 全球单播 (GUA) | `2001:db8::1` | 公网路由 |
| `::1/128` | 环回 (Loopback) | `::1` | 本机 |
| `fc00::/7` | ULA (唯一本地地址) | `fd00::1` | 私有地址,等价 RFC1918 |
| `fe80::/10` | 链路本地 (Link-Local) | `fe80::1` | 仅同链路可用,自动配置 |
| `ff00::/8` | 组播 (Multicast) | `ff02::1` | 多播组 |
| — | 未指定地址 | `::` | 类似 IPv4 的 0.0.0.0 |
## IPv6 地址组成
### EUI-64(自动生成)
从 MAC 地址生成接口标识符(正在被隐私扩展取代):
```
MAC: aa:bb:cc:dd:ee:ff
插入 ff:fe → aa:bb:cc:ff:fe:dd:ee:ff
翻转第七位(UD bit) → ab:bb:cc:ff:fe:dd:ee:ff
结果: fe80::ab:bb:cc:ff:fe:dd:ee:ff (link-local)
```
### SLAAC( Stateless Address Autoconfiguration )
主机无需 DHCP 服务器,通过 Router Advertisement(RA)消息自行配置:
```mermaid
sequenceDiagram
participant Host as 主机
participant R as 路由器
R->>Host: Router Advertisement (每 200s~60min)
Note over Host: RA 中包含:
Note over Host: - 前缀 (如 2001:db8:abcd::/64)
Note over Host: - prefix length
Note over Host: - 默认网关
Note over Host: - DNS (RFC 6106 / RDNSS)
Host->>Host: 自构 IPv6 地址
Host->>R: NDP Neighbor Solicitation (查重)
R-->>Host: (无冲突 → 无回复)
Host->>Host: ✅ 分配 2001:db8:abcd::<interface-id>
```
### DUID-DHCPv6
DHCPv6 服务器分配地址时需要客户端标识:
- **DUID-LLT**: DUID + Link-Layer Time + Link-Layer Address
- **DUID-UUID**: DUID + UUID (持久不变)
## IPv6 首部 vs IPv4 首部
| 字段 | IPv4 | IPv6 |
|------|------|------|
| 版本 | 4 bit | 4 bit |
| 优先级 | DSCP+ECN (8 bit) | Traffic Class + Flow Label (20 bit) |
| 长度 | Total Length (含首部) | Payload Length (**不含**首部) |
| Next Header | Protocol (替代) | Next Header (等同) |
| Hop Limit | TTL | Hop Limit (改名但不改功能) |
| 源/目的 IP | ✅ | ✅ |
| 首部 Checksum | ✅ | ❌ **已删除!** |
| 分片 | ID/DF/MF/Offset | 移到扩展头(由源端分片) |
### IPv6 为什么去掉 Checksum?
- TCP/UDP/ICMPv6 自带端到端校验
- 链路层 Ethernet FCS 已做底层校验
- 每跳计算 Checksum 增加路由器负担 → 提升转发性能
### Flow Label(流标签)
新引入的 20-bit 字段,用于标识属于同一"流"的数据包序列。中间网络设备可据此做 QoS 或负载均衡——无需深度解析载荷。
## IPv6 扩展头部(Extension Headers)
IPv6 将可选功能移出固定首部,放在扩展头链中,由 Next Header 字段串联:
```
Fixed Header → Hop-by-Hop Options (可选) → Destination Options → Routing → Fragment → Authentication (AH) → Encapsulating Security Payload (ESP) → Upper Layer (TCP/UDP/ICMPv6)
```
| 扩展头 | 类型值 | 用途 |
|--------|-------|------|
| Hop-by-Hop Options | 0 | 逐跳选项(极少使用) |
| Routing | 43 | 路由类型 0 (SRv6 前身)、Type 2 (Mobile IPv6) |
| Fragment | 44 | 分片信息(ID/offset/MF),仅出现在原始包中 |
| Authentication (AH) | 51 | IPsec 认证(较少独立使用) |
| Encapsulating Security Payload (ESP) | 50 | IPsec 加密封装 |
| Destination Options | 60 | 终点选项(MIO 移动 IPv6、Jumbo Payload) |
| Mobility Header | 135 | Mobile IPv6 |
> [!warning] Path MTU Discovery in IPv6
> IPv6 规定**路由器不允许对 IP 包分片**。如果包超过链路 MTU,路由器直接丢弃并返回 ICMPv6 "Packet Too Big"。这就是 PMTUD 在 IPv6 中成为强制要求的原因。
## IPv4 到 IPv6 的过渡机制
| 技术 | 原理 | 适用场景 |
|------|------|---------|
| Dual Stack | 同时运行 IPv4 和 IPv6 | 最常见,逐步迁移 |
| Tunneling (6in4/6to4) | IPv6 包封装在 IPv4 中传输 | 穿越纯 IPv4 骨干网 |
| NAT64/DNS64 | 客户端仅有 IPv6,NAT 翻译到 IPv4 | 运营商级部署 |
| SIIT | 状态less 翻译,地址嵌入 (IPv4-mapped) | ISP、企业边界 |
## Go 中的 IPv6 支持
```go
import "net"
// 创建双栈监听器(同时支持 v4/v6)
ln, err := net.Listen("tcp6", ":8080")
// 设置 IPv6Only = false 可同时接受 IPv4 连接 (DualStack)
// 解析 IPv6 地址
ip := net.ParseIP("2001:db8::1") // net.IP 天然支持 v4/v6
_, cidr, _ := net.ParseCIDR("2001:db8::/32")
fmt.Println(cidr.Contains(ip)) // true
```
## 关联笔记
- [[hhs/NETWORK/IPv4首部与分段重组]] — IPv4 和 IPv6 首部的差异对比
- [[hhs/NETWORK/DNS原理与优化]] — AAAA 记录解析 IPv6 地址
- [[hhs/NETWORK/CIDR与子网划分]] — CIDR 在 IPv6 中的应用方式相同
@@ -0,0 +1,175 @@
---
tags: [计算机网络, ARP, gratuitous-ARP, ARP欺骗]
create time: 2026-05-18 00:40
---
# ARP 协议完整流程
## 概述
ARP(Address Resolution Protocol,地址解析协议)负责将 IP 地址解析为同一局域网内的 MAC 地址。它是 IPv4 网络通信中不可或缺的"胶水协议"——每对首次通信的主机之间都必须经过一次 ARP。
## ARP 报文格式
ARP 直接封装在 Ethernet 帧中(EtherType = 0x0806),不依赖 IP:
```
┌───────────────────┬───────────────┬────────────────────┐
│ Hardware Type │ Protocol Type │ Hardware Addr Len │
│ 2 bytes │ 2 bytes │ 1 byte │
│ 1 = Ethernet │ 0x0800=IP │ always 6 │
├───────────────────┼───────────────┼────────────────────┤
│ Protocol Addr Len │ Operation │ Sender MAC │
│ 1 byte │ 2 bytes │ 6 bytes │
│ always 4 │ 1=Request │ │
│ │ 2=Reply │ │
├───────────────────┴───────────────┴────────────────────┤
│ Sender IP Address (4 bytes) │
├────────────────────────────────────────────────────────┤
│ Target IP Address (4 bytes) │
├────────────────────────────────────────────────────────┤
│ Target MAC Address (6 bytes) │
│ (in Request, this is zeroed/empty) │
└────────────────────────────────────────────────────────┘
Total: 28 bytes payload (padded to ≥ 46 bytes by Ethernet)
```
### Operation 字段
| 值 | 含义 |
|----|------|
| 1 | **ARP Request** — "谁是 x.x.x.x?请告诉我的 MAC" |
| 2 | **ARP Reply** — "我是 x.x.x.x,我的 MAC 是 yy" |
| 3 | RARP (Reverse ARP) — 历史遗留,已废弃 |
| 8 | InARP (Inverse ARP) — ATM 网络用 |
## ARP 请求与回复流程
```mermaid
sequenceDiagram
participant Src as PC-A<br/>192.168.1.10/aa:bb:cc:dd:ee:01
participant Bcast as 广播域内所有主机
participant Dst as PC-B<br/>192.168.1.20/?mac
Note over Src: Application wants to send packet to .20<br/>But needs MAC first!
Src->>Bcast: ARP Request (Broadcast)<br/>Who has 192.168.1.20? Tell 192.168.1.10
Note over Src,Bcast: Src MAC=aa:bb:cc:dd:ee:01<br/>Dst MAC=ff:ff:ff:ff:ff:ff<br/>Target IP=.20, Target MAC=00:00:00:00:00:00
loop For each host on LAN
Bcast-->>Host-C: 收到但不匹配 → 忽略
end
Dst-->>Src: ARP Reply (Unicast)<br/>192.168.1.20 is at aa:bb:cc:dd:ee:20
Src->>Src: Update ARP Cache: 192.168.1.20 → aa:bb:cc:dd:ee:20
```
### ARP 缓存条目
```bash
$ ip neigh show
192.168.1.20 dev eth0 lladdr aa:bb:cc:dd:ee:20 STALE
192.168.1.1 dev eth0 lladdr aa:bb:cc:dd:ee:01 REACHABLE
fe80::1 dev eth0 lladdr aa:bb:cc:dd:ee:01 DELAY
# 状态机:
# INCOMPLETE — 请求已发但未收到回复
# REACHABLE — 确认可达(刚收到回复或收到 ACK)
# STALE — 过期但可用,等待下次验证
# DELAY — 已标记为 STALE,等待 probe
# FAILED — Probe 全部失败
```
### ARP 缓存超时时间
```bash
# Linux 默认超时(秒)
$ sysctl net.ipv4.neigh.default.base_reachable_time_ms
net.ipv4.neigh.default.base_reachable_time_ms = 30000 # 30s
# 可调为毫秒精度
$ echo 60000 > /proc/sys/net/ipv4/neigh/default/base_reachable_time_ms # 改为 60s
```
## Gratuitous ARP(免费 ARP / 主动 ARP)
### 什么是 Gratuitous ARP?
一台主机**主动发送 ARP 回复**,即使没有收到任何请求。这相当于在广播域中宣布:"我现在是 xx:xx:xx:xx:xx:xx,绑定的是 x.x.x.x"。
### 三种使用场景
| 场景 | 目的 |
|------|------|
| 网卡故障切换 (VRRP/Keepalived) | 通知交换机更新 MAC→端口映射 |
| IP 冲突检测 | 先发送 GARP,如果有人回应则发现冲突 |
| 虚拟机迁移 | 新宿主机的 MAC 替代旧主机的 IP |
| DHCP 续租 | 重新声明自己的地址绑定关系 |
```mermaid
sequenceDiagram
participant V1 as VM-on-Host-A<br/>IP: 10.0.0.10<br/>MAC: aa:bb:cc:00:00:01
participant SW as Switch
participant Migrate as VM migrates to Host-B
Note over V1: Live Migration starts...
V1->>SW: 最后一帧 from Host-A<br/>Switch learns: .10 → Port-X
Note over Migrate: VM boots on Host-B<br/>Same IP, New MAC!
Migrate->>SW: GARP "10.0.0.10 is now cc:dd:ee:00:00:01!"
SW->>SW: UPDATE MAC table:<br/>.10 → Port-Y ✅
Note over V1,Migrate: Without GARP, all traffic would go to old port 😱
```
## ARP 安全与攻击
### ARP Spoofing / Poisoning
攻击者发送伪造的 ARP Reply,让受害者认为网关的 MAC 已被篡改:
```
Before:
PC: "Gateway is at aa:bb:cc:gg:hh:ii ✅"
Server: "I am at dd:ee:ff:jj:kk:ll ✅"
After Attack (Attacker injects fake ARP):
PC: "Gateway is at mm:nn:oo:pp:qq:rr ❌ ← Attacker's MAC!"
```
### 防范措施
| 措施 | 说明 | 部署位置 |
|------|------|---------|
| **静态 ARP** | `arp -s` 手动绑定 | 小型网络,管理成本高 |
| **Dynamic ARP Inspection (DAI)** | Switch 拦截非合法 DHCP binding 的 ARP | 交换机配置 |
| **ARP Watch / Arpmonitor** | 监控 ARP 变动并告警 | 服务端 |
| **ndp-scan / arp-scan** | 定期扫描 ARP 表变化 | 运维工具 |
```bash
# 手动添加静态 ARP 条目(重启失效)
$ sudo ip neigh add 192.168.1.1 lladdr aa:bb:cc:dd:ee:01 nud permanent dev eth0
# 永久生效需写入 NetworkManager 或 systemd-networkd 配置
```
## IPv6 中的替代:NDP(Neighbor Discovery Protocol)
IPv6 废除了 ARP,改用 NDP(ICMPv6 类型 133/134/135/136):
| ARP 功能 | NDP 实现 | ICMPv6 Type |
|----------|---------|-------------|
| ARP Request | Neighbor Solicitation (NS) | 135 |
| ARP Reply | Neighbor Advertisement (NA) | 136 |
| gratuitous ARP | Unsolicited NA | 136 |
| 路由发现 | Router Solicitation / Advertisement | 133 / 134 |
> [!tip] 为什么 NDP 更安全?
> NDP 支持 SEND(Secure Neighbor Discovery,RFC 3971),利用加密签名防止伪装。虽然部署不多,但架构上比明文 ARP 好得多。
## 关联笔记
- [[hhs/NETWORK/MAC地址与广播域]] — ARP 如何在广播域中工作
- [[hhs/NETWORK/Switch与路由器]] — Switch 如何处理 ARP 包
- [[hhs/NETWORK/OSI与TCP-IP模型对比]] — ARP 跨越链路层和网络层的边界
@@ -0,0 +1,189 @@
---
tags: [计算机网络, ICMP, Ping, Traceroute]
create time: 2026-05-18 00:50
---
# ICMP 与 Ping / Traceroute
## 概述
ICMP(Internet Control Message Protocol,互联网控制消息协议)是 IP 的"副手"——它不承载用户数据,而是用来报告错误、传递诊断信息。ping 和 traceroute 这两个最常用的网络工具,底层全靠 ICMP。
## ICMP 报文格式
```
┌──────────┬───────────┬──────────┬───────────────┐
│ Type(8b) │ Code(8b) │ Checksum │ Rest of Header │
├──────────┼───────────┴──────────┴───────────────┤
│ Data (variable) │
└───────────────────────────────────────────┘
```
### 常用 Type 对照表
| Type | Code | Name | 用途 |
|------|------|------|------|
| 0 | 0 | Echo Reply | ping 回复 |
| 3 | 0–3 | Destination Unreachable | 目的不可达 |
| 3/0 | Network Unreachable | 路由表中无目标网段 |
| 3/1 | Host Unreachable | 同一网段但主机不存在 |
| 3/2 | Protocol Unreachable | TCP/UDP 端口无监听 |
| 3/3 | Port Unreachable | UDP 端口不可达(最常见) |
| 8 | 0 | Echo Request | ping 请求 |
| 11 | 0 | Time Exceeded | TTL 归零(traceroute 利用此) |
| 12 | 0 | Parameter Problem | IP 首部字段错误 |
| 13 | 0 | Timestamp Request | 时间戳查询 |
| 14 | 0 | Timestamp Reply | — |
| 17 | 0 | Address Mask Request | 子网掩码查询(老旧) |
| 18 | 0 | Address Mask Reply | — |
## Ping — 最基础的连通性测试
### Ping 工作原理
```mermaid
sequenceDiagram
participant Client as 客户端
participant Server as 服务器
Client->>Server: ICMP Echo Request<br/>Type=8, ID=0x1234, Seq=0
Note over Server: 内核自动回复,无需应用层处理
Server-->>Client: ICMP Echo Reply<br/>Type=0, ID=0x1234, Seq=0
loop 继续发第 2, 3... 个包
Client->>Server: Echo Request Seq=1
Server-->>Client: Echo Reply Seq=1
end
```
### Linux ping 详解
```bash
$ ping -c 4 -s 1024 example.com
PING example.com (93.184.216.34): 1024 data bytes
1032 bytes from 93.184.216.34: icmp_seq=0 ttl=55 time=23.4 ms
1032 bytes from 93.184.216.34: icmp_seq=1 ttl=55 time=22.8 ms
1032 bytes from 93.184.216.34: icmp_seq=2 ttl=55 time=23.1 ms
1032 bytes from 93.184.216.34: icmp_seq=3 ttl=55 time=23.5 ms
--- example.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss
rtt min/avg/max/stddev = 22.8/23.2/23.5/0.30 ms
```
参数速查:
| 参数 | 含义 |
|------|------|
| `-c N` | 发送 N 个包后停止 |
| `-i SEC` | 间隔秒数(默认 1s,root 可用 < 0.1s) |
| `-s SIZE` | 载荷大小(默认 56 bytes,即 64 byte ICMP 包) |
| `-t TTL` | 指定初始 TTL |
| `-W MSEC` | 等待超时毫秒数 |
| `-q` | 静默模式,仅显示统计摘要 |
### macOS / Windows 的差异
| 特性 | Linux | macOS | Windows |
|------|-------|-------|---------|
| 默认行为 | 不停发送直到 Ctrl+C | 不停发送 | 发 4 次后停止 |
| 默认 TTL | 64 | 64 | 128 |
| 默认载荷 | 56 bytes | 56 bytes | 32 bytes |
| `-c` | ✅ 限制次数 | ❌ | ❌ |
> [!tip] 判断丢包率的阈值
>
> | 丢包率 | 评价 |
> |--------|------|
> | 0% | 正常 |
> | < 0.1% | 可接受(偶尔重传) |
> | 0.1%~1% | 注意观察 |
> | 1%~5% | 可能存在瓶颈或线路老化 |
> | > 5% | 严重问题,需排查物理链路或设备 |
## Traceroute — 路由路径探测
### 原理:利用 ICMP Time Exceeded
```mermaid
sequenceDiagram
participant T as 客户端
participant R1 as Router 1
participant R2 as Router 2
participant D as Destination
T->>R1: ICMP(TTL=1) → R1 TTL归零,返回 Time Exceeded
Note over T: 收到 R1 的 reply → 第一跳延迟记录完毕
T->>R1: ICMP(TTL=2) → R1转发 → R2 TTL归零→ Time Exceeded
Note over T: 收到 R2 的 reply → 第二跳记录完毕
T->>R1: ICMP(TTL=3) → ... → D 收到 → 返回 Echo Reply
Note over T: ✅ 到达目的地
```
### Linux traceroute vs tracepath
```bash
# traceroute 使用 UDP(默认高位端口)或 ICMP(-I 参数)
$ traceroute -I example.com # 使用 ICMP
1 192.168.1.1 1ms 1ms 1ms
2 10.0.0.1 10ms 10ms 10ms
3 202.97.xx.xx 20ms 19ms 20ms
...
12 93.184.216.34 45ms 44ms 44ms
# tracepath 不需要 root 权限,默认 MTU 发现
$ tracepath example.com
1: 192.168.1.1 0.4ms
2: 10.0.0.1 8.2ms
...
12: 93.184.216.34 43.5ms
No route to above host (mtu discovered: 1492)
```
### Windows 等价命令
```cmd
tracert example.com
```
> [!warning] 为什么有些 traceroute "星号 * * *"?
> 路由器可以配置为**不回复 ICMP Time Exceeded**(出于安全考虑)。此时你看不到中间节点,只能看到最后的目的地。
## ICMP Rate Limiting(限速)
Linux 内核默认对 ICMP 进行限速,防止 ICMP flood 攻击:
```bash
$ sysctl net.ipv4.icmp_ratelimit # 每秒最多发出多少个 ICMP
net.ipv4.icmp_ratelimit = 1000 # 默认 1000 msg/s
$ sysctl net.ipv4.icmp_ratemask # 哪些 type 受限速
net.ipv4.icmp_ratemask = 6168 # mask bits
```
> [!note] iptables 中也可以限速
> ```bash
> iptables -A INPUT -p icmp --icmp-type echo-request -m limit \
> --limit 1/s --limit-burst 4 -j ACCEPT
> ```
## IPv6 等价:ICMPv6
IPv6 中 ICMP 被增强为 NDP(Neighbor Discovery Protocol),功能更丰富:
| IPv4 ICMP | IPv6 ICMPv6 对应 |
|-----------|-----------------|
| Echo Request/Reply | 类型 8/0(不变) |
| Destination Unreachable | 类型 1(相同语义) |
| Time Exceeded | 类型 3(traceroute 用) |
| Router Discovery | Router Solicitation / Advertisement(类型 133/134)|
| ARP | Neighbor Solicitation / Advertisement(类型 135/136)|
| PMTUD | Path MTU Discovery 成为强制要求 |
## 关联笔记
- [[hhs/NETWORK/IPv4首部与分段重组]] — ICMP 作为 IP 上层协议(Protocol=1)
- [[hhs/NETWORK/CIDR与子网划分]] — 理解子网后才能正确使用 ping
- [[hhs/NETWORK/NAT原理与应用]] — NAT 会影响 ICMP 的回程路径
@@ -0,0 +1,166 @@
---
tags: [计算机网络, 静态路由, 默认网关, 策略路由]
create time: 2026-05-18 01:00
---
# 静态路由与默认网关
## 概述
路由是数据包从源到目的地所经历的路径选择过程。Linux 内核维护着一张**路由表**,每收到一个包就查表决定下一跳。最基础也是最常用的就是静态路由和默认网关。
## Linux 路由表结构
```bash
$ ip route show
default via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.100 metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100 metric 100
10.0.0.0/8 via 10.0.0.1 dev eth1 proto static metric 200
172.16.0.0/12 dev eth2 scope link
```
### 关键字段含义
| 字段 | 说明 |
|------|------|
| `default` | 匹配所有目标(等价于 `0.0.0.0/0`),即**默认网关** |
| `via` | 下一跳的 IP 地址 |
| `dev` | 出口网卡 |
| `proto` | 路由来源:kernel/auto/static/dhcp |
| `scope` | 范围:link(直连)/ host(单主机)/ global(全局可路由)|
| `metric` | 优先级,值越小越优先 |
## 最长前缀匹配原则
当多条路由能匹配目标 IP 时,选择**前缀最长**的那条:
```
目标 IP: 10.0.0.5
路由表:
default via 192.168.1.1 → /0 (0 bits)
10.0.0.0/8 via 10.0.0.1 → /8 (8 bits) ✅ 选中!
10.0.0.0/24 via 10.0.0.2 → /24 (24 bits) ← 实际上这条更长,应该选它!
10.0.0.5/32 via 10.0.0.3 → /32 (32 bits) ← 最佳匹配!
结论: 最长前缀优先 → 选 /32 那条
```
```mermaid
flowchart TD
S["目标 IP: 10.0.0.5"] --> C{"/32 match?"}
C -->|"是"| R3["10.0.0.5/32<br/>via 10.0.0.3 ✅"]
C -->|"否"--> C2{"/24 match?"}
C2 -->|"是"| R2["10.0.0.0/24<br/>via 10.0.0.2 ✅"]
C2 -->|"否"| C8{"/8 match?"}
C8 -->|"是"| R1["10.0.0.0/8<br/>via 10.0.0.1 ✅"]
C8 -->|"否"| D["default route<br/>via 192.168.1.1 ✅"]
style R3 fill:#98FB98,color:#000
style R2 fill:#DDA0DD
style R1 fill:#FFD700
style D fill:#B0C4DE
```
## 默认网关
默认网关是所有未知目的地的兜底路径:
```bash
# 查看当前默认网关
$ ip route show default
default via 192.168.1.1 dev eth0 proto dhcp
# 添加默认网关
$ ip route add default via 192.168.1.1 dev eth0 metric 100
# 删除默认网关
$ ip route del default via 192.168.1.1
# 修改默认网关(先删后加)
$ ip route replace default via 192.168.1.254 dev eth0 metric 50
```
### 多默认网关(负载均衡与故障转移)
```bash
# ECMP(Equal-Cost Multi-Path)— 同开销的多条路径负载均衡
$ ip route add default via 10.0.0.1 dev eth0
$ ip route add default via 10.0.1.1 dev eth0
# 流量会自动在两条链路间负载均衡(基于哈希)
$ ip -d route show
default nexthop via 10.0.0.1 dev eth0 weight 1
nexthop via 10.0.1.1 dev eth0 weight 1
```
## 静态路由配置
### 基本语法
```bash
# 格式: ip route add <网络>/<掩码> via <下一跳IP> dev <网卡> metric <优先级>
$ sudo ip route add 172.16.0.0/12 via 192.168.1.1 dev eth0 metric 100
$ sudo ip route add 10.100.0.0/16 via 10.0.0.1 dev eth1 onlink
# onlink: 强制使用,即使下一跳不在同一子网
```
### 不同发行版的持久化方式
| 系统 | 配置文件 | 示例 |
|------|---------|------|
| Debian/Ubuntu | `/etc/network/interfaces` | `up ip route add ...` |
| RHEL/CentOS 7+ | `/etc/sysconfig/network-scripts/route-eth0` | `10.0.0.0/8 via 192.168.1.1` |
| Systemd-networkd | `.network` 文件 | `Route:` section |
| NetworkManager | `nmcli connection modify` | `ipv4.routes "..."` |
| Alpine | `/etc/network/interfaces` | `post-up ip route add ...` |
### Netplan (Ubuntu 18.04+)
```yaml
# /etc/netplan/01-netcfg.yaml
network:
version: 2
ethernets:
eth0:
dhcp4: true
routes:
- to: 10.100.0.0/16
via: 192.168.1.1
```
## 策略路由(Policy-Based Routing)
Linux 支持基于源 IP、端口、协议等条件选择不同路由表:
```bash
# 创建自定义路由表
echo "100 web-table" >> /etc/iproute2/rt_tables
echo "200 db-table" >> /etc/iproute2/rt_tables
# 为 web-table 添加路由
ip route add 10.0.0.0/8 via 10.0.0.1 dev eth1 table web-table
# 策略:来自 192.168.10.0/24 的流量走 web-table
ip rule add from 192.168.10.0/24 table web-table
# 效果:即使是同一个目的地,不同源 IP 走不同路径
```
```mermaid
flowchart LR
SrcA["192.168.10.5"] -->|"Source IP match"| Rule["ip rule policy"]
SrcB["192.168.20.5"] -->|"Source IP match"| Rule
Rule -->|"web-table → eth1"| WebRouter["Web Router<br/>10.0.0.1"]
Rule -->|"db-table → eth2"| DBRouter["DB Router<br/>10.1.0.1"]
style WebRouter fill:#DDA0DD,color:#000
style DBRouter fill:#98FB98,color:#000
```
## 关联笔记
- [[hhs/NETWORK/CIDR与子网划分]] — CIDR 前缀长度决定路由表的精确度
- [[hhs/NETWORK/NAT原理与应用]] — NAT 常作为默认网关的角色存在
- [[hhs/NETWORK/动态路由协议]] — OSPF/BGP 自动生成路由项,无需手动配置
@@ -0,0 +1,200 @@
---
tags: [计算机网络, OSPF, BGP, 动态路由协议]
create time: 2026-05-18 01:10
---
# 动态路由协议:OSPF 与 BGP
## 概述
在大型网络中手动维护静态路由不现实——网络拓扑变化时每条路由都要手动更新。动态路由协议让路由器自动"学习"彼此的网络,发现链路故障后自动切换路径。核心分类:
| 类别 | IGP (内部网关协议) | EGP (外部网关协议) |
|------|-------------------|-------------------|
| 代表协议 | OSPF, IS-IS, RIP | **BGP** |
| 使用场景 | 同一个 AS(自治系统)内 | **不同 AS 之间**(即互联网 backbone)|
| 算法类型 | 链路状态 (LSA), 距离向量 | 路径向量 |
## OSPF(Open Shortest Path First)
### 基本概念
OSPF 是一种**链路状态**协议,每台路由器收集全网拓扑信息,独立计算最短路径树(SPF)。
```mermaid
flowchart TD
subgraph "AS 内部 — OSPF Area 0"
R1["Router A"] --- R2["Router B"]
R1 --- R3["Router C"]
R2 --- R3
R1 --- SW1["Network 192.168.1.0/24"]
R2 --- SW2["Network 10.0.0.0/8"]
R3 --- R4["Router D"]
R4 --- SW3["Network 172.16.0.0/12"]
end
style R1 fill:#DDA0DD,color:#000
style R3 fill:#FFD700,color:#000
```
### OSPF 工作流程
```mermaid
sequenceDiagram
participant R1 as Router A
participant R2 as Router B
participant R3 as Router C
Note over R1,R3: Step 1: Neighbor Discovery
R1->>R2: Hello Packet (multicast 224.0.0.5)
R2-->>R1: Hello + DB Description
R1->>R2: Adjacency Established ✅
Note over R1,R3: Step 2: LSA Exchange
R1->>R2: LSUpdate (我的直连网络: 192.168.1.0/24, cost=10)
R1->>R3: LSUpdate (我的直连网络: 192.168.1.0/24, cost=10)
Note over R1,R3: Step 3: SPF Calculation
R2->>R2: Dijkstra → 最优路径已构建
R3->>R3: Dijkstra → 最优路径已构建
Note over R1,R3: Step 4: Routing Table Updated
```
### OSPF 区域(Area)概念
OSPF 用"区域"来水平扩展:
| 区域类型 | 特点 |
|----------|------|
| **Backbone Area 0** | 所有其他 Area 必须连接到 Area 0 |
| Standard Area | 传播全部 LSA,路由表最大 |
| Stub Area | 不接受 Type 5 LSA(外部路由),注入一条 default route |
| Totally Stubby | Stub + 不接收 Type 3(区域间路由),只有一条默认路由 |
| NSSA (Not-So-Stubby Area) | Stub 的变种,允许引入外部路由(Type 7)|
```mermaid
flowchart LR
R1["ABR: Router A<br/>Area 0 ↔ Area 1"] -->|"全部LSA"| A0["Area 0 (Backbone)"]
A0 -->|"全部LSA"| R1
R1 -->|"汇总路由 → Stub"| A1["Area 1 (Totally Stubby)<br/>只有默认路由"]
style R1 fill:#FFD700,color:#000
style A0 fill:#DDA0DD,color:#000
style A1 fill:#98FB98,color:#000
```
- **ABR** (Area Border Router): 连接多个区域的边界路由器
- **ASBR** (AS Boundary Router): 引入外部路由的路由器
### Cost 计算
```
Cost = Reference_Bandwidth / Interface_Bandwidth
参考带宽默认值: 100 Mbps (10^8)
接口实际 Cost:
- 10 Mbps Ethernet: 100 / 10 = 10
- 100 Mbps Ethernet: 100 / 100 = 1
- 1 Gbps Ethernet: 100 / 1000 = 0 → 调为 1
- 10 Gbps Ethernet: 100 / 10000 = 0 → 需调整参考带宽
```
> [!warning] 参考带宽问题
> 如果参考带宽是 100Mbps,那么 Gigabit (1G) 和 TenGig (10G) 的 cost 都是 1,无法区分优劣。正确做法:
> ```bash
> router ospf 1
> distance ospf 10
> auto-cost reference-bandwidth 10000 # 单位 Mbps → 支持到 100G
> ```
### 查看 OSPF 状态
```bash
$ ip route | grep O # 过滤 OSPF learned routes
O 10.0.0.0/8 [110/20] via 192.168.1.1, eth0
OE1 172.16.0.0/12 [110/130] via 192.168.1.1, eth0
$ ip -d ospf show # FRR/quagga/Zebra
$ netstat -s | grep ospf # OSPF 统计信息
# Quagga/FRR 中的详细查看
$ vtysh
show ip ospf neighbor # 邻居关系
show ip ospf database # LSA 数据库
show ip ospf route # 计算出的 OSPF 路由
show ip ospf interface eth0 # 接口 OSPF 状态
```
## BGP(Border Gateway Protocol)
### BGP vs OSPF 对比
| 特性 | OSPF | BGP |
|------|------|-----|
| 范围 | AS 内部 (IGP) | **AS 间 (EGP)** |
| 算法 | 链路状态 (Dijkstra) | **路径向量** |
| 传输 | IP 协议号 89 | **TCP 179** |
| 收敛速度 | 秒级 | 分钟级(设计如此,稳定优先)|
| 可扩展性 | ~几千台路由器 | **全球互联网 10 万+ 前缀** |
| 策略控制 | 基于 cost | **丰富的 attribute 策略** |
### BGP 两种会话类型
```mermaid
flowchart LR
iBGP["iBGP<br/>同一 AS 内的路由器"] -->|"传递可达信息<br/>不改变 next-hop"| R1["AS 65001<br/>Peer A -- Peer B"]
eBGP["eBGP<br/>不同 AS 间"] -->|"交换路由并修改 next-hop"| R2["AS 65001 -- AS 65002<br/>Peer A -- Peer B"]
style iBGP fill:#DDA0DD,color:#000
style eBGP fill:#98FB98,color:#000
```
### BGP 属性(Attributes)选路顺序
当多条路径可选时,按以下优先级逐条比较:
```mermaid
flowchart TD
Start["新路由到达"] --> Weight["① Cisco专有 Weight<br/>本地有效"]
Weight --> LocPrf["② Local Preference<br/>AS 内传递"]
LocPrf --> Originate["③ 本地起源 > 接收的路由"]
Originate --> AS_Path["④ AS Path 最短"]
AS_Path --> ORIGIN["⑤ Origin: IGD < EGP < INCOMPLETE"]
ORIGIN --> MED["⑥ MED: 越低越好"]
MED --> ebgp["⑦ eBGP > iBGP"]
ebgp --> IGP_Cost["⑧ IGP cost to next-hop"]
IGP_Cost --> Cluster["⑨ Cluster List 最短"]
Cluster --> RouterID["⑩ Router ID 最小"]
style Start fill:#FFD700,color:#000
style LocPrf fill:#DDA0DD,color:#000
style AS_Path fill:#98FB98,color:#000
```
### ASN(自治系统编号)
| 范围 | 说明 |
|------|------|
| 0–64511 | 私有 ASN(类似 RFC1918 IP 地址)|
| 64512–65534 | 16-bit 扩展私有 ASN |
| 65535 | 保留 |
| 65536–4294967295 | 32-bit 全局 ASN(互联网注册)|
IANA 分配给各大 ISPs 和大型企业(如 Google: AS15169, Cloudflare: AS13335, Alibaba: AS37963)。
## RIP(Routing Information Protocol)— 了解即可
RIP 是最简单的距离向量协议,已基本被淘汰:
- 跳数上限 15(第 16 跳视为不可达)
- 每 30 秒广播整个路由表
- `routing protocol name is rip` → 太慢了,现代网络不用
## 关联笔记
- [[hhs/NETWORK/静态路由与默认网关]] — 静态路由是动态协议的替代方案
- [[hhs/NETWORK/NAT原理与应用]] — NAT 后方的路由器不需要运行 OSPF/BGP
- [[hhs/NETWORK/负载均衡]] — Load Balancer 通常依赖 L4/L7 而非完整路由协议
@@ -0,0 +1,210 @@
---
tags: [计算机网络, NAT, SNAT, DNAT, PAT, STUN, TURN]
create time: 2026-05-18 01:20
---
# NAT 原理与应用
## 概述
NAT(Network Address Translation,网络地址转换)是 IPv4 时代最重要的"续命"技术之一——它让大量私有 IP 主机通过少数几个公网 IP 访问互联网。理解 NAT 的工作原理对于排查连接问题、设计分布式系统和部署 VPN 至关重要。
> [!QUESTION] NAT 为什么能工作?
> NAT 的本质是一个中间人:它在修改经过的数据包的源或目的地址/端口。因为 TCP/IP 协议的端到端原则要求只有通信两端才应看到完整地址,而 NAT 在中间偷偷改了——这破坏了理论模型的"纯洁性",但解决了现实中的地址短缺问题。
## NAT 分类速览
| 类型 | 全称 | 方向 | 修改字段 | 用途 |
|------|------|------|---------|------|
| **SNAT** | Source NAT | 出向 | 源 IP | 内网 → 外网 |
| **DNAT** | Destination NAT | 入向 | 目的 IP+Port | 外网 → 内网服务器 |
| **PAT** | Port Address Translation | 双向 | 同时改 IP+Port | 多主机共享单公网 IP |
| **静态 NAT** | Static NAT | 双向一对一 | 固定映射 | 内网服务器暴露到公网 |
## SNAT(源地址转换)— 内网上网的钥匙
### 工作流程
```mermaid
sequenceDiagram
participant PC as 内网主机<br/>192.168.1.100:45678
participant NAT as 路由器/NAT网关<br/>eth0: 203.0.113.5<br/>eth1: 192.168.1.1
participant Server as 外部 Web 服务器<br/>93.184.216.34:80
PC->>NAT: SYN src=192.168.1.100:45678 dst=93.184.216.34:80
Note over NAT: NAT Table Entry:<br/>192.168.1.100:45678 ↔ 203.0.113.5:50001
NAT->>Server: SYN src=203.0.113.5:50001 dst=93.184.216.34:80
Server-->>NAT: SYN-ACK dst=203.0.113.5:50001
Note over NAT: 反向查找 NAT Table<br/>203.0.113.5:50001 → 192.168.1.100:45678
NAT-->>PC: SYN-ACK
PC->>NAT: ACK src=192.168.1.100:45678
NAT->>Server: ACK src=203.0.113.5:50001
Note over PC,Server: ← 双方都不知道 NAT 的存在 →
```
### iptables 实现 SNAT
```bash
# 内网接口 eth1, 外网接口 eth0
sudo iptables -t nat -A POSTROUTING \
-s 192.168.1.0/24 -o eth0 -j MASQUERADE
# MASQUERADE vs SNAT:
# MASQUERADE: 自动获取 eth0 的当前 IP(适合动态 IP,如 DHCP/拨号)
# SNAT: 指定固定 IP (适合静态 IP,性能略高)
# 等价写法:
sudo iptables -t nat -A POSTROUTING \
-s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.5
```
```bash
# 查看 NAT 表条目
$ sudo conntrack -L | head -10
tcp 6 119 SYN_SENT ... src=192.168.1.100 dst=93.184.216.34
src=203.0.113.5 dst=93.184.216.34
```
## DNAT(目的地址转换)— 反向代理
### 典型场景:将公网 IP 的 80 端口转发到内网 Web 服务器
```mermaid
sequenceDiagram
participant Client as 外部客户端<br/>x.x.x.x:12345
participant FW as 防火墙/NAT<br/>公网: 203.0.113.5:80
participant Web as 内网 Web<br/>192.168.1.10:80
Client->>FW: SYN src=x.x.x.x:12345 dst=203.0.113.5:80
Note over FW: PREROUTING 链: DNAT<br/>203.0.113.5:80 → 192.168.1.10:80
FW->>Web: SYN src=x.x.x.x:12345 dst=192.168.1.10:80
Note over Web: ← 注意: 源IP仍然是客户端真实IP (Full Cone)
Web-->>FW: SYN-ACK dst=x.x.x.x:12345
FW->>Client: SYN-ACK
Note over FW,Web: 回程路由需要配置: ip route add x.x.x.x via ...
```
### iptables 实现 DNAT
```bash
sudo iptables -t nat -A PREROUTING \
-d 203.0.113.5 -p tcp --dport 80 -j DNAT \
--to-destination 192.168.1.10:80
# 还需允许转发
sudo iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 80 -j ACCEPT
# 如果希望 Web 能看到客户端真实 IP(不启用 SNAT)
sudo sysctl net.ipv4.conf.all.accept_local=1
```
### Docker 的端口映射本质就是 DNAT + SNAT
```bash
# docker run -p 8080:80 nginx
# = DNAT: host:8080 → container_ip:80
# + SNAT: container reply 时把源 IP 改回 host IP
iptables -t nat -A PREROUTING \
-d <host_ip> -p tcp --dport 8080 -j DNAT --to-destination <container_ip>:80
```
## PAT / NAPT(端口级 NAT)— 解决地址枯竭的关键
### 核心机制
```
内网主机 NAT设备 公网IP:Port池
───────── ─────── ─────────────
192.168.1.10:45678 ──→ 203.0.113.5:50001 203.0.113.5
192.168.1.11:45678 ──→ 203.0.113.5:50002 ← 多个内网IP
192.168.1.12:45678 ──→ 203.0.113.5:50003 ← 复用同一个公网IP
192.168.1.10:45679 ──→ 203.0.113.5:50004
```
一个公网 IP 最多可提供 **65535 个端口**,理论上支持约 6.5 万并发内网用户。实际受限于内核文件描述符限制(通常 ~6.5 万)。
## NAT 对 TCP 的影响
### TIME_WAIT 放大效应
由于所有内网主机的连接都映射到同一个公网 IP,NAT 设备的 TIME_WAIT 连接数会远超单机。当 NAT 重启时,所有半关闭的连接都会丢失。
### FTP 的问题
FTP 使用**独立的控制连接和数据连接**,且在 HTTP body 中嵌入 IP 和端口信息:
```
FTP 控制通道: PORT 192,168,1,100,180,13 ← "数据连接请发到这个IP和端口"
```
NAT 后的内网 IP 对公网不可达!解决方案:
| 方案 | 说明 |
|------|------|
| **FTP ALG** | NAT 设备深度解析 FTP 控制流,改写 PORT 命令中的 IP |
| **被动模式 PASV** | 服务器提供监听端口,由客户端发起数据连接 |
| **SFTP over SSH** | 完全绕过 FTP,用加密隧道传输文件 |
> [!warning] NAT ALG 的隐患
> ALG (Application Layer Gateway) 需要深度解析应用层协议内容,容易出错且增加安全风险。现代实践中更倾向于 SFTP/SCP 替代 FTP。
## NAT 穿透技术
### NAT 类型分类
| 类型 | 行为 | 连通性 |
|------|------|--------|
| Full Cone | 任何外部地址都能发送 UDP 到你的映射端口 | ⭐⭐⭐⭐⭐ 最佳 |
| Restricted Cone | 仅已知 IP 能发 | ⭐⭐⭐⭐ |
| Port Restricted Cone | 仅已知 IP+Port 能发 | ⭐⭐⭐⭐ |
| Symmetric | 不同目的地获得不同映射端口 | ⭐⭐ 最难穿透 |
### 常用穿透方案
```mermaid
flowchart TD
P1["STUN<br/>Session Traversal Utilities for NAT"] -->|"1. 告诉对方我的公网 IP:Port"| P2["TURN<br/>Traversal Using Relays around NAT"]
P2 -->|"2. 中继服务器转发电文"| P3["ICE<br/>Interactive Connectivity Establishment"]
P3 -->|"3. 尝试直连 → 失败则 fallback 到 TURN"| Final["WebSocket/WebRTC ✅"]
style P1 fill:#DDA0DD,color:#000
style P2 fill:#FFD700,color:#000
style P3 fill:#98FB98,color:#000
```
### ICE 流程
```
Step 1: Host Candidate — 本机直接 IP(局域网内直连最快)
Step 2: SRFLX Candidate — STUN 探测得到的公网映射地址
Step 3: Relay Candidate — TURN 服务器分配的 relay 地址
排序优先级: Host > SRFLX > Relay
尝试顺序: 先试直连,超时后走中继
```
## NAT64 / DNS64
IPv6 过渡的重要机制:IPv6-only 客户端访问 IPv4-only 服务器
```
客户端 (IPv6): 2001:db8::1
DNS64: 将 A 记录 93.184.216.34 → AAAA 记录 ::ffff:93.184.216.34
NAT64 网关: 拦截 IPv6 包 → 转换为 IPv4 包 → 发送到 Internet
回包: IPv4 → IPv6 转换 → 返回客户端
```
```bash
# Linux NAT64 内核模块
modprobe nf_nat_ipv6
sysctl -w net.ipv6.conf.all.forwarding=1
ip6tables -t nat -A POSTROUTING -s fd00::/64 -j SNAT --to-source 2001:db8:64::1
```
## 关联笔记
- [[hhs/NETWORK/CIDR与子网划分]] — NAT 使用 RFC1918 私有地址空间
- [[hhs/NETWORK/TCP状态机详解]] — NAT 影响 TCP 连接生命周期
- [[hhs/NETWORK/DNS原理与优化]] — DNS64 将 A 记录转为 AAAA 记录