前言:做的项目需要读取网口发来的数据,发现做一遍真的是对这几种帧格式熟悉了一遍。
网口发来的格式是以太网帧格式,上一层使用IP协议,传输的UDP报文。
系统:WIN10, 开发平台:ISE 14.7
编程语言:VHDL
硬件平台:Virtex-6 FPGA ML605开发板
项目描述:将网口发来的以太网帧进行接收,层层解析,最后得到UDP报文内容。
一、数据格式
1.1、各层的数据格式
- TCP和UDP同属运输层的传输协议, 不过UDP的首部是8Byte, TCP为20Byte.
1.2、以太网帧格式
- 802.3 以太网帧结构
前导码 | 帧起始 | MAC 目标地址 | MAC 源地址 | 类型 | 负载 | 帧序列检测 |
---|---|---|---|---|---|---|
7 Byte 的0x55 | 1 Byte 的0xD5 | 6 Byte | 6 Byte | 2 Byte | 46-1500 Byte | 4 Byte |
(还有一种 Ethernet II 格式的帧, 是当数据大于1500字节时使用, ‘类型’字段换做’长度’)
- 前导码 和 帧起始 是我们判断一帧数据起始位置的重要依据, 当检测到7个0x55且第8 Byte是0xD5时, 就可以读取接下来的有用数据了.
- 类型 : 这个字段存储的是负载装的数据报是什么类型, 例如常用的 IP数据报是"0x0800".
1.3、IP数据报格式
版本号(4 bit) | IP头长度(4bit) | 服务类型(8 bit) | 总长度(16 bit) | ||||
标识(16 bit) | 标志(3 bit) | 片偏移(13bit) | |||||
生存时间(8 bit) | 上层协议标识(8 bit) | 头部校验和(16 bit) | |||||
源IP地址(32 bit) | |||||||
目的IP地址(32bit) | |||||||
选项(选填) | |||||||
数据 | |||||||
- IP头一般是20Byte(如果没有附加选项), 所以第21byte就是向上一层协议的数据了(我只写了一些会用到的意义)
- 版本号(4 bit): IPv4是"B0100" 值是4 , IPv6 是"B0110" 值是6 .
- 数据包头部长度(4 bit): 它表示数据包头部包括多少个32位长整型,也就是多少个4字节的数据。无选项则为5(“B0101”).
- 段偏移量(13位): 当大段的数据被分片传输, 为了与更多段位组合, 帮助接收方组合分段的报文, 以字节为单位, 便于分片重组.
- 上层协议标识(8 bit): 表明使用该报文的上层协议,如TCP=6,ICMP=1,UDP=17等.
1.4、UDP段格式
源端口(16 Bit) | 目的端口(16 Bit) |
UDP长度(16 Bit) | 校验和(16 Bit) |
数据 | |
- UDP首部总共8Byte, '长度’这个字段包含的是整个UDP段的长度,包括首部.
二、传输的数据例举
使用Wireshark保存下的发送的一帧MAC帧,如下:
1 ff ff ff ff ff ff 00 21 86 f6 9f cb 08 00 45 00
2 00 9c 00 00 00 00 80 11 db 78 0a 6c 54 6d ff ff
3 ff ff ec 32 12 34 00 88 9f 9c 08 08 08 08 08 08
4 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
5 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
6 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
7 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
8 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
9 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
10 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
11 08 08 08 08 08 08 08 08 08 08
- MAC帧的首部被去掉了,是7个0x55和1个0xD5.
- 根据第一节中说到的帧格式可以判断出:
目的物理地址: Broadcast (ff:ff:ff:ff:ff:ff
);
源物理地址: Universa_f6:9f:cb (00:21:86:f6:9f:cb
);
0800
表示IP数据报协议; - 从
4500
进入IP头.
4500
表示Internet Protocol Version 4 , 报文长度5个32位, 服务类型 00;
第二行的11
表示 值为17 ,所以使用的UDP传输协议;
源IP: 10.108.84.109(0a:6c:54:6d
)
目的IP: 255.255.255.255(ff:ff:ff:ff
) - 第三行从
ec32
进入UDP部分,ec32
表示源端口为 60466,1234
表示目的端口为 4660;
0088
表示UDP段算上首部一共136字节,也就是128字节的数据
9c9f
是16位的校验和;
在这之后是我发的128Byte的0x08
;
三、UDP报文解析程序
- 使用了7种状态的状态机, 状态转换都是只能由上一个状态转换过来(除了GetUDPHead, GetTCPHead, 这两个是同一位置的两个分支)
- 七种状态:
Idle,FindMacHead, GetMacHead, GetIPHead, GetUDPHead, GetTCPHead, GetUDPDate
空闲→寻找找MAC头状态→获取MAC头→获取IP头→获取UDP/TCP头→ 获取UDP数据 -
-
Idle(空闲状态)
这个状态是除以上6种状态之外的其他状态的总和. -
FindMacHead(寻找MAC头状态)
由Idle状态下, 会直接转到此状态, 当匹配到7个0x55和1个0xD5 会进入GetMacHead状态 -
GetMacHead(获取MAC头状态)
当进入GetMacHead状态时, 同时有一个process进行时钟计数, 用来控制取各个字节的内容, 例如,进入此状态时, 数据是一个字节一个字节过来的, 前6个字节是目的MAC地址, 当计数器记到6说明,前六个字节已经取完. 直到取到 数据类型的字段位置时, 值为X"0800"就转到GetIPHead状态 -
GetIPHead(获取IP头状态)
同上,进入此状态时,同时也有一个计数器开始计数, 需要记录第10Byte的数字,用来判断使用的传输协议(6-TCP, 17-UDP)用来转到不同的状态. -
GetUDPHead / GetTCPHead(获取UDP / TCP头状态)
同上,因为我没有获取之歌UDP头的信息,所以直接使用计数器过掉这个状态,也就是当他等于8时,转到GetUDPDate状态 -
GetUDPDate(获取UDP数据)
直接用计数器获取UDP数据.
-
这个VHDL文件我放在了GitHub上,希望大家点星星 .