LWIP主线
STM32+LWIP------报文格式解析
1. DMA描述符
平台为stm32F407+LAN8720,使用DMA中断接收方式。在ST提供的以太网驱动程序stm32f4x7_eth.c中使用的DMA描述符管理缓冲区,其连接结构描述如下图:
-
一个以太网数据包可以跨越一个或者多个DMA描述符。
-
一个DMA描述符只能用于一个以太网数据包。
-
DMA描述符列表中的最后一个描述符指向第一个,形成链式结构。
在ST的以太网驱动程序stm32f4x7_eth.h中用ETH_DMADESCTypeDef定义DMA描述符,代码如下:
typedef struct { __IO uint32_t Status; /*!状态 */ uint32_t ControlBufferSize; /*控制Buffer1,和Buffer2的长度*/ uint32_t Buffer1Addr; /*Buffer1 地址 */ uint32_t Buffer2NextDescAddr; /* Buffer2 地址或者下一个描述符地址 */ /* 增强的以太网DMA描述符 */ #ifdef USE_ENHANCED_DMA_DESCRIPTORS uint32_t ExtendedStatus; /*增强描述符状态*/ uint32_t Reserved1; /* 保留*/ uint32_t TimeStampLow; /* 时间戳低位 */ uint32_t TimeStampHigh; /* 时间戳高位 */ #endif /* USE_ENHANCED_DMA_DESCRIPTORS */ } ETH_DMADESCTypeDef;
2. DMA描述符链式结构
STM32的DMA描述符又分为发送描述符和接收描述符,它们都用ETH_DMADESCTypeDef来定义,用图说明以接收描述符(发送描述符类似)的链式结构如图:
在stm32f4x7_eth.c文件中定义两个DMA描述符数组,一个用DMA接收一个用于DMA发送,代码如下:
__align(4) ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB];/* Ethernet Rx MA Descriptor */
__align(4) ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB];/* Ethernet Tx DMA Descriptor */
__align(4) uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; /* Ethernet Receive Buffer */
__align(4) uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; /* Ethernet Transmit Buffer */
3. DMA追踪描述符
因为它们被用作数据缓冲,那么占用内部RAM太多,所以在工程中用内存分配函数为它们在外部SRAM中申请内存。有了缓冲区后,stm32f4x7_eth.c为了追踪Rx/Tx DMA描述符还定义了两个全局指针变量:
__IO ETH_DMADESCTypeDef *DMATxDescToSet;
__IO ETH_DMADESCTypeDef *DMARxDescToGet;
这两个指针变量用于指向下一个要发送或者接受的数据包,图示:
2. 以太网帧结构
我们知道TCP/IP协议采用分层结构,其分层模型及协议如下表:
应用层 | HTTP、Telnet、FTP、SMTP、SNMP |
---|---|
传输层 | TCP、UDP |
网间网层 | IP[ARP、RARP、ICMP] |
网络接口层 | Ethernet、X.25、SLIP、PPP |
任何通讯协议都有独特的报文格式,TCP/IP协议也不例外。对于通讯协议编程,我们首先要清楚其报文格式。由于TCP/IP协议采用分层模型,各层都有专用的报头,以下就简单介绍以太网下TCP/IP各层报文格式。
以太网是目前最流行的一种局域网组网技术,其正式标准是IEEE 802.3标准,它规定了在以太网中传输的数据帧结构,其结构如下所示:
前同步码 | SFD | 目的地址 | 源地址 | 长度/类型 | 数据和填充 | CRC |
---|---|---|---|---|---|---|
7字节 | 1字节 | 6字节 | 6字节 | 2字节 | 46字节~1500字节 | 4字节 |
8字节的前导用于帧同步,CRC域用于帧校验。这些用户不必关心其由网卡芯片自动添加。目的地址和源地址是指网卡的物理地址,即MAC地址,具有唯一性。帧类型或协议类型是指数据包的高级协议,如 0x0806表示ARP协议,0x0800表示IP协议等
-
在物理层上看,一个完整的以太网帧有7个字段,事实上,前两个字段并不能算是真正意义的以太网数据帧,他们是网卡在物理层上的发送以太网数据时添加上去的,为了实现底层数据的数据传输,物理层使用7个字节前同步码(0和1交替的56位)实现物理层帧输入/输出的同步;使用1个字节SFD(帧首定界符,固定10101011)标识帧的开始。
-
剩余5个字段是真正的以太网数据,其中包含了目的地址和源地址,它们都是6字节长度(MAC地址长度通常为6),因为MAC能在网络上唯一的标识自己,网卡接收数据时,通常将目的地址字段和自身MAC地址比较,判断是否接收该数据包。
-
长度/类型字段具有两个意义,当这两个字节的值小于1518时,那么它代表了其后数据字段的长度;如果大于1518,则表示该以太网帧中的数据属于哪个上层协议(IP数据包:0x800, ARP数据包:0x806)。
-
数据和填充表示该以太网帧携带的上层协议数据,它们的最小长度为46字节,最大长度位1500字节。
-
CRC字段值包含了该以太网帧的错插脚验信息。
以太网帧又可细分,如图:
3. ARP数据报
ARR协议的基本功能是使用目标主机的IP地址,查询其对应的MAC地址,以保证底层链路上数据包通信的进行。
ARP数据包分为ARP请求包和应答包,ARP请求包通过以太网广播的方式发送,用于向具有某个IP地址的主机发送请求,希望该主机返回其MAC地址;ARP应答包,收到ARP请求的主机会对比该数据包中的IP地址与自己的IP是否符合,若是,则该主机向源主机返回一个ARP应答包,向源主机报告自己的MAC地址。源主机通过提取ARP应答包中的相关字段来更新ARP缓存表。ARP数据包经典的组成结构如下:
在LWIP中的etharp.h中用了一大堆的数据结构和宏来描述:
PACK_STRUCT_BEGIN
/** the ARP message, see RFC 826 ("Packet format") */
struct etharp_hdr {
PACK_STRUCT_FIELD(u16_t hwtype); //硬件类型(2个字节)
PACK_STRUCT_FIELD(u16_t proto); //协议类型(2个字节)
PACK_STRUCT_FIELD(u8_t hwlen); //硬件地址长度(1个字节)
PACK_STRUCT_FIELD(u8_t protolen); //协议地址长度(1个字节)
PACK_STRUCT_FIELD(u16_t opcode); //操作字段op(2字节)
PACK_STRUCT_FIELD(struct eth_addr shwaddr); //发送方MAC地址(6字节)
PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); //发送方IP地址(4字节)
PACK_STRUCT_FIELD(struct eth_addr dhwaddr); //接收方MAC地址(6字节)
PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); //接收方IP地址(4字节)
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
4. IP数据报
IP数据报和ARP报文都是组装在以太网数据帧中发送的。IP数据报有自己的组织格式,它分为IP首部和数据,如图所示:
首部可以有20~60字节,可以包含40在细节的选项字段,但通常没有,银子常见的IP首部长度位20个字节。数据包规定首部的格式,却没有规定其后的数据的格式,所以P数据报可以用来运输任意类型的数据。
5. ICMP报文
ICMP协议用于在IP主机、路由器之间传递控制消息,这里的控制消息可以包括很多种,例如报错误信息、网络状况信息、主机状况信息等,这些控制消息虽然不传输用户数据,但是对于用户数据报的有效递交起着重要作用。
ICMP报文时使用IP数据报来封装和发送的,携带ICMP报文的IP数据报完全向携带其他类型的数据报那样在网络中被转发,没有额外的可靠性和优先性。IP数据报时封装在底层物理数据帧中发送的,这样ICMP报文会经历两次封装过程,如图:
ICMP使用IP进行交互,是因为一个报文可能要经过几个物理网络才能到达其最终目的地,因此它不可能单独地通过某个物理传输进行交付,必须使用IP提供的交付服务,屏蔽掉各种底层物理结构的差异。
ICMP的报文有8个字节首部和可变长度的数据部分组成。
- 对于不同类型的ICMP报文,首部的格式也会出现一定的差异,但是首部前4个字节的字段对所有类型都是通用的。首部用一个结构体描述,如下:
PACK_STRUCT_BEGIN
struct icmp_echo_hdr {
PACK_STRUCT_FIELD(u8_t type);//标识该ICMP报文的具体类型
PACK_STRUCT_FIELD(u8_t code);//进一步指出产生这种类型的ICMP的原因
PACK_STRUCT_FIELD(u16_t chksum);//校验
PACK_STRUCT_FIELD(u16_t id);
PACK_STRUCT_FIELD(u16_t seqno);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
-
不同类型的报文,数据部分的长度和含义也存在着差异。总的来说,差错报告报文的数据部分携带了引起差错的IP数据报信息;查询报文的数据部分懈怠了查询请求或查询结果相关的数据。
ICMP报文格式如图:
6. UDP数据报
UDP协议在IP协议上增加了复用、分用和差错检测功能。UDP的特点:
-
是无连接的。相比于TCP协议,UDP协议在传送数据前不需要建立连接,当然也就没有释放连接。
是尽最大努力交付的。也就是说UDP协议无法保证数据能够准确的交付到目的主机。也不需要对接收到的UDP报文进行确认。
-
是面向报文的。也就是说UDP协议将应用层传输下来的数据封装在一个UDP包中,不进行拆分或合并。因此,运输层在收到对方的UDP包后,会去掉首部后,将数据原封不动的交给应用进程。
没有拥塞控制。因此UDP协议的发送速率不送网络的拥塞度影响。
5. UDP支持一对一、一对多、多对一和多对多的交互通信。
-
UDP协议分为首部字段和数据字段,其中首部字段只占用8个字节,分别是个占用两个字节的源端口、目的端口、长度和检验和。
长度:UDP报文的整个大小,最小为8个字节(仅为首部)。
检验和:在进行检验和计算时,会添加一个伪首部一起进行运算。伪首部(占用12个字节)为:4个字节的源IP地址、4个字节的目的IP地址、1个字节的0、一个字节的数字17、以及占用2个字节UDP长度。这个伪首部不是报文的真正首部,只是引入为了计算校验和。相对于IP协议的只计算首部,UDP检验和会把首部和数据一起进行校验。接收端进行的校验和与UDP报文中的校验和相与,如果无差错应该全为1。如果有误,则将报文丢弃或者发给应用层、并附上差错警告。
最后给出udp报文的封装格式:
7. TCP数据报
(1)TCP源端口(Source Port):16位的源端口包含初始化通信的端口号。源端口和IP地址的作用是标识报文的返回地址。
(2)TCP目的端口(Destination Port):16位的目的端口域定义传输的目的。这个端口指明报文接收计算机上的应用程序地址接口。
(3)序列号(Sequence Number):TCP连线发送方向接收方的封包顺序号。
(4)确认序号(Acknowledge Number):接收方回发的应答顺序号。
(5)头长度(Header Length):表示TCP头的双四字节数,如果转化为字节个数需要乘以4。
(6)URG:是否使用紧急指针,0为不使用,1为使用。
(7)ACK:请求/应答状态。0为请求,1为应答。
(8)PSH:以最快的速度传输数据。
(9)RST:连线复位,首先断开连接,然后重建。
(10)SYN:同步连线序号,用来建立连线。
(11)FIN:结束连线。如果FIN为0是结束连线请求,FIN为1表示结束连线。
(12)窗口大小(Window):目的机使用16位的域告诉源主机,它想收到的每个TCP数据段大小。
(13)校验和(Check Sum):这个校验和和IP的校验和有所不同,不仅对头数据进行校验还对封包内容校验。
(14)紧急指针(Urgent Pointer):当URG为1的时候才有效。TCP的紧急方式是发送紧急数据的一种方式。