Skip to main content

3. Specification (规范)

3.1. Internet Header Format (互联网头部格式)

以下是互联网头部内容的摘要:

    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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Example Internet Datagram Header
(互联网数据报头部示例)

注意: 每个刻度标记代表一个比特位置。


Version (版本): 4 bits

版本字段指示互联网头部的格式。本文档描述版本4。

取值: 4 (二进制: 0100)

# 示例
version = 4 # IPv4

IHL (Internet Header Length, 互联网头部长度): 4 bits

互联网头部长度是以32比特字 (32-bit words) 为单位的互联网头部长度,因此指向数据的开始位置。请注意,正确头部的最小值为5。

取值范围: 5-15 (表示20-60字节)

计算:

头部长度 (字节) = IHL × 4

IHL=5 → 20字节 (最小,无选项)
IHL=6 → 24字节 (4字节选项)
IHL=15 → 60字节 (最大,40字节选项)

示例:

# 典型的IP头部 (无选项)
IHL = 5
header_length_bytes = IHL * 4 # 20字节

# 带选项的IP头部
IHL = 7
header_length_bytes = IHL * 4 # 28字节

Type of Service (服务类型): 8 bits

服务类型提供了所需服务质量抽象参数的指示。这些参数用于在通过特定网络传输数据报时指导实际服务参数的选择。

字段结构:

     0     1     2     3     4     5     6     7
+-----+-----+-----+-----+-----+-----+-----+-----+
| | | | | | |
| PRECEDENCE | D | T | R | 0 | 0 |
| | | | | | |
+-----+-----+-----+-----+-----+-----+-----+-----+

Bits 0-2: Precedence (优先级)
Bit 3: D = Delay (0=Normal, 1=Low)
Bit 4: T = Throughput (0=Normal, 1=High)
Bit 5: R = Reliability (0=Normal, 1=High)
Bits 6-7: Reserved (保留,必须为0)

Precedence (优先级)

含义用途
111Network Control网络控制
110Internetwork Control互联网控制
101CRITIC/ECP关键/紧急
100Flash Override闪电覆盖
011Flash闪电
010Immediate立即
001Priority优先
000Routine常规

服务指示

  • D (Delay): 低延迟 vs 正常延迟
  • T (Throughput): 高吞吐量 vs 正常吞吐量
  • R (Reliability): 高可靠性 vs 正常可靠性

使用建议:

❌ 不建议: 同时设置D、T、R全部为1
✅ 建议: 最多设置两个指示

常见组合:
- 交互式流量 (Telnet): 低延迟 (D=1)
- 文件传输 (FTP): 高吞吐量 (T=1)
- 关键数据: 高可靠性 (R=1)
- 批量数据: 正常 (D=T=R=0)

示例:

# ToS字段示例
TOS_ROUTINE = 0x00 # 000 00000
TOS_LOW_DELAY = 0x10 # 000 10000 (Telnet)
TOS_HIGH_THROUGHPUT = 0x08 # 000 01000 (FTP)
TOS_HIGH_RELIABILITY = 0x04 # 000 00100 (SNMP)

Total Length (总长度): 16 bits

总长度是数据报的长度,以八位字节 (octets) 为单位,包括互联网头部和数据。此字段允许数据报的长度最多为65,535个八位字节。

限制和建议:

  • 理论最大值: 65,535字节
  • 实际最小值: 576字节 (所有主机必须准备接受)
  • 推荐: 仅在确保目的地可以接受时才发送大于576字节的数据报

576字节的原因:

576字节 = 20字节IP头部 + 8字节传输层头部 + 512字节数据
(或 60字节最大IP头部 + 4字节TCP头部 + 512字节数据)

这允许合理大小的数据块传输,同时确保广泛兼容性

计算示例:

# 示例1: 最小IP数据报
ip_header = 20 # 字节
data = 0 # 字节
total_length = ip_header + data # 20字节

# 示例2: 典型数据报
ip_header = 20
tcp_header = 20
data = 1460
total_length = ip_header + tcp_header + data # 1500字节 (以太网MTU)

# 示例3: 分段数据报
ip_header = 20
data = 556
total_length = ip_header + data # 576字节

Identification (标识): 16 bits

由发送方分配的标识值,用于帮助组装数据报的分段。

