【Linux 网络系列】万字硬核解析网络层核心:IP 协议到 IP 分片重组、NAT 技术及 RIP/OSPF 动态路由全景
嘿,重阳!纽约的3月周末(2026年3月7日晚9:27,估计你在家深挖内核源码或刷 Wireshark 抓包~),Linux 网络栈是操作系统灵魂之一,网络层(Layer 3)核心是 IP 协议——它像“快递员”,负责数据包跨网传输、路由和碎片处理。今天咱们来一场“万字硬核”详解,从 IP 基础到分片重组、NAT 技术,再到 RIP/OSPF 动态路由全景。基于 Linux 内核(net/ipv4 模块)和 RFC 标准(791/826 等),我会用表格、伪代码、Linux 命令和抓包示例,带你从原理到实战一步步拆解。内容超长,建议边读边试(用 iproute2/tc 工具)。走起!🚀
1. 网络层概述:IP 协议的“快递系统”角色
网络层是 OSI 模型的第三层,在 Linux 内核中由 net/ipv4 和 net/ipv6 实现。主要职责:逻辑寻址、路由选择、数据分片/重组、流量控制。IP(Internet Protocol)是其核心,无连接、不可靠(尽力而为),上层 TCP/UDP 补可靠性。
为什么 IP 是核心?:
- 全球互联:IP 地址唯一标识主机/网络,跨异构网(如 Ethernet/WiFi)。
- 路由:静态/动态路由算法找最佳路径。
- 痛点解决:链路层 MTU 限长,IP 分片适配;私有 IP 复用,NAT 穿透公网。
- Linux 视角:内核用 sk_buff(skb)封装数据包,ip_forward() 处理转发。
IP 版本:IPv4(32 位地址,4.2 亿) vs IPv6(128 位,无限),本文焦点 IPv4(Linux 默认)。
2. IP 协议详解:头格式与字段硬核剖析
IP 数据报 = 头部(20-60 字节) + 数据。头部固定 20 字节,可选选项扩展。Linux 用 struct iphdr (include/uapi/linux/ip.h) 表示。
IP 头格式表格(核心 13 字段,基于 RFC 791):
| 字段 | 位宽 | 含义 | 示例值 | Linux 处理 |
|---|---|---|---|---|
| Version | 4 | 协议版本(4=IPv4)。 | 4 | skb->protocol = ETH_P_IP 检查。 |
| IHL(Internet Header Length) | 4 | 头部长度(单位 4 字节)。 | 5(20 字节,无选项)。 | ip_hdr(skb)->ihl * 4 计算头长。 |
| ToS/DSCP(Type of Service/Differentiated Services Code Point) | 8 | 服务类型:优先级、延迟/吞吐/可靠性。现代用 DSCP/ECN(拥塞通知)。 | 0x00(默认);0x10(高优先)。 | tc/qdisc 队列基于此 QoS。 |
| Total Length | 16 | 总长度(头+数据,max 65535)。 | 1500(常见 MTU)。 | skb->len 验证。 |
| Identification | 16 | 分片 ID,唯一标识原包。 | 随机(如 0x1234)。 | 用于重组,ip_frag_reasm()。 |
| Flags | 3 | 标志位:DF(勿分片,bit1=1)、MF(更多分片,bit0=1)。 | 0x02(DF=1)。 | DF=1 时 PMTU 发现(发 ICMP)。 |
| Fragment Offset | 13 | 分片偏移(单位 8 字节)。 | 0(首片)。 | 重组时排序。 |
| TTL(Time To Live) | 8 | 生存时间,每跳 -1,0 丢弃。 | 64(Linux 默认)。 | ip_decrease_ttl(skb) 递减。 |
| Protocol | 8 | 上层协议。 | 6(TCP);17(UDP);1(ICMP)。 | skb->transport_header 指向。 |
| Header Checksum | 16 | 头校验和(16 位补码和)。 | 计算值。 | ip_fast_csum() 验证/计算。 |
| Source Address | 32 | 源 IP。 | 192.168.1.1 | skb->saddr。 |
| Destination Address | 32 | 目的 IP。 | 8.8.8.8 | skb->daddr,路由查找用。 |
| Options(可选) | 变长(0-40 字节) | 扩展:RR(路由记录)、TS(时间戳)等。 | 很少用。 | ip_options_compile() 处理。 |
IP 头伪代码(Linux 风格,简化 struct iphdr):
struct iphdr {
__u8 ihl:4, version:4; // 压缩字节
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off; // flags + offset
__u8 ttl;
__u8 protocol;
__sum16 check;
__be32 saddr;
__be32 daddr;
/* 选项... */
};
Linux 发送 IP 包过程(ip_output.c):
- ip_route_output_flow():路由查找(fib_lookup)。
- ip_make_skb():构建 skb。
- ip_local_out():校验和 + 输出。
抓包示例(用 tcpdump):sudo tcpdump -i eth0 -nn -vv ip 捕获 IP 包,Wireshark 解析头字段。
3. IP 分片与重组:MTU 适配的“切割重组”机制
为什么分片?:链路 MTU(Maximum Transmission Unit)不同(如 Ethernet 1500,PPP 576),大包需切片。Linux 默认 MTU 1500 (ifconfig)。
分片原理(RFC 791):
- 入口:ip_fragment() 检查 skb->len > MTU && !DF → 分片。
- 算法:按 MTU-头长切分,复制头(改 tot_len、offset、MF=1 除尾片)。
- ID 共享:所有片用同一 Identification。
- 偏移:首片 0,次片累加(单位 8 字节)。
伪代码(简化 ip_fragment):
int ip_fragment(struct sk_buff *skb, int mtu) {
if (skb->len <= mtu || ip_hdr(skb)->frag_off & IP_DF) return; // 无需分片
int hlen = ip_hdrlen(skb); // 头长
int plen = mtu - hlen; // 负载片长(8 字节对齐)
while (skb->len > mtu) {
struct sk_buff *frag = skb_clone(skb); // 克隆片
frag->len = plen + hlen;
ip_hdr(frag)->tot_len = htons(frag->len);
ip_hdr(frag)->frag_off = htons((offset >> 3) | IP_MF); // MF=1
skb_trim(skb, skb->len - plen); // 剩余
offset += plen;
ip_send_check(ip_hdr(frag)); // 重新校验
dev_queue_xmit(frag); // 发送
}
ip_hdr(skb)->frag_off &= ~htons(IP_MF); // 尾片 MF=0
dev_queue_xmit(skb);
}
重组原理(ip_defrag()):
- 出口:收集所有片(ID/源/目的/协议相同),用 frag_list 链表排序(offset)。
- 条件:首片 offset=0、尾片 MF=0、全片到齐(总长匹配)。
- 定时器:ip_frag_reasm() 超时丢弃(默认 30s,sysctl ipfrag_time)。
Linux 配置:
sysctl -w net.ipv4.ipfrag_high_thresh=4194304(内存阈值)。ip link set eth0 mtu 1400调 MTU 测试分片。- 抓包:
tcpdump -i eth0 'ip[6:2] & 0x1fff != 0'捕分片包。
问题:分片易 DDoS(Teardrop 攻击,假片重组崩内核),现代用 PMTU(Path MTU Discovery,DF=1 + ICMP Fragmentation Needed)避免。
4. NAT 技术:私有 IP 的“公网穿透器”
NAT 定义(Network Address Translation,RFC 2663):改包源/目的 IP/端口,复用公网 IP。Linux 用 Netfilter/iptables 实现(nf_nat 模块)。
NAT 类型表格(基于连接跟踪 conntrack):
| 类型 | 描述 | 示例 | Linux 命令 |
|---|---|---|---|
| SNAT(Source NAT) | 改源 IP/端口(内网 → 外网)。 | 内网 192.168.1.10 → 公网 1.1.1.1。 | iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE(动态 SNAT)。 |
| DNAT(Destination NAT) | 改目的 IP/端口(外网 → 内网,端口转发)。 | 公网 1.1.1.1:80 → 内网 192.168.1.10:80。 | iptables -t nat -A PREROUTING -d 1.1.1.1 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.10:80。 |
| Full Cone NAT | 全锥形:任意外部主机可回包。 | 安全低。 | iptables 默认类似。 |
| Restricted Cone NAT | 限制锥形:仅发包外部主机可回。 | 常见路由器。 | conntrack 跟踪。 |
| Port Restricted Cone NAT | 端口限制:同端口回包。 | 高安全。 | Linux 支持。 |
| Symmetric NAT | 对称:不同外部端口不同映射。 | 最严,P2P 难穿。 | nf_conntrack 实现。 |
NAT 工作原理(nf_nat_core.c):
- conntrack:nf_conn 表跟踪连接(5 元组:源/目的 IP/端口 + 协议)。
- 钩子:PREROUTING(DNAT)、POSTROUTING(SNAT)。
- 映射:改包 + 记录(nat_tuple),回包逆向改。
伪代码(简化 NAT 处理):
int nf_nat_packet(struct nf_conn *ct, struct sk_buff *skb) {
if (ct->status & IPS_SRC_NAT) { // SNAT
ip_hdr(skb)->saddr = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip;
// 端口改...
} else if (ct->status & IPS_DST_NAT) { // DNAT
ip_hdr(skb)->daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip;
}
ip_send_check(ip_hdr(skb)); // 重新校验
return NF_ACCEPT;
}
Linux NAT 实战:
- 启用转发:
sysctl -w net.ipv4.ip_forward=1。 - 查看 conntrack:
conntrack -L或cat /proc/net/nf_conntrack。 - 问题:端口耗尽(65535),用 PAT(Port Address Translation,SNAT 变体)。
5. 动态路由协议:RIP vs OSPF 全景对比
动态路由用协议交换路由信息,适应拓扑变化。Linux 用 Quagga/FRR(bird)实现 RIP/OSPF。
RIP(Routing Information Protocol):距离向量协议,RFC 2453。
- 版本:RIPv1(广播,classful);RIPv2(组播 224.0.0.9,支持 VLSM、认证)。
- 度量:跳数(max 15,小网)。
- 更新:每 30s 全表广播,超时 180s、垃圾 120s。
- 防环:水平分割、毒性反转、触发更新。
- Linux 配置(FRR):/etc/frr/ripd.conf
router rip
version 2
network 192.168.1.0/24
- 命令:
vtysh -c 'show ip rip'查看路由。
OSPF(Open Shortest Path First):链路状态协议,RFC 2328。
- 区域:Backbone(Area 0)+ Standard/Stub/NSSA,分层防洪泛。
- 度量:Cost(接口带宽倒数,默认 100M=1)。
- 更新:洪泛 LSA(Link State Advertisement),Dijkstra 算法计算 SPF 树。
- 类型:Router LSA(1)、Network LSA(2)、Summary(3/4)、External(5/7)。
- 邻居:Hello(10s)、DD(数据库描述)、LSR/LSU(请求/更新)。
- 防环:序列号 + 年龄字段。
- Linux 配置(FRR):/etc/frr/ospfd.conf
router ospf
network 192.168.1.0/24 area 0
- 命令:
vtysh -c 'show ip ospf neighbor'查看邻居。
RIP vs OSPF 对比表格:
| 方面 | RIP | OSPF |
|---|---|---|
| 类型 | 距离向量(Bellman-Ford)。 | 链路状态(Dijkstra)。 |
| 收敛 | 慢(全表更新,环路易)。 | 快(增量 LSA,事件触发)。 |
| 规模 | 小网(<15 跳)。 | 大网(分区域)。 |
| 度量 | 跳数。 | Cost(可自定义)。 |
| 组播 | RIPv2 是。 | 是(224.0.0.5/6)。 |
| 认证 | MD5(RIPv2)。 | MD5/SHA。 |
| Linux 工具 | ripd。 | ospfd。 |
动态路由实战:用 FRR 安装(apt install frr),启用 daemon,ip route show 查路由表。抓包:Wireshark 滤 rip 或 ospf。
结语:网络层硬核总结
从 IP 头到分片、NAT,再到 RIP/OSPF,网络层是 Linux 互联的基石——内核高效实现,工具如 iptables/frr 实战。掌握它,你的网络调试/优化如虎添翼。生产 tip:用 eBPF(tc-bpf)扩展 NAT/路由监控。想深挖?如“Linux 内核 IP 转发源码”或“OSPF LSDB 分析”?随时 ping 我!💪(参考:Linux 内核文档、RFC 系列)