SylixOS 协议栈学习(二)以太网网卡接口部分初始化
在《SylixOS_协议栈学习(一)网络接口结构》中,介绍了netif 结构体和netdev结构体。
下面举个例子来看一个以太网网卡接口结构是怎样被初始化,还有数据包是如何接收和发送的。
- static struct netdev_funcs net_drv = { ①
- .init = __enetCoreInit,
- .transmit = __enetCoreTx,
- .receive = __enetCoreRecv,
- };
- pNetDev->speed = 0; ②
- pNetDev->mtu = 1500;
- pNetDev->hwaddr_len = 6;
- pNetDev->drv =&net_drv;
- lib_strcpy(ip, "192.168.3.202"); ③
- lib_strcpy(gw, "192.168.3.1");
- lib_strcpy(netmask, "255.255.255.0");
- netdev_add(pNetDev, ip, netmask, gw, IFF_UP |IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST) == 0) ; ④
①声明了网卡驱动函数,分别是以太网初始化函数,以太网发送函数,以太网接收函数。
②声明了网卡驱动参数(省略了一部分)。
③声明了三个分别用于暂存 IP 地址、子网掩码和网关地址的变量。
④网卡驱动程序调用netdev_add函数来增加一个网络接口。
netdev_add函数源码:
- int netdev_add (netdev_t *netdev, const char *ip, const char *netmask, const char *gw, int if_flags)
- {
- ......
- netif = (struct netif *)netdev->sys;
- lib_bzero(netif, sizeof(struct netif));
- ......
- if (netifapi_netif_add(netif, &ip4, &netmask4, &gw4, netdev, netdev_netif_init, tcpip_input)) {
- return (-1);
- }
- ......
- if (netdev->init_flags & NETDEV_INIT_AS_DEFAULT) {
- netifapi_netif_set_default(netif);
- }
- }
netdev_add函数其实是netif_add的封装。最终还是通过调用netif_add函数来增加一个网络接口。(其中netdev->sys 是预留的用于存储netif结构体的成员变量),其中netdev_netif_init 是用户自己定义的底层接口初始化函数, tcpip_input 函数是向 IP 层递交数据包的函数。
netif_add函数源码:
- struct netif *
- netif_add(struct netif *netif,
- const ip4_addr_t *ipaddr, const ip4_addr_t *netmask, const ip4_addr_t *gw,
- void *state, netif_init_fn init, netif_input_fn input)
- {
- ......
- netif->state = state; //指向用户关心的信息,这里为 NULL
- netif->num = netifnum++; //设置 num 字段,
- netif->input = input; // input 函数被赋值 ,(tcpip_input向 IP 层递交数据包的函数)
- ......
- netif_set_addr(netif, ipaddr, netmask, gw); //设置变量 enc28j60 的三个地址
- if (init(netif) != ERR_OK) { //用户自己的底层接口初始化函数(netdev_netif_init自己定义的底层接口初始化函数)
- return NULL;
- }
- netif->next = netif_list; //将初始化后的节点插入链表 netif_list
- netif_list = netif; // netif_list 指向链表头
- ......
- }
上面的初始化函数调用了用户自己定义的底层接口初始化函数,这里为netdev_netif_init函数,再来看看它的源代码:
- /* lwip netif add call back function */
- static err_t netdev_netif_init (struct netif *netif)
- {
- ......
- netif->output = etharp_output; //IP 层发送数据包函数
- ......
- netif->linkoutput = netdev_netif_linkoutput; //ARP 模块发送数据包函数
- ......
- }
至此,以太网网卡接口部分初始化结束。
在SylixOS中对LWIP进行了一次封装,最终使用的还是netif结构体和netif_add函数。
netif_add函数中填充了input、output、linkoutput等函数用于IP层从网卡上取得一个数据包、IP层向网卡发送一个数据包 、ARP 模块向网卡发送一个数据包。
以太网帧格式:
解析:
以太网目的地址:接收方设备的硬件地址(48bit,目的地址全为1的特殊地址是广播地址)。
以太网源地址:发送方的硬件地址
帧类型:表示后面数据的类型(其中,0x0806表示后面的数据是属于ARP包的,0x8035表示后面的数据属于RARP包,0x0800表示后面的数据是属于IP包)。
数据:(以太网帧数据要求最小为46字节,若小于该值,在不足的空间填充pad字节)。
CRC:用于帧内后续字节差错的循环冗余码检验(它也被称为FCS或帧检验序列)。
以太网帧传输最小长度要求64字节(6+6+2+46+4),在TCP/IP详解中提到的最小长度60字节包括以太网帧头但没包括以太网帧尾。
以太网帧传输最大长度要求1518字节(6+6+2+1500+4),最大值1500 称为以太网的最大传输单元(MTU),不同网络类型有不同MTU。
ARP报文仅28字节,所以通过以太网帧封装时,会填充18字节的PAD,达到数据段46字节的最小要求。
Related Articles
SylixOS 协议栈学习(三)ICMP 处理
ICMP(Internet control message protocol)是网络控制报文协议,用于在IP主机跟路由器之间传输控制信息的。 控制信息指网络不通、主机是否可达、路由是否有用等。 之前在将ip4_input时,涉及到三次ICMP的东西: 1、在转发过程中,如果TTL变为0,则要用icmp_time_exceeded函数向源主机发送一份超时ICMP信息。 2、会通过ip报头,判断是否是ICMP报文,如果是,则调用icmp_input函数。 ...
SylixOS 协议栈学习(四)ping 工作流程
Ping工作流程: 1、涉及协议介绍 运行ping程序时,会用到以下协议: ICMP(因特网控制报文协议):用于IP主机、路由器直接传递控制消息(一般是差错信息,如TCP/UDP传输失败,会构建ICMP报文返回)。 IP协议:一种不可靠、无连接的传输协议(目的是为了更好的提供传输服务)。(对于TCP传输,已十分可靠,不需要在IP层再链接;UDP本身就是不可靠、无连接的协议,使用IP协议能更快传输)。 ...
SylixOS使用的网络协议栈是?
Q:SylixOS使用的网络协议栈是? SylixOS使用的网络协议栈是 lwip 版本为 v2.1.0。 /** X.x.x: Major version of the stack */ #define LWIP_VERSION_MAJOR 2 /** x.X.x: Minor version of the stack */ #define LWIP_VERSION_MINOR 1 /** x.x.X: Revision of the stack */ #define ...
SylixOS 协议栈学习(一)网络接口结构
1) netif 的结构体来描述一个硬件网络接口的。 struct netif { struct netif *next; // 指向下一个 netif 结构的指针 struct ip_addr ip_addr; // IP 地址相关配置 struct ip_addr netmask; struct ...
【网络攻击】阿基里斯测试仪测试导致协议栈崩溃解决办法
问:【网络攻击】阿基里斯测试仪测试导致协议栈崩溃解决办法 测试方法: 发送 ip fragment 报文,10M 流量,测试报文一直没有最后一片的标志,就是相当于无限分片。 这些报文先把协议栈 pbuf 全部占满了,一直没有释放,在等最后一片或者超时。 所以在测试过程中,协议栈资源一直是满的,无法提供服务。 分析: ( 由源码可知,协议栈针对这个的超时时间应该是 15s ),如果支持无限分片,资源迟早会消耗光 答:修改IP分片