(一)ARP之 数据包接收过程
?
? 先看一下整个数据流的传输过程。
-
首先etherneti_input()函数 从底层网卡驱动接收到原始数据,若是ip包或者ARP包则调用ethernet_input()。
s32_t ethernetif_input(struct netif *netif) { struct ethernetif *ethernetif; //网络接口信息结构体,此处无用。 struct eth_hdr *ethhdr; //以太网帧 头部结构体指针。 struct pbuf *p; ethernetif = netif->state; p = low_level_input(netif); //!!调用底层函数读取一个数据包。 if (p == NULL) { return 0; } ethhdr = p->payload; //将ethhdr指针指向数据包中以太网头部 switch (htons(ethhdr->type)) { //判断数据包中的帧类型 ,要大小端转换。 case ETHTYPE_IP: case ETHTYPE_ARP: /* full packet send to tcpip_thread to process */ if (netif->input(p, netif)!=ERR_OK) //调用netif->input 进行处理。 { pbuf_free(p); //释放Pbuf p = NULL; } break; default: pbuf_free(p); p = NULL; break; } return 1; }
可见,此函数未作实质性的处理,只是判断以太网中帧类型,并调用中netif->input函数指针处理,此处指向的就是 ethernet_input函数。我们来看看ethernet_input函数做了哪些事情。(不停地套娃~)
err_t ethernet_input(struct pbuf *p, struct netif *netif)
{
struct eth_hdr* ethhdr; //以太网帧头部结构体指针
u16_t type;
if (p->len <= SIZEOF_ETH_HDR) { //长度校验,ARP包必须包含在第一个PBUF的数据区。
goto free_and_return;
}
ethhdr = (struct eth_hdr *)p->payload; //以太网帧指针 指向以太网帧头部
type = ethhdr->type; //获取帧类型
switch (type) {
case PP_HTONS(ETHTYPE_IP):
#if ETHARP_TRUST_IP_MAC
/* update ARP table */
etharp_ip_input(netif, p); //使用IP头部以及 以太网头部MAC 更新 ARP表。
#endif /* ETHARP_TRUST_IP_MAC */
/* skip Ethernet header */
if(pbuf_header(p, -ip_hdr_offset)) { //去掉以太网头部
goto free_and_return; //若操作失败,则释放pbuf
} else {
/* pass to IP layer */
ip_input(p, netif); //若头部去除成功,则调用IP输入处理函数处理数据。
}
break;
case PP_HTONS(ETHTYPE_ARP): //若是ARP数据包
/* pass p to ARP module */
etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p); //调用ARP数据包处理函数。
break;
default:
goto free_and_return;
}
return ERR_OK;
free_and_return:
pbuf_free(p);
return ERR_OK;
}
? 这个函数也很简单,就是根据数据包的type 类型进行判断 是IP包还是ARP包?是IP包则去掉 **以太网帧头部**,调用 **ip_input** 函数处理,若是ARP包,则调用 **etharp_arp_input() **处理数据包。
那我们接下来看看数据包是怎么处理的呢?
(二)ARP之 数据包处理过程
从之前讲述的知识可以了解到 etharp_arp_input()函数有两个功能:
-
若接收到是ARP应答包,则需要根据应答信息更新ARP缓存表。
-
若接收到ARP请求包,则需要:
-
若这个请求包,与本机IP地址不符合,则不需要应答,但是要将请求包中的 IP和MAC加入到自己的缓存表中,以备到时候需要使用。
-
若这个请求包,与本机IP地址符号,除了要将源主机的IP与MAC加入缓存表之外,还要回复一个应答,告诉本机的MAC地址是多少。
-
好,有了上面的思路,我们具体来看一看代码是怎么实现的。
(1)etharp_arp_input()函数分析
细细品味~~
static void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
{
struct etharp_hdr *hdr; //ARP数据包包头部结构指针。
struct eth_hdr *ethhdr; //以太网头部结构体指针
ip_addr_t sipaddr, dipaddr;
u8_t for_us; //指示ARP包是否发送给本机的。
/* drop short ARP packets: we have to check for p->len instead of p->tot_len here
since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */
if (p->len < SIZEOF_ETHARP_PACKET) { //arp 数据包不能分装在两个PBUF中,不然不能用指针来操作内部了。
pbuf_free(p);
return;
}
ethhdr = (struct eth_hdr *)p->payload;
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR); //hdr 指向ARP数据包首部
/* RFC 826 "Packet Reception": */
if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) || //判断ARP数据包的合法型,要是不合法,则直接删除该数据包。
(hdr->hwlen != ETHARP_HWADDR_LEN) ||
(hdr->protolen != sizeof(ip_addr_t)) ||
(hdr->proto != PP_HTONS(ETHTYPE_IP))) {
pbuf_free(p);
return;
}
/* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
* structure packing (not using str