LWIPARP协议(5数据包接收)
ARP数据包处理
以太网是有自己独立的寻址方式(MAC地址),而对于TCP/IP的上层协议(如TCP协议、IP协议),它们是以IP地址作为网络的标识,如果没有IP地址则无法进行收发数据。当数据通过网卡中接收回来的时候,LwIP内核就需要将数据进行分解,如果是IP数据报则递交给IP协议去处理,如果是ARP数据包则交由ARP协议去处理。
真正让LwIP内核去处理接收到的数据包是ethernet_input()函数。代码太多了,简单截取部分代码。
err_t
ethernet_input(struct pbuf *p, struct netif *netif)
{
struct eth_hdr *ethhdr;
u16_t type;
LWIP_ASSERT_CORE_LOCKED();
//校验数据长度
if (p->len <= SIZEOF_ETH_HDR) {
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
MIB2_STATS_NETIF_INC(netif, ifinerrors);
goto free_and_return;
}
if (p->if_idx == NETIF_NO_INDEX) {
p->if_idx = netif_get_index(netif);
}
/* ethhdr指针指向以太网帧头部,并且强制转换成eth_hdr结构 */
ethhdr = (struct eth_hdr *)p->payload;
//获取类型
type = ethhdr->type;
if (ethhdr->dest.addr[0] & 1)
{
/* 这可能是多播或广播数据包,如果目标IP地址的第一个字节的bit0是1,
那么有可能是多播或者是广播数据包,所以,还需要进行判断,
如果是多播的,就将pbuf标记为链路层多播。 */
if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) {
if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) &&
(ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2))
{
/* 将pbuf标记为链路层多播 */
p->flags |= PBUF_FLAG_LLMCAST;
}
}
else if (eth_addr_cmp(ðhdr->dest, ðbroadcast))
{
/* 将pbuf标记为链路层广播 */
p->flags |= PBUF_FLAG_LLBCAST;
}
}
switch (type) {
/* 如果是IP数据报 */
case PP_HTONS(ETHTYPE_IP):
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
goto free_and_return;
}
/* 去掉太网首部 */
if (pbuf_remove_header(p, next_hdr_offset))
{
goto free_and_return;
}
else
{
/* 递交到IP层处理 */
ip4_input(p, netif);
}
break;
//对于是ARP包
case PP_HTONS(ETHTYPE_ARP):
if (!(netif->flags & NETIF_FLAG_ETHARP))
{
goto free_and_return;
}
/* 去掉太网首部 */
if (pbuf_remove_header(p, next_hdr_offset))
{
ETHARP_STATS_INC(etharp.lenerr);
ETHARP_STATS_INC(etharp.drop);
goto free_and_return;
}
else
{
/* 传递到ARP协议处理 */
etharp_input(p, netif);
}
break;
default:
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
MIB2_STATS_NETIF_INC(netif, ifinunknownprotos);
goto free_and_return;
}
return ERR_OK;
free_and_return:
pbuf_free(p);
return ERR_OK;
}
ARP数据包的处理
重点来了,我们主要是讲解对收到的ARP数据包处理
ARP数据包的处理函数为etharp_input(),在这里它完成两个任务:
如果收到的是ARP应答包,说明本机之前发出的ARP请求包有了回应,就根据应答包更新自身的ARP缓存表;
如果收到的是ARP请求包,如果包中的目标IP地址与主机IP地址匹配,除了记录原主机的IP与MAC地址,更新自身的ARP表外,还要向源主机发送一个ARP应答包。但是如果如果包中目标IP地址与主机IP地址不匹配,则尽可能记录源主机的IP与MAC地址,更新自身的ARP表,并丢弃该请求包,为什么说是尽可能呢,因为主机的ARP缓存表是有限的,不可能记录太多的ARP表项,所以在有空闲的表项时才记录,如果没有空闲的表项,ARP觉得它自己已经尽力了,也记不住那么多表项。
void
etharp_input(struct pbuf *p, struct netif *netif)
{
struct etharp_hdr *hdr;
/* these are aligned properly, whereas the ARP header fields might not be */
ip4_addr_t sipaddr, dipaddr;
u8_t for_us;
LWIP_ASSERT_CORE_LOCKED();
LWIP_ERROR("netif != NULL", (netif != NULL), return;);
hdr = (struct etharp_hdr *)p->payload;
/* 判断ARP包的合法性,判断ARP包的合法性,已经类型是否为以太网、硬件地址长度是否为ETH_HWADDR_LEN、
协议地址长度是否为sizeof(ip4_addr_t)以及协议是否为ARP协议,如果都满足则表示ARP包合法。 */
if ((hdr->hwtype != PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET)) ||
(hdr->hwlen != ETH_HWADDR_LEN) ||
(hdr->protolen != sizeof(ip4_addr_t)) ||
(hdr->proto != PP_HTONS(ETHTYPE_IP))) {
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
pbuf_free(p);
return;
}
ETHARP_STATS_INC(etharp.recv);
//拷贝源IP地址与目标IP地址
IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
/* 看看主机网卡是否配置了IP地址 */
if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
for_us = 0;
}
else
{
/* 判断ARP数据包的目标IP地址与主机IP地址是否一样 */
for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif));
}
/* 更新ARP缓存表项 */
etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
/* 更新完毕,根据包的类型处理 */
switch (hdr->opcode)
{
/* ARP请求包 */
case PP_HTONS(ARP_REQUEST):
if (for_us) {
/* 是请求自己的,那就要做出应答 */
etharp_raw(netif,
(struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
(struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
&hdr->shwaddr, &sipaddr,
ARP_REPLY);
}
/* 不是给自己的,如果不是给自己的,原因有两种,一种是网卡自身尚未配置IP地址,这样子就只打印相关调试信息。
另一种是ARP包中的目标IP地址与主机IP地址不符合,也不用做出回应,直接丢弃即可,并输出相关调试信息*/
else if (ip4_addr_isany_val(*netif_ip4_addr(netif)))
{
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n"));
}
else
{
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n"));
}
break;
/* 对于ARP应答包 不用处理,前面已经更新ARP表项了*/
case PP_HTONS(ARP_REPLY):
break;
default:
ETHARP_STATS_INC(etharp.err);
break;
}
pbuf_free(p);
}
更新ARP表项
etharp_update_arp_entry()函数是用于更新ARP缓存表的,它会在收到一个ARP数据包的时候被调用,它会先查找一个ARP表项,如果没有找到这个ARP表项的记录,就会去新建一个ARP表项,然后重置ARP表项的参数(状态、网卡。IP地址与对应的MAC地址以及生存时间等),然后检测ARP表项中是否挂载数据包,如果有就将这些数据包发送出去。
表项的更新方式,动态表项有两种方式,分别为ETHARP_FLAG_TRY_HARD和ETHARP_FLAG_FIND_ONLY。前者表示无论如何都要创建一个表项,如果ARP缓存表中没有空间了,那就需要回收较老的表项,将他们删除,然后建立新的表项。而如果是后者,就让内核尽量更新表项,如果ARP缓存表中没有空间了,那么也无能为力,实在是添加不了新的表项。
static err_t
etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
{
s16_t i;
if (ip4_addr_isany(ipaddr) ||
ip4_addr_isbroadcast(ipaddr, netif) ||
ip4_addr_ismulticast(ipaddr)) {
return ERR_ARG;
}
/* 查找或者创建ARP表项,并且返回索引值 */
i = etharp_find_entry(ipaddr, flags, netif);
/* 如果索引值不合法,更新ARP表项失败 */
if (i < 0) {
return (err_t)i;
}
/* 设置表项状态为ETHARP_STATE_STABLE */
arp_table[i].state = ETHARP_STATE_STABLE;
/* 记录网卡 */
arp_table[i].netif = netif;
/* 插入ARP索引树 */
mib2_add_arp_entry(netif, &arp_table[i].ipaddr);
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", i));
/* 更新缓存表中的MAC地址 */
SMEMCPY(&arp_table[i].ethaddr, ethaddr, ETH_HWADDR_LEN);
/* 重置生存时间 */
arp_table[i].ctime = 0;
/* 如果表项上与未发送的数据包,那就将这些数据包发送出去 */
#if ARP_QUEUEING //使用队列方式
while (arp_table[i].q != NULL)
{
struct pbuf *p;
/* 定义q指向ARP表项中的数据包缓存队列 */
struct etharp_q_entry *q = arp_table[i].q;
/* 指向下一个数据包节点 */
arp_table[i].q = q->next;
/* 获取pbuf数据包 */
p = q->p;
/* 释放MEMP_ARP_QUEUE类型的内存块 */
memp_free(MEMP_ARP_QUEUE, q);
#else
if (arp_table[i].q != NULL) {
struct pbuf *p = arp_table[i].q;
arp_table[i].q = NULL;
#endif
/* 发送缓存队列的数据包 */
ethernet_output(netif, p, (struct eth_addr *)(netif->hwaddr), ethaddr, ETHTYPE_IP);
/* free the queued IP packet */
pbuf_free(p);
}
return ERR_OK;
}
上一篇: android.support迁移到androidx
下一篇: C++标准库 vector排序
推荐阅读