用途:

  • 标识属于同一原始数据报的所有分段
  • 必须在源-目的地址对和协议的组合中唯一

示例:

import random

# 生成唯一标识
identification = random.randint(0, 65535)

# 所有分段使用相同的标识
fragment1.identification = identification
fragment2.identification = identification
fragment3.identification = identification

Flags (标志): 3 bits

各种控制标志。

      0   1   2
+---+---+---+
| | D | M |
| 0 | F | F |
+---+---+---+

Bit 0: Reserved (保留,必须为0)
Bit 1: DF (Don't Fragment) - 不分段
0 = May Fragment (可以分段)
1 = Don't Fragment (不要分段)
Bit 2: MF (More Fragments) - 更多分段
0 = Last Fragment (最后一个分段)
1 = More Fragments (还有更多分段)

DF (Don't Fragment) 标志

DF=1 时:

  • 数据报不得被分段
  • 如果需要分段才能传递,则丢弃数据报
  • 发送ICMP "Fragmentation Needed" 错误

用途:

# Path MTU Discovery (路径MTU发现)
# 发送带DF标志的数据报,逐渐增大大小
# 当收到ICMP错误时,确定路径MTU

def discover_path_mtu(dest_ip):
mtu = 1500
while mtu > 68:
send_packet(dest_ip, size=mtu, DF=1)
if receives_icmp_frag_needed():
mtu = get_mtu_from_icmp()
else:
return mtu
return 68 # 最小MTU

MF (More Fragments) 标志

MF=0: 这是最后一个分段 (或未分段的数据报) MF=1: 后面还有更多分段

示例:

原始数据报:
Flags: [0|0|0], Offset=0

分段后:
分段1: Flags: [0|0|1], Offset=0 (还有更多)
分段2: Flags: [0|0|1], Offset=185 (还有更多)
分段3: Flags: [0|0|0], Offset=370 (最后一个)

Fragment Offset (分段偏移): 13 bits

此字段指示此分段在数据报中的位置。偏移量以8个八位字节 (64比特) 块为单位。

计算:

实际偏移 (字节) = Fragment Offset × 8

Offset=0 → 从字节0开始
Offset=1 → 从字节8开始
Offset=10 → 从字节80开始
Offset=185 → 从字节1480开始

最大范围:

13 bits → 最大值 8191
8191 × 8 = 65,528字节

这与Total Length的最大值65,535字节相符

分段示例:

# 原始数据报: 1500字节 (20头部 + 1480数据)
# 分成3个分段,每个最多576字节

# 分段1: 576字节 (20头部 + 556数据)
fragment1 = {
'total_length': 576,
'identification': 12345,
'flags_MF': 1,
'fragment_offset': 0, # 0 × 8 = 字节0
'data': original_data[0:556]
}

# 分段2: 576字节 (20头部 + 556数据)
fragment2 = {
'total_length': 576,
'identification': 12345,
'flags_MF': 1,
'fragment_offset': 69, # 69 × 8 = 字节552
'data': original_data[556:1112]
}

# 分段3: 408字节 (20头部 + 388数据)
fragment3 = {
'total_length': 408,
'identification': 12345,
'flags_MF': 0, # 最后一个分段
'fragment_offset': 139, # 139 × 8 = 字节1112
'data': original_data[1112:1500]
}

Time to Live (生存时间, TTL): 8 bits

此字段指示数据报允许在互联网系统中保持活动的最长时间。如果此字段包含值零,则数据报必须被销毁。

单位: 秒(理论上)或跳数(实际上)

范围: 0-255

操作:

  • 发送方设置初始TTL值
  • 每个路由器在转发前减少TTL
  • 如果TTL达到0,丢弃数据报并发送ICMP Time Exceeded

常见初始值:

Linux:   64
Windows: 128
Cisco: 255

示例:

def forward_packet(packet):
packet.ttl -= 1

if packet.ttl == 0:
send_icmp_time_exceeded(packet.source)
drop_packet(packet)
else:
route_packet(packet)

应用:

# Traceroute利用TTL
# 发送TTL=1, 2, 3...的数据报
# 每个路由器返回ICMP Time Exceeded
# 从而映射出到目的地的路径

$ traceroute google.com
1 192.168.1.1 1 ms
2 10.0.0.1 5 ms
3 172.16.1.1 10 ms
...

Protocol (协议): 8 bits

此字段指示下一级协议,用于互联网数据报的数据部分。

常见值:

协议说明
1ICMP互联网控制消息协议
2IGMP互联网组管理协议
6TCP传输控制协议
17UDP用户数据报协议
41IPv6IPv6封装
47GRE通用路由封装
50ESP封装安全载荷
51AH认证头部
89OSPF开放最短路径优先

示例:

PROTOCOL_ICMP = 1
PROTOCOL_TCP = 6
PROTOCOL_UDP = 17

def process_ip_packet(packet):
if packet.protocol == PROTOCOL_TCP:
handle_tcp(packet.data)
elif packet.protocol == PROTOCOL_UDP:
handle_udp(packet.data)
elif packet.protocol == PROTOCOL_ICMP:
handle_icmp(packet.data)

Header Checksum (头部校验和): 16 bits

头部校验和字段是互联网头部的16位反码和的反码。

重要:

  • 仅检查头部,不检查数据
  • 每经过一个路由器都必须重新计算(因为TTL等字段会变化)

计算方法:

def calculate_checksum(header):
"""
计算IP头部校验和
"""
# 1. 将校验和字段设为0
header[10:12] = b'\x00\x00'

# 2. 将头部视为16位字的序列并求和
sum = 0
for i in range(0, len(header), 2):
word = (header[i] << 8) + header[i+1]
sum += word

# 3. 将进位加回
while sum >> 16:
sum = (sum & 0xFFFF) + (sum >> 16)

# 4. 取反码
checksum = ~sum & 0xFFFF

return checksum

def verify_checksum(header):
"""
验证IP头部校验和
"""
sum = 0
for i in range(0, len(header), 2):
word = (header[i] << 8) + header[i+1]
sum += word
while sum >> 16:
sum = (sum & 0xFFFF) + (sum >> 16)

# 如果校验和正确,结果应该是0xFFFF
return (sum & 0xFFFF) == 0xFFFF

示例:

IP头部 (十六进制):
45 00 00 3c 1c 46 40 00 40 06 00 00 ac 10 0a 63 ac 10 0a 0c

计算步骤:
1. 求和: 0x4500 + 0x003c + 0x1c46 + 0x4000 + 0x4006 + 0x0000
+ 0xac10 + 0x0a63 + 0xac10 + 0x0a0c
= 0x2BBCF

2. 处理进位: 0xBBCF + 0x0002 = 0xBBD1

3. 取反码: ~0xBBD1 = 0x442E

校验和 = 0x442E

Source Address (源地址): 32 bits

源地址是32位的发送方IP地址。

格式: 点分十进制

192.168.1.100 = 11000000.10101000.00000001.01100100

Destination Address (目的地址): 32 bits

目的地址是32位的接收方IP地址。


Options (选项): 可变长度

选项可能出现也可能不出现在数据报中。它们在所有IP实现中必须 (must) 被实现。

常见选项:

1. Security (安全)

用于指定安全级别

2. Loose Source Routing (松散源路由)

指定数据报必须经过的路由器列表

3. Strict Source Routing (严格源路由)

指定数据报必须严格按照的路径

4. Record Route (记录路由)

记录数据报经过的路由器

5. Timestamp (时间戳)

记录数据报经过路由器的时间


Padding (填充): 可变长度

互联网头部填充用于确保互联网头部以32位字边界结束。填充为零。


字段总结表

字段大小范围/值说明
Version4 bits4IP版本
IHL4 bits5-15头部长度 (×4字节)
ToS8 bits0-255服务类型
Total Length16 bits20-65535总长度 (字节)
Identification16 bits0-65535分段标识
Flags3 bitsDF, MF控制标志
Fragment Offset13 bits0-8191偏移量 (×8字节)
TTL8 bits0-255生存时间
Protocol8 bits1,6,17...上层协议
Header Checksum16 bits-头部校验和
Source Address32 bits-源IP地址
Dest Address32 bits-目的IP地址
Options变长-可选字段
Padding变长-填充到32位边界

关键要点: IP头部格式是互联网协议的核心,每个字段都有其特定用途。理解这些字段对于网络编程、故障排除和安全分析至关重要。