ARP数据包的处理函数为etharp _input(),在这里它完成两个任务:
1. 如果收到的是ARP应答包,说明本机之前发出的ARP请求包有了回应,就根据应答包更新自身的ARP缓存表;
2. 如果收到的是ARP请求包,如果包中的目标IP地址与主机IP地址匹配,除了记录原主机的IP与MAC地址,更新自身的ARP表外,还要向源主机发送一个ARP应答包。但是如果如果包中目标IP地址与主机IP地址不匹配,则尽可能记录源主机的IP与MAC地址,更新自身的ARP表,并丢弃该请求包,为什么说是尽可能呢,因为主机的ARP缓存表是有限的,不可能记录太多的ARP表项,所以在有空闲的表项时才记录,如果没有空闲的表项,ARP觉得它自己已经尽力了,也记不住那么多表项。
etharp _input()函数的源码具体见代码清单 10 9。
```
1 void
2 etharp_input(struct pbuf *p, struct netif *netif)
3 {
4 struct etharp_hdr *hdr;
5
6 ip4_addr_t sipaddr, dipaddr;
7 u8_t for_us;
8
9 LWIP_ASSERT_CORE_LOCKED();
10
11 LWIP_ERROR("netif != NULL", (netif != NULL), return;);
12
13 hdr = (struct etharp_hdr *)p->payload;
14
15 /* 判断ARP包的合法性 */
16 if ((hdr->hwtype != PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET)) ||
17 (hdr->hwlen != ETH_HWADDR_LEN) ||
18 (hdr->protolen != sizeof(ip4_addr_t)) ||
19 (hdr->proto != PP_HTONS(ETHTYPE_IP))) (1)
20 {
21 LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
22 ("etharp_input: packet dropped, wrong hw type, hwlen, proto,
23 protolen or ethernet type(%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
24 hdr->hwtype,(u16_t)hdr->hwlen, hdr->proto, (u16_t)hdr->protolen));
25
26 ETHARP_STATS_INC(etharp.proterr);
27 ETHARP_STATS_INC(etharp.drop);
28
29 pbuf_free(p);
30 return;
31 }
32 ETHARP_STATS_INC(etharp.recv);
33
34 //拷贝源IP地址与目标IP地址
35 IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr); (2)
36 IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr); (3)
37
38 /* 看看主机网卡是否配置了IP地址 */
39 if (ip4_addr_isany_val(*netif_ip4_addr(netif))) (4)
40 {
41 for_us = 0;
42 }
43 else
44 {
45 /* 判断目标IP地址与主机IP地址是否一样 */
46 for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif)); (5)
47 }
48
49 /* 更新ARP缓存表 */
50 etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), (6)
51 for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
52
53 /* 更新完毕,根据包的类型处理 */
54 switch (hdr->opcode) (7)
55 {
56 /* ARP request? */
57 case PP_HTONS(ARP_REQUEST): (8)
58 /* ARP请求包 */
59 LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE,
60 ("etharp_input: incoming ARP request\n"));
61 /* 是请求自己的 */
62 if (for_us)
63 {
64 /* 做出回应 */
65 etharp_raw(netif,
66 (struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
67 (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
68 &hdr->shwaddr, &sipaddr,
69 ARP_REPLY); (9)
70 /* 不是给自己的 */
71 }
72 else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) (10)
73 {
74 LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
75 ("etharp_input: we are unconfigured, ARP request ignored.\n"));
76 }
77 else (11)
78 {
79 LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
80 ("etharp_input: ARP request was not for us.\n"));
81 }
82 break;
83 case PP_HTONS(ARP_REPLY): (12)
84 /* 对于ARP应答包*/
85 LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
86 ("etharp_input: incoming ARP reply\n"));
87 break;
88 default:
89 LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
90 ("etharp_input: ARP unknown opcode type %"S16_F"\n",
91 lwip_htons(hdr->opcode)));
92 ETHARP_STATS_INC(etharp.err); (13)
93 break;
94 }
95 /* 释放内存 */
96 pbuf_free(p); (14)
97 }
```
(1):判断ARP包的合法性,已经类型是否为以太网、硬件地址长度是否为ETH_HWADDR_LEN、协议地址长度是否为sizeof(ip4_addr_t)以及协议是否为ARP协议,如果都满足则表示ARP包合法。
(2):拷贝源IP地址到sipaddr变量中,因为在ARP包中的IP地址字段并不是对齐的,不能直接使用,所以需要拷贝到临时变量,方便直接操作。
(3):同理拷贝目标IP地址到dipaddr变量中。
(4):看看主机网卡是否配置了IP地址,如果没有配置,将for_us变量设置为0,表示不是给主机自己的ARP包。
(5):调用ip4_addr_cmp()函数判断目标IP地址与主机IP地址是否一样,如果一样则返回1,将for_us变量设置为1,反之设置为0。
(6):调用etharp_update_arp_entry()函数更新ARP缓存表,这个操作有点特殊,我们稍后讲解。
(7):更新完毕,根据包的类型处理,即根据ARP数据包的op字段进行处理。
(8):对于ARP请求包,首先要判断一下是否是给主机自己的,如果是则要回应,否则就直接丢弃即可。
(9):是请求自己的,调用etharp_raw()函数作出应答。
(10):如果不是给自己的,原因有两种,一种是网卡自身尚未配置IP地址,这样子就只打印相关调试信息。
(11):另一种是ARP包中的目标IP地址与主机IP地址不符合,也不用做出回应,直接丢弃即可,并输出相关调试信息。
(12):对于ARP应答包,理论上应该更新ARP缓存表的,毕竟发出去的ARP请求包得到回应,但是在前面已经更新了缓存表了,此处就不用重复更新了。
(13):对于其他情况,直接返回错误代码。
(14):释放内存。
- 说明
- 第1章:网络协议简介
- 1.1:常用网络协议
- 1.2:网络协议的分层模型
- 1.3:协议层报文间的封装与拆封
- 第2章:LwIP简介
- 2.1:LwIP的优缺点
- 2.2:LwIP的文件说明
- 2.2.1:如何获取LwIP源码文件
- 2.2.2:LwIP文件说明
- 2.3:查看LwIP的说明文档
- 2.4:使用vscode查看源码
- 2.4.1:查看文件中的符号列表(函数列表)
- 2.4.2:函数定义跳转
- 2.5:LwIP源码里的example
- 2.6:LwIP的三种编程接口
- 2.6.1:RAW/Callback API
- 2.6.2:NETCONN API
- 2.6.3:SOCKET API
- 第3章:开发平台介绍
- 3.1:以太网简介
- 3.1.1:PHY层
- 3.1.2:MAC子层
- 3.2:STM32的ETH外设
- 3.3:MII 和 RMII 接口
- 3.4:PHY:LAN8720A
- 3.5:硬件设计
- 3.6:软件设计
- 3.6.1:获取STM32的裸机工程模板
- 3.6.2:添加bsp_eth.c与bsp_eth.h
- 3.6.3:修改stm32f4xx_hal_conf.h文件
- 第4章:LwIP的网络接口管理
- 4.1:netif结构体
- 4.2:netif使用
- 4.3:与netif相关的底层函数
- 4.4:ethernetif.c文件内容
- 4.4.1:ethernetif数据结构
- 4.4.2:ethernetif_init()
- 4.4.3:low_level_init()
- 第5章:LwIP的内存管理
- 5.1:几种内存分配策略
- 5.1.1:固定大小的内存块
- 5.1.2:可变长度分配
- 5.2:动态内存池(POOL)
- 5.2.1:内存池的预处理
- 5.2.2:内存池的初始化
- 5.2.3:内存分配
- 5.2.4:内存释放
- 5.3:动态内存堆
- 5.3.1:内存堆的组织结构
- 5.3.2:内存堆初始化
- 5.3.3:内存分配
- 5.3.4:内存释放
- 5.4:使用C库的malloc和free来管理内存
- 5.5:LwIP中的配置
- 第6章:网络数据包
- 6.1:TCP/IP协议的分层思想
- 6.2:LwIP的线程模型
- 6.3:pbuf结构体说明
- 6.4:pbuf的类型
- 6.4.1:PBUF_RAM类型的pbuf
- 6.4.2:PBUF_POOL类型的pbuf
- 6.4.3:PBUF_ROM和PBUF_REF类型pbuf
- 6.5:pbuf_alloc()
- 6.6:pbuf_free()
- 6.7:其它pbuf操作函数
- 6.7.1:pbuf_realloc()
- 6.7.2:pbuf_header()
- 6.7.3:pbuf_take()
- 6.8:网卡中使用的pbuf
- 6.8.1:low_level_output()
- 6.8.2:low_level_input()
- 6.8.3:ethernetif_input()
- 第7章:无操作系统移植LwIP
- 7.1:将LwIP添加到裸机工程
- 7.2:移植头文件
- 7.3:移植网卡驱动
- 7.4:LwIP时基
- 7.5:协议栈初始化
- 7.6:获取数据包
- 7.6.1:查询方式
- 7.6.2:ping命令详解
- 7.6.3:中断方式
- 第8章:有操作系统移植LwIP
- 8.1:LwIP中添加操作系统
- 8.1.1:拷贝FreeRTOS源码到工程文件夹
- 8.1.2:添加FreeRTOS源码到工程组文件夹
- 8.1.3:指定FreeRTOS头文件的路径
- 8.1.4:修改stm32f10x_it.c
- 8.2:lwipopts.h文件需要加入的配置
- 8.3:sys_arch.c/h文件的编写
- 8.4:网卡底层的编写
- 8.5:协议栈初始化
- 8.6:移植后使用ping测试基本响应
- 第9章:LwIP一探究竟
- 9.1:网卡接收数据的流程
- 9.2:内核超时处理
- 9.2.1:sys_timeo结构体与超时链表
- 9.2.2:注册超时事件
- 9.2.3:超时检查
- 9.3:tcpip_thread线程
- 9.4:LwIP中的消息
- 9.4.1:消息结构
- 9.4.2:数据包消息
- 9.4.3:API消息
- 9.5:揭开LwIP神秘的面纱
- 第10章:ARP协议
- 10.1:链路层概述
- 10.2:MAC地址的基本概念
- 10.3:初识ARP
- 10.4:以太网帧结构
- 10.5:IP地址映射为物理地址
- 10.6:ARP缓存表
- 10.7:ARP缓存表的超时处理
- 10.8:ARP报文
- 10.9:发送ARP请求包
- 10.10:数据包接收流程
- 10.10.1:以太网之数据包接收
- 10.10.2:ARP数据包处理
- 10.10.3:更新ARP缓存表
- 10.11:数据包发送流程
- 10.11.1:etharp_output()函数
- 10.11.2:etharp_output_to_arp_index()函数
- 10.11.3:etharp_query()函数
- 第11章:IP协议
- 11.1:IP地址.md
- 11.1.1:概述
- 11.1.2:IP地址编址
- 11.1.3:特殊IP地址