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,185 @@
---
tags: [计算机网络, QUIC, HTTP/3, ConnectionID, 0-RTT, QPACK, NAT迁移]
create time: 2026-05-18 05:25
---
# QUIC 协议深度解析
## 概述
QUIC (Quick UDP Internet Connections) 正在静默地替代 TCP——Google、Cloudflare、Microsoft 的流量中已有超过 40% 走的是 QUIC。理解 QUIC 的设计哲学和具体机制,是把握下一代网络协议的钥匙。
## 为什么需要 QUIC?
```mermaid
flowchart TD
Problem["TCP 的三大历史包袱"] --> P1["• 拥塞控制算法写死在内核<br/>无法按需调整"]
Problem --> P2["• IP/port 变化 → 四元组变化<br/>连接必须重建"]
Problem --> P3["• TLS in TCP → 双重握手延迟<br/>三次握手 + TLS 握手 = 最少 2 RTT"]
Solution["QUIC 的三个解答"] --> S1["✅ 用户态实现 BBRv2 等算法<br/>应用层可编程、随时升级"]
Solution --> S2["✅ Connection ID 独立于 IP/port<br/>WiFi↔5G 无缝切换"]
Solution --> S3["✅ 内置 TLS 1.3 + 0-RTT<br/>首次握手即加密, 续连零延迟"]
style Problem fill:#FFD700,color:#000
style Solution fill:#98FB98,color:#000
```
### TCP vs QUIC 核心对比
| 维度 | TCP + TLS | QUIC + TLS 1.3 |
|------|-----------|---------------|
| 传输协议 | TCP (可靠,面向字节流) | UDP (不可靠,自带可靠性层) |
| 拥塞控制 | 内核固定 (cubic/bbr) | 用户态可插拔 (BBRv2, NewReno...) |
| 连接标识 | 四元组 (src_ip, src_port, dst_ip, dst_port) | Connection ID (16 bytes, 自定义) |
| 握手延迟 | 1 RTT (TCP) + 1 RTT (TLS 1.3) = 2 RTT | 1 RTT 完成 TCP+TLS (0-RTT 可选) |
| 多路复用 | 应用层自己搞 (HTTP/2) | 原生支持多 Stream,零队头阻塞 |
| NAT 穿透 | 断开重连 | CID 不变,自动迁移 |
| 头部压缩 | HPACK (HTTP/2) / QPACK (HTTP/3) | QPACK(解决队头阻塞) |
## QUIC 的连接管理
### Connection ID —— NAT 迁移的关键
```
TCP 连接标识 = (src_ip, src_port, dst_ip, dst_port)
→ IP 变了 → 四元组变 → 连接断了 ❌
QUIC 连接标识 = Connection ID (16 bytes,由端方自定义)
→ IP/port 变了 → CID 不变 → 连接继续 ✅
典型场景: iPhone 从 WiFi 切换到 5G
旧 IP: fe80::abcd → 新 IP: fe80::efgh
TCP: 连接断开,需重连 (RTT × 2)
QUIC: CID 不变,数据包自动迁移到新 IP
```
```
Connection ID 的生命周期:
─────────────────────────
Client → Server: Initial Packet {CID: client_cid}
Server → Client: Initial Packet {CID: server_cid}
Server → Client: Handshake Packet {CID: server_cid}
Client → Server: Handshake Packet {CID: client_cid}
Client → Server: 1-RTT Packet {CID: client_cid} ← 加密载荷, 业务数据
```
> [!tip] 为什么 QUIC 用 UDP 而不是直接改 TCP?
>
> 修改 TCP 需要在操作系统内核层面做全球部署,这几乎是不可能的(每个发行版都有不同)。而把可靠性搬到用户态用 UDP 承载——虽然看起来是"倒退"——但部署极其灵活:SDK 更新即可生效。Google 的 QUIC 实现就是作为一个 Chrome 扩展库无声更新的。
### QUIC Stream 独立性 —— 消除队头阻塞
```
QUIC Stream 独立性:
┌──────────────┐
│ Stream 0 │ ← 控制通道 (HTTP/3 信令)
│ Stream 1 │ ← Request A [✓ Data ✓ ACK] ✅ 正常
│ Stream 2 │ ← Request B [✓ Data ✓ ACK] ✅ 正常
│ Stream 3 │ ← Request C [✗ Lost 🔄 仅重传此流!]
│ Stream 4 │ ← Response A [✓ Data ✓ ACK] ←不受 Stream 3 影响!
│ Stream 5 │ ← Request D [🔄 正在发送...]
└──────────────┘
对比 HTTP/2 (共用 TCP 流):
请求 C 丢包 → 所有后续请求(包括 D、响应A)都要等 C 重传完成!
这就是 HTTP/2 在弱网下不如 HTTP/3 的原因。
```
## 0-RTT 握手与重放攻击
### 0-RTT 的工作原理
```mermaid
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: 首次连接: 1-RTT handshake
C->>S: ClientHello + HelloRetryRequest + Finished
S-->>C: 1-RTT Ready
Note over C,S: Session 缓存 (Session Ticket)
C->>S: ClientHello + 0-RTT data + Finished
Note over S: 验证 Session Ticket + 重放检测
alt 允许 0-RTT
S-->>C: Early data accepted ✅
else 拒绝 0-RTT
S-->>C: 0-RTT rejected ⚠️ (但仍处理后续数据)
end
```
```
⚠️ 0-RTT 的重放攻击风险:
─────────────────────────
攻击者截获了客户端的 0-RTT 请求 (如 POST /transfer?to=hacker&amount=10000)
然后把这个请求重新发给服务器
服务器认为是新的合法请求 → 重复转账 💸
防御措施:
1. 服务端对 0-RTT 请求标记 "early data"
2. 幂等操作 (GET/HEAD/PUT 删除) 可以安全使用 0-RTT
3. 非幂等操作需在应用层加防重放 Token
4. QUIC 的 MaxEarlyData 字段限制重放窗口大小
```
### HTTP/3 的 QPACK —— 区别于 HPACK
```
HPACK (HTTP/2) 的问题:
• Header 压缩表与数据共享同一 TCP 流
• 如果 TCP 数据包丢失 → 解压失败 → 所有后续 header 阻塞
• 这就是 HTTP/2 的队头阻塞在头部压缩层面的体现
QPACK (HTTP/3) 的解法:
• 压缩表独立于数据流 (unblocked streams)
• 即使某个 QUIC stream 丢了,header 仍能正确解码
• 通过 encoder/decoder 两侧维护独立的动态表实现
```
## QUIC 连接生命周期
```
QUIC 连接的建立:
─────────────────
1. Client → Send Initial Packet (无 CID 或临时 CID)
2. Server → Send Initial Packet (含 server_cid)
3. Server → Send Handshake Packet
4. Client → Send Handshake Packet
5. Both → Send 1-RTT Packets (加密载荷)
6. Connection established ✅
关闭:
─────────────────
• QUIC 没有 RST/FIN 标志位
• 用 CLOSE_CONNECTION_FRAME 通知对方
• 双方确认后关闭
• 类似 TCP 的四次挥手但在应用层实现
```
## Go 中的 QUIC 实现
```go
import (
"github.com/quic-go/quic-go"
)
// QUIC 服务器
config := &quic.Config{
Tracer: quic.TraceWriter, // 可观测性
}
listener, err := quic.Listen(nil, nil, config)
conn, err := listener.Accept(ctx) // 接受新连接
stream, err := conn.OpenStream() // 打开一个独立的 Stream
stream.Write([]byte("hello")) // Stream 3 的数据不会影响 Stream 5
```
> [!tip] 为什么 Go 原生不支持 QUIC?
> Go 的 net/http 标准库目前仍基于 TCP。第三方实现 `quic-go` (by @malgorithms) 是最成熟的 Go QUIC 库,已在多个生产环境使用。Go 官方对加入 QUIC 支持持开放态度。
## 关联笔记
- [[hhs/NETWORK/HTTPS与TLS握手]] — TLS 1.3 是 QUIC 的安全基础
- [[hhs/NETWORK/TCP段结构与状态机]] — QUIC 要替代的正是 TCP 的这些机制
- [[hhs/NETWORK/前沿与进阶综述]] — QUIC 在更广泛前沿技术中的位置
@@ -0,0 +1,296 @@
---
tags: [计算机网络, eBPF, XDP, kprobe, Tracepoint, ServiceMesh, Istio, Envoy]
create time: 2026-05-18 05:30
---
# eBPF 与 Service Mesh
## 概述
eBPF 让 Linux 内核变得可编程而不需要编译内核模块;Service Mesh(Istio/Linkerd)让微服务间的流量治理从代码中剥离到基础设施层。这两者代表了可观测性和流量治理的两个重要方向。
## eBPF(extended Berkeley Packet Filter)
### 什么是 eBPF?
eBPF 允许在**内核态安全地运行沙箱程序**,无需修改内核源码或加载内核模块。它最初是一个 packet filter 增强版,现在已扩展到 tracing、monitoring、security 等多个领域。
```
传统方案: eBPF 方案:
───────── ─────────
编译内核模块 (.ko) ↔ 编写 eBPF C 程序
手动符号导出 ↔ kprobe/kfunc/tracepoint 直接挂钩
系统重启加载 ↔ insmod bpf 即时生效
无类型安全检查 ↔ 内核验证器严格检查 (verifier)
崩溃影响全局 ↔ 沙箱隔离,失败不崩内核
无法卸载或调试 ↔ 可卸载,bpftool 可调试
```
### eBPF 的核心架构
```mermaid
flowchart TD
subgraph "用户态 (User Space)"
Prog["eBPF 程序<br/>(C/BPF 汇编)"]
Map["bpf_map<br/>KV 存储<br/>hash/array/perf_event/..."]
Tool["bpftool<br/>BCC tools<br/>bpftrace"]
end
subgraph "内核态 (Kernel Space)"
Verifier["Verifier<br/>严格安全检查"]
JIT["JIT Compiler<br/>C → x86/arm/riscv 机器码"]
subgraph "Attachment Points"
KP["kprobe / kretprobe<br/>hook 任意内核函数入口/出口"]
TP["tracepoint<br/>内核预定义探测点"]
TC["Traffic Control<br/>qdisc classifier"]
XDP["XDP (eXpress Data Path)<br/>网卡驱动级别最快路径"]
end
Map -->|"写入"| PerfEvent["perf event → 用户态采集"]
end
Tool -->|"加载"| Prog
Prog -->|"提交"| Verifier
Verifier -->|"验证通过"| JIT
JIT -->|"注册到"| KP
JIT -->|"注册到"| TP
JIT -->|"注册到"| TC
JIT -->|"注册到"| XDP
```
### eBPF 在网络中的典型应用层级
```mermaid
flowchart LR
NIC["Network Interface Card"] -.->|XDP 微秒级处理| XDP["XDP<br/>最快路径: 网卡驱动级别"]
TC["Traffic Control<br/>tc filter/qdisc"] -.->|中等路径| Stack["Linux Network Stack"]
Stack -.->|kprobe hook| Kprobe["kprobe/kretprobe<br/>TCP/IP 栈钩子"]
XDP -->|"drop/mirror/redirect"| NIC
TC -->|"police/packet/cgroup"| Stack
Kprobe -->|"tcp_connect/tcp_rcv_skb..."| App["可观测性工具"]
style XDP fill:#FF6B6B,color:#fff
style TC fill:#FFD700,color:#000
style Kprobe fill:#98FB98,color:#000
```
| 挂载点 | 性能 | 用途 | 典型工具 |
|--------|------|------|---------|
| **XDP** | 微秒级,最快 | DDoS 过滤、L4 LB、镜像 | AF_XDP, cilium-hubble |
| **tc (Traffic Control)** | 纳秒~微秒级 | qdisc 调度、带宽限制、重定向 | tc-bpf, polycube |
| **kprobe/kretprobe** | 低开销 | 追踪任意内核函数 | bcc tcpconnect, bpftrace |
| **tracepoint** | 极低开销 | 内核预定义事件 | perf trace |
| **CGroup** | 进程级 | 网络隔离、限速 | cgroup-bpf |
| **LSM** | 安全级别 | 强制访问控制 | libbpf-security |
### 常用 eBPF 工具实战
```bash
# === BCC Tools (Python-based eBPF 工具集) ===
# 追踪所有新建 TCP 连接
sudo ./tcpconnect
PID COMM IP SADDR DADDR DPORT
1234 curl 4 192.168.1.10 93.184.216.34 443
# 查看 TCP 连接存活时间
sudo ./tcplife
PID COMM FD LADDR:LADDR → RADDR:RADDR state msec
1234 chrome 42 192.168.1.10:54321→ 93.184.216.34:443 ESTAB 12345
# 查看 TCP 状态分布
sudo ./tcpstates
Tracing TCP states... Output every 1 seconds.
TIME_WAIT 42
ESTABLISHED 128
CLOSE_WAIT 0
# === bpftrace (一行脚本) ===
# 实时打印每个 TCP 包的大小
sudo bpftrace -e 'kprobe:tcp_sendmsg { @bytes[tid] = arg0; }'
# 统计每秒 DNS 查询数
sudo bpftrace -e 'tracepoint:dns:dns_query { @count++; } interval:s:1 { print(@count); clear(@count); }'
# === Cilium Hubble (Kubernetes 可观测性) ===
# 服务间流量可视化
hubble observe --namespace=default
FLOW 08:23:45.123 DefaultPod → api-server (TCP NEW) DPT=8080
FLOW 08:23:45.125 api-server → DefaultPod (TCP FIN)
# 按命名空间聚合
hubble observe --aggregate 5s
```
### eBPF 监控网络问题的经典场景
```bash
# 场景 1: 哪个进程的 TCP 连接最多?
sudo bpftrace -e '
kprobe:sock_alloc
/@pids[comm]/++
'
# 场景 2: 慢连接诊断——每个 TCP 连接耗时多久?
sudo bpftrace -e '
kprobe:tcp_v4_connect /arg0/ {
@start[tid] = nsecs;
}
kretprobe:tcp_v4_connect /arg0 >= 0/ {
$delta = (nsecs - @start[tid]) / 1000;
@latency_us = hist($delta);
delete(@start[tid]);
}'
# 场景 3: SYN Flood 检测
sudo bpftrace -e '
kprobe:tcp_v4_do_rcvd
@syn_count["SYN_RECV"]++;
interval:1s {
print(@syn_count);
clear(@syn_count);
}'
```
## Service Mesh (Istio / Linkerd)
### Sidecar 模式原理
```mermaid
flowchart LR
Pod["Pod / VM"] -->|"app container"| App["my-service v1"]
Pod -->|"proxy container"| Proxy["envoy sidecar"]
Proxy -->|"xDS API"| CP["Istiod<br/>Pilot(路由) + Citadel(mTLS)"]
CP -->|"推送配置"| Proxy
subgraph "Sidecar 自动接管:"
MTLS["mTLS 双向认证"]
Retry["重试/熔断/超时"]
Tracing["分布式链路追踪 (Jaeger)"]
Metrics["指标收集 (Prometheus)"]
Canary["金丝雀/蓝绿发布"]
Dashboard["流量仪表盘"]
end
Proxy -.-> MTLS
Proxy -.-> Retry
Proxy -.-> Tracing
Proxy -.-> Metrics
Proxy -.-> Canary
Proxy -.-> Dashboard
style Proxy fill:#DDA0DD,color:#000
style CP fill:#FFD700,color:#000
```
### Istio Traffic Management 核心资源
```yaml
# VirtualService: 路由规则
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: my-service
spec:
hosts: ["my-service.default.svc.cluster.local"]
http:
- match:
- headers:
x-canary: {exact: "true"}
route:
- destination: {host: my-service, subset: v2} # canary 版
weight: 100
- route:
- destination: {host: my-service, subset: v1}
weight: 90 # 正式版 90%
- destination: {host: my-service, subset: v2}
weight: 10 # 金丝雀 10%
timeout: 5s # 超时控制
retries: # 重试策略
attempts: 3
perTryTimeout: 2s
retryOn: "5xx,reset,connect-failure,gateway-error"
# DestinationRule: 定义 subset (版本分组)
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: my-service
spec:
host: my-service.default.svc.cluster.local
trafficPolicy:
tls:
mode: ISTIO_MUTUAL # 自动 mTLS
connectionPool:
tcp:
maxConnections: 100 # 连接池上限
http:
h2UpgradePolicy: DEFAULT # 升级到 HTTP/2
http1MaxPendingRequests: 100
http2MaxRequests: 1000
subsets:
- name: v1 # 打标签为 v1
labels: {app-version: v1}
- name: v2 # 打标签为 v2
labels: {app-version: v2}
# Gateway: 入站流量入口
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: my-gateway
spec:
selector: {istio: ingressgateway}
servers:
- port: {number: 443, name: https, protocol: HTTPS}
tls: {mode: SIMPLE, credentialName: my-cert}
hosts: ["api.example.com"]
```
### Istio 工作原理
```
开发者视角:
──────────────
我的代码完全不知道 Service Mesh 的存在!
不需要引入任何 SDK、注解或配置。
流量全部被 envoy sidecar 拦截和转发。
运维视角:
──────────────
Istiod (control plane) 统一管理所有 envoy proxy
↓ 通过 xDS API (CDS/EDS/LDS/RDS)
推送路由规则、监听配置、集群发现给每个 sidecar
↓
sidecar 热更新配置,无需重启应用
```
```bash
# 确认 sidecar 注入成功
kubectl get pods -n default
NAME READY STATUS
my-service-abc123-xk9qp 2/2 Running ← 2/2 = app + envoy sidecar
# 检查 envoy 配置
kubectl exec -it my-service-abc123-xk9qp -c envoy -- \
envoy --admin-address-port localhost:15000 stats
# istioctl 诊断
istioctl analyze # 分析配置正确性
istioctl proxy-status # sidecar 同步状态
istioctl proxy-config routes my-service-pod # 查看某 pod 的路由表
```
## 关联笔记
- [[hhs/NETWORK/QUIC协议深度解析]] — QUIC 也是用户态实现的协议
- [[hhs/NETWORK/WireGuardVPN原理与实践]] — WireGuard 同样在内核态运行
- [[hhs/NETWORK/01-SocketAPI与backlog详解]] — Socket API 是 eBPF kprobe 最常挂钩的地方
@@ -0,0 +1,182 @@
---
tags: [计算机网络, WireGuard, VPN, ChaCha20, Curve25519, OpenVPN, IPsec]
create time: 2026-05-18 05:35
---
# WireGuard VPN 原理与实践
## 概述
WireGuard 是一个极简的 VPN 协议,代码量不到 OpenVPN 的 1/10、IPsec 的 1/25。它凭借极低的延迟和超高的吞吐量,正在成为新一代标准 VPN 方案——甚至已合入 Linux 内核(≥ 5.6)。
## 为什么比 OpenVPN / IPsec 快?
```mermaid
flowchart LR
IPsec["IPsec\n• IKEv2 密钥交换 (多轮 RTT)\n• ESP/AH 扩展头链\n• 用户态+内核态来回跳转\n• ~10-20 种 cipher suite 协商"]
WG["WireGuard\n• Curve25519 密钥交换 (单次)\n• ChaCha20-Poly1305 AEAD\n• 纯内核态实现 (netlink API)\n• 仅 4 种密码学原语"]
style IPsec fill:#DDA0DD,color:#000
style WG fill:#98FB98,color:#000
```
### 性能对比实测数据
| 指标 | WireGuard | OpenVPN (UDP) | IPsec |
|------|-----------|--------------|-------|
| **吞吐量** | ~9.5 Gbps | ~1.5 Gbps | ~3.0 Gbps |
| **CPU 占用 (Gbps)** | 0.8% | 12.5% | 3.2% |
| **握手延迟** | < 1ms | 50-200ms | 100-300ms |
| **代码行数** | ~4,000 LoC | ~50,000 LoC | ~100,000 LoC |
| **配置复杂度** | 极简 (INI 格式) | 中等 | 复杂 |
| **NAT 遍历** | 原生支持 | 需要辅助 (UDP encapsulation) | 复杂 |
| **移动性** | 支持 (PersistentKeepalive + endpoint change) | 部分支持 | 有限支持 |
> [!tip] 代码行数少 = 更少的 bug = 更高的安全性
>
> 审计 ~4000 行 C 代码的成本远低于审计 ~50000 行。而且每少一行代码就少一个潜在的漏洞入口。
## WireGuard 的密码学基础
```
WireGuard 仅使用 4 种经过严格审查的密码学原语:
───────────────────────────────────────────────
1. Curve25519 — ECDH 密钥交换 (x25519)
2. ChaCha20 — 对称加密 (AES 替代)
3. Poly1305 — MAC / AEAD 认证
4. SHA-512 — 哈希函数
没有: RSA, ECC(除Curve25519外), CBC, HMAC, DRBG...
→ 选择极少 → 无协商 → 更快更简单
```
### 三重加密手风琴 (Triple Handshake)
```mermaid
sequenceDiagram
participant A as Peer A
participant B as Peer B
Note over A,B: 1. A → B: 主动握手 (A 发起)
A->>B: {Ephemeral Key A} 用 B's Static Key 加密
B->>A: {Ephemeral Key B} 用 A's Ephemeral Key + B's Static Key 加密
A->>B: {Empty} 用 A's Ephemeral Key + B's Ephemeral Key 加密
Note over A,B: ✅ 三方加密完成, 会话密钥建立
Note over A,B: 2. B → A: 响应式重握手 (B 轮换密钥)
B->>A: {Ephemeral Key B'} 用 A's Static Key 加密
A->>B: {Ephemeral Key A'} 用 B's Ephemeral Key + A's Static Key 加密
B->>A: {Empty} 用 B's Ephemeral Key + A's Ephemeral Key 加密
Note over A,B: ✅ 新会话密钥, 前向保密保证
```
> [!question] 什么是前向保密 (Forward Secrecy)?
> 即使长期密钥(Static Key)未来被泄露,也无法解密过去的通信——因为每次会话都使用了临时的 Ephemeral Key。三重手风琴保证了每一轮的临时密钥都在下一轮中被"销毁"。
## WireGuard 配置详解
### server.conf
```ini
[Interface]
Address = 10.0.0.1/24 # WireGuard 虚拟网卡 IP
ListenPort = 51820 # UDP 监听端口
PrivateKey = <server_private_key> # 私钥 (安全保存!)
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE # NAT 转发
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer] # 客户端 1
PublicKey = <client1_public_key>
AllowedIPs = 10.0.0.2/32 # 只允许这个 IP 通过这个 peer
PersistentKeepalive = 25 # 每 25s 发一次 keepalive (穿透 NAT)
[Peer] # 客户端 2
PublicKey = <client2_public_key>
AllowedIPs = 10.0.0.3/32
PersistentKeepalive = 25
```
### client.conf
```ini
[Interface]
Address = 10.0.0.2/24 # 客户端自己的虚拟 IP
PrivateKey = <client_private_key> # 客户端私钥
# DNS 解析通过隧道走 VPN
DNS = 10.0.0.1
[Peer]
PublicKey = <server_public_key> # ⚠️ 注意方向: 客户端存服务端的公钥
Endpoint = vpn.example.com:51820 # 服务器地址
AllowedIPs = 0.0.0.0/0 # 所有流量走 VPN (全隧模式)
# AllowedIPs = 10.0.0.0/24 # 仅内网流量走 VPN (-split tunneling)
PersistentKeepalive = 25
```
### 一键生成密钥对
```bash
# 生成服务器密钥对
$ wg genkey | tee server-private.key | wg pubkey > server-public.key
# 生成客户端密钥对 (可重复 N 次)
$ wg genkey | tee client1-private.key | wg pubkey > client1-public.key
$ wg genkey | tee client2-private.key | wg pubkey > client2-public.key
# 生成 QR Code (方便手机扫码连接)
$ qrencode -t UTF8 <(cat <(echo "[Interface]"); echo "PrivateKey=$(cat client1-private.key)"; echo "[Peer]"; echo "PublicKey=$(cat server-public.key)"; echo "AllowedIPs=0.0.0.0/0"; echo "Endpoint=vpn.example.com:51820"; echo "PersistentKeepalive=25")
```
### 日常运维命令
```bash
# 启动 WireGuard
$ sudo wg-quick up wg0
$ sudo systemctl enable --now wg-quick@wg0 # 开机自启
# 查看状态
$ wg show
interface: wg0
public key: abc123def456...
private key: (hidden)
listening port: 51820
peer: xyz789ghi012...
endpoint: 203.0.113.5:45678
allowed ips: 10.0.0.2/32
latest handshake: 3 seconds ago ← 3 秒前还在握手! 连接活跃
transfer: 1.23 GiB received, 890 MiB sent
# 动态添加/移除 peer (不需要重启!)
$ wg set wg0 peer <new_pubkey> allowed-ips 10.0.0.4/32
$ wg remove wg0 peer <old_pubkey>
# 导入导出配置
$ wg syncconf wg0 <(wg-quick strip wg0)
```
## WireGuard 的限制与注意事项
```
⚠️ WireGuard 不适合的场景:
─────────────────────────────────
• 需要传统用户认证 (PAP/CHAP/LDAP) → WireGuard 只认密钥
• 细粒度 per-user 带宽控制 → 需配合 tc (traffic control)
• 大规模动态节点 (千级以上) → 管理 1000 个 peer 的 AllowedIPs 很痛苦
• 与现有 IPsec 设备互通 → WireGuard 不能做 IPsec gateway
✅ WireGuard 非常适合:
─────────────────────────────────
• 个人/小团队私有 VPN
• 云服务器到 IDC 的专线
• IoT 设备远程管理
• Kubernetes 集群跨 AZ 通信
```
## 关联笔记
- [[hhs/NETWORK/eBPF与ServiceMesh]] — eBPF 可用于监控 WireGuard 接口的流量
- [[hhs/NETWORK/SDNSRv6与网络编程最佳实践]] — SDN + SRv6 是更宏大的网络可编程愿景
- [[hhs/NETWORK/IPv6地址与扩展头部]] — IPv6 SLAAC 自动配置与 WireGuard 的静态分配对比
@@ -0,0 +1,267 @@
---
tags: [计算机网络, SRv6, SDN, OpenFlow, P4, Go网络编程]
create time: 2026-05-18 05:40
---
# SDN、SRv6 与 Go 网络编程最佳实践
## 概述
当 eBPF 让内核可编程之后,SDN 让整个网络架构变得可编程。SRv6 将路由策略编码进 IPv6 地址本身,而 Source Routing 让源端可以精确控制数据包经过的每一跳。本章从原理走向工程实践。
## SRv6(Segment Routing over IPv6)
### 核心理念
SRv6 是 MPLS 的 IPv6 等价物,但有一个关键区别:**路径信息直接编码在 IPv6 地址中**。
```
传统 MPLS: SRv6:
标签栈 (stacked labels) IPv6 Ext Header: SRH (Segment Routing Header)
每个中间节点查转发表 每个 Segment = 一个操作
中间节点依赖 LSP 源端指定整条路径 (source routing)
部署需要全局配置 LDP/RSVP-TE Controller 集中下发
```
### SRv6 数据包结构
```
IPv6 Header:
Src: controller-assigned address
Dst: SRH (Segment Routing Header)
Next Header: 59 (NoNextHeader, 表示后面没有额外 header)
Hdr Ext Len: N-1
Routing Type: 4
Segments Left: N
Last Entry: N-1
Flags: 0
Tag: 0
Segments[N]: endpoint6::action1 ← 第 N 段
Segments[N-1]: endpoint6::action2 ← 第 N-1 段
...
Segments[1]: next-hop-ip ← 最后一段就是实际目的 IP
Payload: actual data (TCP/UDP/ICMP)
```
```
SRv6 典型路径:
──────────────────
Client ──→ PE1 ──→ PE2 ──→ PE3 ──→ Provider Edge ──→ Destination
↑ ↑
SRH[2]=PE2 SRH[1]=PE3
数据包发出时 SRH = [PE2, PE3, Dest]
经过 PE1 后: Segments Left=2, 目的地址变为 PE2::action
经过 PE2 后: Segments Left=1, 目的地址变为 PE3::action
到达 PE3: Segments Left=0, 交付给最终目的地
```
```
SRv6 的 End.SID 动作类型:
├── End : 转发到目的地址 (最基本)
├── End.X : 转发到指定邻接关系 (二层转发)
├── End.DX : 解封装并三层转发 (VxLAN 出口)
├── End.DT4 : 解封装并从 IPv4 VRF 转发
├── End.DT6 : 解封装并从 IPv6 VRF 转发
├── End.M : 添加到 MPLS 标签栈
└── End.B6 : 绑定列表 + 执行 SRGB 操作
```
## SDN(Software Defined Networking)
### SDN 三层架构
```mermaid
flowchart TB
subgraph "Application Plane"
App["Traffic Engineering<br/>Security Policies<br/>Multi-tenant Isolation<br/>Bandwidth On-Demand"]
end
subgraph "Control Plane"
Controller["SDN Controller<br/>OpenDaylight / ONOS / Ryu / FRRouting"]
end
subgraph "Data Plane"
SW1["Switch A<br/>OpenFlow Protocol"]
SW2["Switch B<br/>OpenFlow Protocol"]
SW3["Router C<br/>P4 可编程"]
end
App -->|"REST API"| Controller
Controller -->|"OpenFlow<br/>NetConf/YANG"| SW1
Controller -->|"OpenFlow"| SW2
Controller -->|"P4Runtime"| SW3
style Controller fill:#DDA0DD,color:#000
style App fill:#98FB98,color:#000
```
### OpenFlow 工作原理
```
传统交换机: OpenFlow 交换机:
数据面 + 控制面耦合 数据面与控制面分离
每台设备自己算路由 控制器决定一切转发逻辑
主机收到包 → 本地查表 → 转发 主机收到包 → 查 Flow Table
├─ 命中 → 按 Action 转发
└─ 未命中 → Packet-In 问 Controller
Controller → Flow-Mod 加规则
↓
下次命中 → 直接转发 (高速)
```
```python
# 用 ryu 框架写一个简单的 SDN Controller (Python)
from ryu.base import app_manager
from ryu.controller import ofp_event
from ryu.controller.handler import MAIN_DISPATCHER
from ryu.controller.handler import set_ev_cls
from ryun.ofproto import ofproto_v1_3
class SimpleSDN(app_manager.RyuApp):
OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER)
def packet_in_handler(self, ev):
msg = ev.msg
dp = msg.datapath
ofp = dp.ofproto
ofp_parser = dp.ofproto_parser
# 添加 FLOW_MOD: match 全部流量 → 泛洪到所有端口
actions = [ofp_parser.OFPActionOutput(ofp.OFPP_FLOOD)]
out = ofp_parser.OFPPacketOut(
datapath=dp, buffer_id=msg.buffer_id,
in_port=msg.match['in_port'], actions=actions
)
dp.send_msg(out)
```
### P4 —— 可编程数据平面
```
P4 (Programming Protocol-independent Packet Processors):
允许你定义数据包的解析器和处理流水线,然后编译成
特定 ASIC/FPGA/NIC 上的硬件代码。
核心概念:
parser → 如何解析报文头
deparser → 如何组装报文
pipeline → 匹配- action 表的流处理逻辑
control → 整个管道的编排
```
```p4
// P4 示例: 简单的负载均衡器
header ethernet_t ethernet;
header ipv4_t ipv4;
struct headers {
ethernet_t ethernet;
ipv4_t ipv4;
}
parser parse_headers(packet_in& p, out headers_t hdr) {
p.extract(hdr.ethernet);
p.extract(hdr.ipv4);
}
control ingress(packet_in& p, inout headers_t hdr) {
// 基于目标 IP 选择后端
action forward_to_server1() {
modify_field(hdr.ipv4.dstAddr, SERVER1_IP);
}
action forward_to_server2() {
modify_field(hdr.ipv4.dstAddr, SERVER2_IP);
}
table lb_table {
key = {
hdr.ipv4.dstAddr: exact;
}
actions = { forward_to_server1; forward_to_server2; }
}
}
```
## Go 中的网络编程最佳实践汇总
### 高性能 HTTP Server 模板
```go
package main
import (
"context"
"crypto/tls"
"net"
"net/http"
"time"
)
func NewServer(addr string, handler http.Handler) *http.Server {
return &http.Server{
Addr: addr,
Handler: handler,
// 超时设置 (防 Slowloris)
ReadTimeout: 10 * time.Second,
ReadHeaderTimeout: 5 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 1 << 20, // 1MB
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS13,
NextProtos: []string{"h2", "http/1.1"},
},
}
}
// StartListener 用自定义 listener 启动
func StartListener(srv *http.Server, network, addr string) error {
ln, err := net.Listen(network, addr)
if err != nil {
return err
}
// 底层使用 SO_REUSEPORT 实现多 worker 共享端口
return srv.Serve(ln)
}
```
### 出站请求的高性能 Transport
```go
import "golang.org/x/net/http2"
func HighPerfTransport() *http.Transport {
transport := &http.Transport{
MaxIdleConns: 200,
MaxIdleConnsPerHost: 50, // ← Go 默认只有 2, 务必显式设置!
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
DialContext: (&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true, // RFC 6724 happy eyeballs
}).DialContext,
}
// 强制 HTTP/2 (Go 1.6+ 自动协商, 显式配置确保)
http2.ConfigureTransport(transport)
return transport
}
```
## 关联笔记
- [[hhs/NETWORK/WireGuardVPN原理与实践]] — WireGuard 也可以作为 SDN 的数据平面组件
- [[hhs/NETWORK/eBPF与ServiceMesh]] — eBPF 可以与 SDN Controller 协同工作
- [[hhs/NETWORK/03-零拷贝与GoNetpoller]] — Go 网络编程的基础技术