TCP/UDP 网络协议教程 / 02-IP 协议基础
02 - IP 协议基础
2.1 IP 协议概述
IP(Internet Protocol)是网络层的核心协议,负责将数据包从源主机传输到目的主机。
关键特性:
• 无连接:不建立连接,直接发送
• 不可靠:不保证到达、不保证顺序
• 尽力而为:Best-effort delivery
• 路由:通过路由器逐跳转发
2.2 IPv4 头部结构
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 字段 | 长度 | 说明 |
|---|
| Version | 4 bit | IP 版本号(4 或 6) |
| IHL | 4 bit | 头部长度(单位:4字节) |
| Total Length | 16 bit | 数据包总长度(最大 65535) |
| TTL | 8 bit | 生存时间(每经过路由器减1) |
| Protocol | 8 bit | 上层协议(TCP=6, UDP=17) |
| Source Address | 32 bit | 源 IP 地址 |
| Destination Address | 32 bit | 目的 IP 地址 |
# 解析 IP 头部示例
import struct
def parse_ip_header(data):
"""解析 IP 头部"""
if len(data) < 20:
return None
# 解析前 20 字节
fields = struct.unpack('!BBHHHBBHII', data[:20])
version = fields[0] >> 4
ihl = fields[0] & 0xF
ttl = fields[5]
protocol = fields[6]
src_ip = struct.pack('!I', fields[8])
dst_ip = struct.pack('!I', fields[9])
import socket
return {
'version': version,
'header_length': ihl * 4,
'ttl': ttl,
'protocol': protocol, # 6=TCP, 17=UDP
'src_ip': socket.inet_ntoa(src_ip),
'dst_ip': socket.inet_ntoa(dst_ip)
}
2.3 IPv4 地址
地址分类
| 类别 | 范围 | 默认掩码 | 用途 |
|---|
| A 类 | 1.0.0.0 - 126.255.255.255 | /8 | 大型网络 |
| B 类 | 128.0.0.0 - 191.255.255.255 | /16 | 中型网络 |
| C 类 | 192.0.0.0 - 223.255.255.255 | /24 | 小型网络 |
| D 类 | 224.0.0.0 - 239.255.255.255 | - | 多播 |
| E 类 | 240.0.0.0 - 255.255.255.255 | - | 保留 |
私有地址
| 类别 | 地址范围 | CIDR |
|---|
| A 类 | 10.0.0.0 - 10.255.255.255 | 10.0.0.0/8 |
| B 类 | 172.16.0.0 - 172.31.255.255 | 172.16.0.0/12 |
| C 类 | 192.168.0.0 - 192.168.255.255 | 192.168.0.0/16 |
特殊地址
| 地址 | 用途 |
|---|
| 0.0.0.0 | 所有地址 |
| 127.0.0.1 | 本地回环 |
| 255.255.255.255 | 有限广播 |
| 169.254.x.x | 链路本地地址 |
2.4 子网划分
CIDR 表示法
IP地址/前缀长度
例如:192.168.1.0/24
前缀长度 24 表示前 24 位是网络部分
子网掩码:255.255.255.0
可用主机数:2^(32-24) - 2 = 254
import ipaddress
def subnet_info(network_str):
"""获取子网信息"""
network = ipaddress.ip_network(network_str, strict=False)
return {
'network_address': str(network.network_address),
'broadcast_address': str(network.broadcast_address),
'netmask': str(network.netmask),
'prefix_len': network.prefixlen,
'total_hosts': network.num_addresses,
'usable_hosts': network.num_addresses - 2 if network.prefixlen < 31 else network.num_addresses,
'first_host': str(network.network_address + 1),
'last_host': str(network.broadcast_address - 1)
}
# 示例
print(subnet_info('192.168.1.0/24'))
# {'network_address': '192.168.1.0', 'broadcast_address': '192.168.1.255',
# 'netmask': '255.255.255.0', 'prefix_len': 24, 'total_hosts': 256,
# 'usable_hosts': 254, 'first_host': '192.168.1.1', 'last_host': '192.168.1.254'}
子网划分示例
原始网络:192.168.1.0/24(256 个地址)
划分为 4 个子网:
┌─────────────────┬─────────────────┬─────────────────┬─────────────────┐
│ 192.168.1.0/26 │ 192.168.1.64/26 │192.168.1.128/26 │192.168.1.192/26 │
│ 64 个地址 │ 64 个地址 │ 64 个地址 │ 64 个地址 │
│ .1 - .62 │ .65 - .126 │ .129 - .190 │ .193 - .254 │
└─────────────────┴─────────────────┴─────────────────┴─────────────────┘
2.5 IPv6 简介
IPv6 地址格式
128 位地址,冒号十六进制表示:
完整:2001:0db8:0000:0000:0000:0000:0000:0001
简化:2001:db8::1
规则:
1. 前导零可省略
2. 连续全零段可用 :: 替代(只能使用一次)
IPv4 vs IPv6
| 特性 | IPv4 | IPv6 |
|---|
| 地址长度 | 32 位 | 128 位 |
| 地址数量 | ~43 亿 | ~3.4×10^38 |
| 表示方法 | 点分十进制 | 冒号十六进制 |
| 头部大小 | 20-60 字节 | 40 字节(固定) |
| 分片 | 路由器和发送端 | 仅发送端 |
| 校验和 | 有 | 无(交给上层) |
| NAT | 广泛使用 | 不需要 |
2.6 路由基础
路由表
# 查看路由表
$ ip route show
default via 192.168.1.1 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.100
10.0.0.0/8 via 192.168.1.254 dev eth0
路由决策过程:
1. 提取目的 IP 地址
2. 与路由表逐条匹配(最长前缀匹配)
3. 找到匹配项 → 从指定接口转发
4. 无匹配项 → 使用默认路由(default gateway)
5. 无默认路由 → 丢弃并发送 ICMP 错误
路由类型
| 类型 | 说明 | 示例 |
|---|
| 直连路由 | 直接连接的网络 | 192.168.1.0/24 dev eth0 |
| 静态路由 | 手动配置 | 10.0.0.0/8 via 192.168.1.254 |
| 动态路由 | 协议自动学习 | BGP、OSPF、RIP |
| 默认路由 | 兜底路由 | default via 192.168.1.1 |
2.7 IP 分片
分片原因
MTU (Maximum Transmission Unit):链路层最大传输单元
以太网 MTU = 1500 字节
IP 数据包最大 = 65535 字节
当 IP 包 > MTU 时,需要分片
分片字段
| 字段 | 说明 |
|---|
| Identification | 标识同一个数据包的所有分片 |
| Flags.DF | Don’t Fragment,不允许分片 |
| Flags.MF | More Fragments,后面还有分片 |
| Fragment Offset | 分片在原始数据包中的偏移(8字节为单位) |
def analyze_fragments():
"""分片示例分析"""
original_size = 4000 # 原始数据包大小
mtu = 1500 # 链路 MTU
ip_header = 20 # IP 头部大小
max_data = mtu - ip_header # 每片最大数据 = 1480
fragments = []
offset = 0
remaining = original_size - ip_header # 3980 字节数据
while remaining > 0:
frag_data = min(remaining, max_data)
mf = 1 if remaining > max_data else 0
fragments.append({
'offset': offset,
'data_size': frag_data,
'MF': mf,
'total_size': frag_data + ip_header
})
offset += frag_data
remaining -= frag_data
return fragments
for frag in analyze_fragments():
print(f"分片: offset={frag['offset']:5d}, size={frag['data_size']:5d}, MF={frag['MF']}")
# 分片: offset= 0, size= 1480, MF=1
# 分片: offset= 1480, size= 1480, MF=1
# 分片: offset= 2960, size= 1020, MF=0
2.8 ICMP 协议
ICMP 是 IP 的辅助协议,用于错误报告和网络诊断。
| 类型 | 代码 | 说明 | 工具 |
|---|
| 0 | 0 | Echo Reply | ping |
| 3 | 0 | 网络不可达 | - |
| 3 | 1 | 主机不可达 | - |
| 3 | 3 | 端口不可达 | - |
| 8 | 0 | Echo Request | ping |
| 11 | 0 | TTL 超时 | traceroute |
# ping 使用 ICMP Echo
$ ping -c 3 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=118 time=12.3 ms
# traceroute 使用 TTL 超时
$ traceroute 8.8.8.8
1 192.168.1.1 (192.168.1.1) 1.234 ms
2 10.0.0.1 (10.0.0.1) 5.678 ms
...
2.9 ARP 协议
ARP 将 IP 地址解析为 MAC 地址。
ARP 工作流程:
1. 主机 A 要发送数据给 192.168.1.2
2. A 检查 ARP 缓存表
3. 若无缓存,广播 ARP 请求:"谁是 192.168.1.2?"
4. 192.168.1.2 单播回复:"我是,MAC 是 xx:xx:xx:xx:xx:xx"
5. A 更新 ARP 缓存,发送数据
# 查看 ARP 缓存
$ arp -a
? (192.168.1.1) at 00:11:22:33:44:55 [ether] on eth0
? (192.168.1.2) at 66:77:88:99:aa:bb [ether] on eth0
2.10 注意事项
⚠️ MTU 问题:路径 MTU 发现(PMTUD)失败会导致连接问题,某些防火墙会丢弃 ICMP “需要分片"消息
⚠️ IP 地址耗尽:IPv4 地址已耗尽,NAT 和 IPv6 是解决方案
⚠️ 安全考虑:IP 欺骗(IP Spoofing)是常见的攻击手段,需要配合上层协议验证
2.11 扩展阅读
下一章:03 - TCP 头部详解 - 深入理解 TCP 协议的每个字段