网络|协议|报文
完整的网络数据报文
一个完整的网络数据包,从以太网帧头开始,包含了所有协议层的数据:
+------------------+------------------+------------------+------------------+
| 以太网帧头 | IP 头 | TCP 头 | TCP 数据 |
| (14/18字节) | (20-60字节) | (20-60字节) | (变长) |
+------------------+------------------+------------------+------------------+
1. 以太网帧(Ethernet Frame)
标准以太网帧结构(14字节)
0 6 12 14
+---------------+---------------+---------------+
| 目标MAC地址 | 源MAC地址 | 类型/长度 |
| (6 bytes) | (6 bytes) | (2 bytes) |
+---------------+---------------+---------------+
- 字节 0-5:目标 MAC 地址
- 字节 6-11:源 MAC 地址
- 字节 12-13:帧类型(EtherType) ← 这就是我们要解析的
为什么用 (packet[12] << 8) + packet[13]
提取帧类型 ?
因为帧类型字段占用 2 字节,且是大端序(Big-Endian) :
packet[12] = 高字节 (例如 0x08)
packet[13] = 低字节 (例如 0x00)组合成 16 位整数:
packet_type = (0x08 << 8) + 0x00 = 0x0800
常见的帧类型 EtherType 值
EtherType | 含义 |
---|---|
0x0800 |
IPv4 |
0x0806 |
ARP |
0x86DD |
IPv6 |
0x8100 |
VLAN 标签(802.1Q) |
VLAN 帧(18字节)
如果 packet_type == 0x8100
,说明这是一个 VLAN 标签帧:
0 6 12 14 16 18
+---------------+---------------+-------+-------+---------------+
| 目标MAC | 源MAC | 0x8100| VLAN | 实际EtherType |
| (6 bytes) | (6 bytes) | (2B) | (2B) | (2 bytes) |
+---------------+---------------+-------+-------+---------------+
- 字节 12-13:
0x8100
(VLAN 标记) - 字节 14-15:VLAN 标签(优先级 + VLAN ID)
- 字节 16-17:真正的 EtherType(如
0x0800
表示 IPv4)
2. IP 头(IPv4 Header)
IPv4 头结构(最小20字节)
0 8 15 16 31 32 47
+-------+-------+---------------+-------------------------------+
|Ver|IHL| DSCP | Total Length | Identification |
48 50 51 63 64 71 72 79 80 95
+-------+-----------------------+-------------------------------+
|Flags |Fragment Offset | TTL |Protocol| Header Chksum|
96 127
+---------------------------------------------------------------+
| Source IP Address (4 bytes) |
128 159
+-------------------------------+-------------------------------+
| Destination IP Address (4 bytes) |
160 191
+-------------------------------+-------------------------------+
| Options (if IHL > 5) |
+---------------------------------------------------------------+
关键字段解析
字节 0(版本 Ver + 报文头长度 IHL)
buf_post = 14 # IP 包起始地址,如果带 VLAN 标签则为 18
ip_version = pkt->data[buf_pos] & 0xf0;
ip_header_length = (pkt->data[buf_pos] & 0x0f) * 4;
- 高 4 位:版本(IPv4 = 4)
- 低 4 位:IHL(Internet Header Length) ,表示 IP 头长度,单位是 4 字节
字节 9(协议 Protocol 类型)
if (pkt->data[buf_pos + 9] != 0x06) {return -1; // 不是 TCP
}
常见协议号:
协议号 | 协议 |
---|---|
0x01 |
ICMP |
0x06 |
TCP |
0x11 |
UDP |
字节 2-3(总长度)
ip_total_length = (pkt->data[buf_pos + 2] << 8) | pkt->data[buf_pos + 3];
表示整个 IP 数据包的长度(包括 IP 头 + TCP 头 + TCP 数据)。
字节 12-19(源 IP 和目标 IP)
// 源 IP 地址(4字节)
memcpy(&source_ip, &pkt->data[buf_pos + 12], 4);// 目标 IP 地址(4字节)
memcpy(&dst_ip, &pkt->data[buf_pos + 16], 4);
3. TCP 头(TCP Header)
TCP 头结构(最小20字节)
0 16 32
+-------------------------------+-------------------------------+
| Source Port | Destination Port |
+-------------------------------+-------------------------------+
| valueSequence Number |
+-------------------------------+-------------------------------+
| Acknowledgment Number |
96
+-------+-------+---------------+-------------------------------+
|Offset |Reserv | Flags | Window Size |
+-------+-------+---------------+-------------------------------+
| Checksum | Urgent Pointer |
+-------------------------------+-------------------------------+
| Options (if Offset > 5) |
160 xx
+-------------------------------+-------------------------------+
| data |
关键字段解析
字节 0-3(源端口和目标端口)
// buf_pos 现在指向 TCP 头的起始位置
src_port = (pkt->data[buf_pos] << 8) | pkt->data[buf_pos + 1];
dst_port = (pkt->data[buf_pos + 2] << 8) | pkt->data[buf_pos + 3];// 检查是否是 8000 端口
if (dst_port != 8000 && src_port != 8000) {return -1;
}
字节 12(数据偏移 Data Offset)
tcp_header_length = ((pkt->data[buf_pos + 12] >> 4) & 0x0f) * 4;
- 高 4 位:Data Offset,表示 TCP 头长度,单位是 4 字节
- 低 4 位:保留位
示例:
pkt->data[buf_pos + 12] = 0x50右移 4 位: 0x05TCP头长度 = 5 × 4 = 20 字节(标准长度)
如果值为 0x60
,则 TCP 头长度 = 24 字节(包含选项)。
4. TCP 数据(HTTP 报文)
// TCP 数据的长度
tcp_data_length = ip_total_length - ip_header_length - tcp_header_length;// TCP 数据的起始位置
tcp_data = &pkt->data[buf_pos];// 搜索 "XJWECOME"
for (i = 0; i <= tcp_data_length - WEBHMI_TRIGGER_LEN; i++) {if (memcmp(&tcp_data[i], "XJWECOME", 8) == 0) {// 找到触发字符串!return 0;}
}