在网络层,源主机与目的主机之间是通过IP地址来唯一标识的。但是以太网是通过一个48bit的MAC地址来标识不同的网络通信设备的。那么IP数据包最终需要在物理网络上进行发送,就必须将IP地址转换为目标主机对应的MAC地址。
ARP协议被用来解决上述问题。为了实现在IP地址和MAC之间的转换,ARP协议引入了ARP缓存表的概念。ARP缓存表中存放了最近获得周围其他主机IP地址到MAC地址之间的映射记录。
系统初始化时,ARP缓存表是空的(静态绑定除外)。此时(调用netif_set_up时),会向外界广播一个自己的地址信息,称为无回报ARP请求。其他主机接收到ARP数据包之后,会更新ARP缓存表。
当主机A要与主机B通信时:
第1步:主机A在ARP缓存中,检查与主机B的IP地址相匹配的MAC地址。
第2步:如果主机A在ARP缓存中没有找到映射,它将在本地网络上广播ARP请求帧。本地网络上的每台主机都接收到ARP请求并且检查是否与自己的IP地址匹配。如果发现请求的IP地址与自己的IP地址不匹配,它将丢弃ARP请求。
第3步:主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射添加到本地ARP缓存中。
第4步:主机B将包含其MAC地址的ARP回复消息直接发送回主机A。
第5步:当主机A收到从主机B发来的ARP回复消息时,会用主机B的IP和MAC地址映射更新ARP缓存。主机B的MAC地址确定后,主机A就能通过IP地址和主机B通信了。
注:ARP缓存是有生存期的,一般为20分钟。生存期结束后,将再次重复上面的过程。
IP数据包从源主机到达最终目的主机的过程中,该IP数据包可能会经过中间物理网络中多种网络设备的转发,在每一次转发过程中都会涉及到地址转换的问题。在非最后一步转发中,当转发主机和目的主机不在同一个局域网中时,即便知道目的主机的MAC地址,两者也不能直接通信,必须经过路由转发才可以。所以此时,发送主机通过ARP协议获得的将不是目的主机的真实MAC地址,而是一台可以通往局域网外的路由器的MAC地址。在数据转发的最后一步,分组必将经过最后一条物理路线到达它的目的站,发送主机这时将目的主机IP地址映射为目标MAC地址。
ARP报文格式
/* 以太网头部 */
struct eth_hdr {
PACK_STRUCT_FIELD(struct eth_addr dest); //目的MAC地址
PACK_STRUCT_FIELD(struct eth_addr src); //源MAC地址
PACK_STRUCT_FIELD(u16_t type); //帧类型(IP:0x0800、ARP:0x0806)
} PACK_STRUCT_STRUCT;
/* ARP头部 */
struct etharp_hdr {
PACK_STRUCT_FIELD(u16_t hwtype); //硬件地址类型(以太网:1)
PACK_STRUCT_FIELD(u16_t proto); //映射协议地址类型(IP:0x0800)
PACK_STRUCT_FIELD(u16_t _hwlen_protolen); //硬件地址长度+协议地址长度
PACK_STRUCT_FIELD(u16_t opcode); //操作字段(ARP请求:1、ARP应答:2)
PACK_STRUCT_FIELD(struct eth_addr shwaddr); //源MAC地址
PACK_STRUCT_FIELD(struct ip_addr2 sipaddr); //源IP地址
PACK_STRUCT_FIELD(struct eth_addr dhwaddr); //目的MAC地址
PACK_STRUCT_FIELD(struct ip_addr2 dipaddr); //目的IP地址
} PACK_STRUCT_STRUCT;
/* 帧类型 */
#define ETHTYPE_ARP 0x0806 //ARP
#define ETHTYPE_IP 0x0800 //IP
#define ETHTYPE_VLAN 0x8100 //VLAN
#define ETHTYPE_PPPOEDISC 0x8863 //PPPOEDISC
#define ETHTYPE_PPPOE 0x8864 //PPPOE
/* ARP数据类型(操作字段OP) */
#define ARP_REQUEST 1 //ARP请求
#define ARP_REPLY 2 //ARP应答
前面说到网络接口启动的时候,要向外界发送一个无回报ARP请求,用来通知网络中的其它主机。在分析网络接口管理的时候遇到过,代码如下:
/* 使能网络接口 */
void netif_set_up(struct netif *netif)
{
/* 设置网络接口使能标志位 */
if (!(netif->flags & NETIF_FLAG_UP )) {
netif->flags |= NETIF_FLAG_UP;
/* 广播无回报ARP */
if (netif->flags & NETIF_FLAG_ETHARP) {
etharp_gratuitous(netif);
}
}
}
下面从ARP发送开始,一步一步分析无回报ARP请求是如何发送的
/* 组建并发送ARP(请求/响应)数据包 */
static err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr, const struct eth_addr *ethdst_addr, const struct eth_addr *hwsrc_addr, const struct ip_addr *ipsrc_addr, const struct eth_addr *hwdst_addr, const struct ip_addr *ipdst_addr, const u16_t opcode)
{
struct pbuf *p;
err_t result = ERR_OK;
u8_t k;
struct eth_hdr *ethhdr;
struct etharp_hdr *hdr;
/* 为ARP请求申请内存空间 */
p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
if (p == NULL) {
return ERR_MEM;
}
/* 以太网头部指针 */
ethhdr = p->payload;
/* ARP头部指针 */
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
/* 操作字段 */
hdr->opcode = htons(opcode);
/* 源MAC地址和目的MAC地址 */
k = ETHARP_HWADDR_LEN;
while(k > 0) {
k--;
hdr->shwaddr.addr[k] = hwsrc_addr->addr[k];
hdr->dhwaddr.addr[k] = hwdst_addr->addr[k];
ethhdr->dest.addr[k] = ethdst_addr->addr[k];
ethhdr->src.addr[k] = ethsrc_addr->addr[k];
}
/* 源IP地址、目的IP地址 */
hdr->sipaddr = *(struct ip_addr2 *)ipsrc_addr;
hdr->dipaddr = *(struct ip_addr2 *)ipdst_addr;
/* 硬件地址类型、协议地址类型 */
hdr->hwtype = htons(HWTYPE_ETHERNET);
hdr->proto = htons(ETHTYPE_IP);
/* 硬件地址长度、协议地址长度 */
hdr->_hwlen_protolen = htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr));
/* 帧类型 */
ethhdr->type = htons(ETHTYPE_ARP);
/* 发送数据包 */
result = netif->linkoutput(netif, p);
/* 释放数据包空间 */
pbuf_free(p);
p = NULL;
return result;
}
ARP请求,是通过调用 etharp_raw函数实现的。ARP头部中目的MAC地址全0,表示MAC地址待填充。
/* 广播MAC地址 */
const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
/* 待填充MAC地址 */
const struct eth_addr ethzero = {{0,0,0,0,0,0}};
/* 广播一个ARP请求 */
err_t etharp_request(struct netif *netif, struct ip_addr *ipaddr)
{
return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, ðbroadcast, (struct eth_addr *)netif->hwaddr, &netif->ip_addr, ðzero, ipaddr, ARP_REQUEST);
}
无回报ARP请求的原理是:将自身IP作为目的IP发送出去,这样就不会有任何主机响应,但是其它主机接收到后会更新ARP缓存表
/* 广播一个无回报ARP请求 */
#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr)
前面说到当主机发送数据包时,需要先查找ARP缓存表来获取目的主机MAC地址。下面来具体分析ARP缓存表的数据结构体,以及ARP缓存表的建立、查找和删除。
ARP表项数据结构
/* ARP表项 */
struct etharp_entry {
struct etharp_q_entry *q; //待发送数据包缓存链表
struct ip_addr ipaddr; //IP地址
struct eth_addr ethaddr; //MAC地址
enum etharp_state state; //ARP表项状态
u8_t ctime; //时间信息
struct netif *netif; //网络接口指针
};
/* ARP缓存表 */
static struct etharp_entry arp_table[ARP_TABLE_SIZE];
ARP缓存表项状态
/* ARP表项状态 */
enum etharp_state {
ETHARP_STATE_EMPTY = 0, //空
ETHARP_STATE_PENDING, //挂起,已发送ARP请求还未得到响应
ETHARP_STATE_STABLE //已建立
};
发送IP数据包之前,需要查ARP缓存表,如果在ARP缓存表中没有找到相应表项。则先发送ARP请求,并将数据包暂时缓存起来,得到ARP响应之后再发送。ARP提供了etharp_q_entry 结构体,用于管理这些数据包。
/* 未建立ARP表项之前,待发送IP数据包管理结构体 */
struct etharp_q_entry {
struct etharp_q_entry *next;
struct pbuf *p;
};
ARP缓存表项是有时限的,超过时限这将该ARP缓存表项删除。一般情况下,已经建立的表项为20分钟,处于挂起状态的表项为10秒钟。通过一个定时器回调函数etharp_tmr来进行计时处理。
/* 已建立表项寿命 (240 * 5) seconds = 20 minutes */
#define ARP_MAXAGE 240
/* 挂起表项寿命 (2 * 5) seconds = 10 seconds */
#define ARP_MAXPENDING 2
/* ARP定时器回调函数(周期5秒) */
void etharp_tmr(void)
{
u8_t i;
/* 遍历ARP缓存表 */
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
/* ARP缓存表时间加一 */
arp_table[i].ctime++;
/* 已建立表项和挂起表项超时 */
if (((arp_table[i].state == ETHARP_STATE_STABLE) && (arp_table[i].ctime >= ARP_MAXAGE)) ||
((arp_table[i].state == ETHARP_STATE_PENDING) && (arp_table[i].ctime >= ARP_MAXPENDING))) {
/* ARP表项待发送数据包缓存链表不为空 */
if (arp_table[i].q != NULL) {
/* 释放待发送数据包缓存链表 */
free_etharp_q(arp_table[i].q);
arp_table[i].q = NULL;
}
/* 设置ARP表项状态为空 */
arp_table[i].state = ETHARP_STATE_EMPTY;
}
}
}
/* 释放ARP表项待发送数据包缓存链表 */
static void free_etharp_q(struct etharp_q_entry *q)
{
struct etharp_q_entry *r;
/* 遍历待发送数据包缓存链表 */
while (q) {
r = q;
q = q->next;
/* 释放待发送数据包 */
pbuf_free(r->p);
/* 释放待发送数据包管理结构体 */
memp_free(MEMP_ARP_QUEUE, r);
}
}
ARP缓存表的建立和查找都是基于find_entry实现的。下面先从find_entry开始,一步一步分析
/* 匹配ARP缓存表时不允许回收表项 */
#define ETHARP_TRY_HARD 1
/* 匹配ARP缓存表时不建立新表项 */
#define ETHARP_FIND_ONLY 2
/* 通过IP地址查找ARP缓存表,如果不存在则按一定规则建立新表项 */
/* 建立新表项的规则:1.在空表项处 2.删除已建立的最老表项 3.删除挂起且没有缓存待发送数据包的最老表项 4.删除挂起且有缓存待发送数据包的最老表项 */
static s8_t find_entry(struct ip_addr *ipaddr, u8_t flags)
{
s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
s8_t empty = ARP_TABLE_SIZE;
u8_t i = 0, age_pending = 0, age_stable = 0;
s8_t old_queue = ARP_TABLE_SIZE;
u8_t age_queue = 0;
if (ipaddr) {
/* 最新一次访问的表项为已建立态 */
if (arp_table[etharp_cached_entry].state == ETHARP_STATE_STABLE) {
/* IP地址和表项IP地址匹配 */
if (ip_addr_cmp(ipaddr, &arp_table[etharp_cached_entry].ipaddr)) {
return etharp_cached_entry;
}
}
}
/* 遍历所有ARP表项,匹配到表项直接返回 */
for (i = 0; i < ARP_TABLE_SIZE; ++i) {
/* 记录第一个空表项下标 */
if ((empty == ARP_TABLE_SIZE) && (arp_table[i].state == ETHARP_STATE_EMPTY)) {
empty = i;
}
/* 该表项为挂起态 */
else if (arp_table[i].state == ETHARP_STATE_PENDING) {
/* IP地址和表项IP地址匹配 */
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
/* 将当前表项记录为最新一次访问表项 */
etharp_cached_entry = i;
return i;
}
/* 该表项待发送数据包缓冲区不为空 */
else if (arp_table[i].q != NULL) {
/* 记录最老的挂起态且待发送数据包缓冲区不为空的表项下标 */
if (arp_table[i].ctime >= age_queue) {
old_queue = i;
age_queue = arp_table[i].ctime;
}
}
/* 该表象待发送数据包缓冲区为空 */
else {
/* 记录最老的挂起态且待发送数据包缓冲区为空的表项下标 */
if (arp_table[i].ctime >= age_pending) {
old_pending = i;
age_pending = arp_table[i].ctime;
}
}
}
/* 该表项为已建立态 */
else if (arp_table[i].state == ETHARP_STATE_STABLE) {
/* IP地址和表项IP地址匹配 */
if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
/* 将当前表项记录为最新一次访问表项 */
etharp_cached_entry = i;
return i;
}
/* 记录最老的已建立态表项下标 */
else if (arp_table[i].ctime >= age_stable) {
old_stable = i;
age_stable = arp_table[i].ctime;
}
}
}
/* 该IP没有匹配到ARP表项,且没有空表项,且不允许删除老表项。或者不允许建立新表项 */
if (((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_TRY_HARD) == 0)) || ((flags & ETHARP_FIND_ONLY) != 0)) {
return (s8_t)ERR_MEM;
}
/* 存在空表项 */
if (empty < ARP_TABLE_SIZE) {
i = empty;
}
/* 存在已建立态的表项 */
else if (old_stable < ARP_TABLE_SIZE) {
i = old_stable;
}
/* 存在挂起态且待发送数据包缓冲区为空的表项 */
else if (old_pending < ARP_TABLE_SIZE) {
i = old_pending;
}
/* 存在挂起态且待发送数据包缓冲区不为空的表项 */
else if (old_queue < ARP_TABLE_SIZE) {
i = old_queue;
/* 释放待发送数据包缓存链表 */
free_etharp_q(arp_table[i].q);
arp_table[i].q = NULL;
}
/* 不存在可以删除的表项 */
else {
return (s8_t)ERR_MEM;
}
/* 删除该表项 */
arp_table[i].state = ETHARP_STATE_EMPTY;
/* 设置为新的表项 */
if (ipaddr != NULL) {
ip_addr_set(&arp_table[i].ipaddr, ipaddr);
}
arp_table[i].ctime = 0;
etharp_cached_entry = i; /* 将当前表项记录为最新一次访问表项 */
return (err_t)i;
}
更新(不存在则建立)ARP缓存表。当ARP缓存表从挂起转为建立的时候,需要发送原先缓存的待发送数据包。
/* 更新(不存在则建立)ARP缓存表 */
static err_t update_arp_entry(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr *ethaddr, u8_t flags)
{
s8_t i;
u8_t k;
/* 地址不能为广播地址、组播地址、不确定地址 */
if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr)) {
return ERR_ARG;
}
/* 通过IP地址查找ARP缓存表,如果不存在则按一定规则建立新表项 */
i = find_entry(ipaddr, flags);
/* 未找到且建立失败 */
if (i < 0)
return (err_t)i;
/* 设置该表项为已建立态 */
arp_table[i].state = ETHARP_STATE_STABLE;
/* 绑定网络接口 */
arp_table[i].netif = netif;
/* 绑定MAC地址 */
k = ETHARP_HWADDR_LEN;
while (k > 0) {
k--;
arp_table[i].ethaddr.addr[k] = ethaddr->addr[k];
}
/* 时间信息置0 */
arp_table[i].ctime = 0;
/* 遍历数据包缓冲区链表 */
while (arp_table[i].q != NULL) {
struct pbuf *p;
struct etharp_q_entry *q = arp_table[i].q;
arp_table[i].q = q->next;
p = q->p;
/* 释放待发送数据包管理结构体 */
memp_free(MEMP_ARP_QUEUE, q);
/* 发送数据 */
etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
/* 释放待发送数据包 */
pbuf_free(p);
}
return ERR_OK;
}
查找ARP缓存表,也是基于find_entry实现。
/* 查找ARP缓存表,如果不存在不建立新表项 */
s8_t etharp_find_addr(struct netif *netif, struct ip_addr *ipaddr, struct eth_addr **eth_ret, struct ip_addr **ip_ret)
{
s8_t i;
/* 查找ARP缓存表,如果不存在不建立新表项 */
i = find_entry(ipaddr, ETHARP_FIND_ONLY);
/* 如果该表项为已建立态 */
if((i >= 0) && arp_table[i].state == ETHARP_STATE_STABLE) {
/* 返回IP地址和MAC地址 */
*eth_ret = &arp_table[i].ethaddr;
*ip_ret = &arp_table[i].ipaddr;
/* 返回表项下标 */
return i;
}
return -1;
}
分析完ARP缓存表的查找之后,继续来分析IP数据包是怎么借用功能将数据发送出去的
/* 经过ARP功能填充头部,发送IP数据包 */
err_t etharp_output(struct netif *netif, struct pbuf *q, struct ip_addr *ipaddr)
{
struct eth_addr *dest, mcastaddr;
/* 向前调整出以太网头部空间 */
if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
return ERR_BUF;
}
/* 目的MAC地址置NULL */
dest = NULL;
/* IP地址是广播地址 */
if (ip_addr_isbroadcast(ipaddr, netif)) {
/* 设置目的MAC地址为广播MAC地址 */
dest = (struct eth_addr *)ðbroadcast;
}
/* IP地址是组播地址 */
else if (ip_addr_ismulticast(ipaddr)) {
/* 设置目的MAC地址为组播MAC地址 */
mcastaddr.addr[0] = 0x01;
mcastaddr.addr[1] = 0x00;
mcastaddr.addr[2] = 0x5e;
mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
mcastaddr.addr[4] = ip4_addr3(ipaddr);
mcastaddr.addr[5] = ip4_addr4(ipaddr);
dest = &mcastaddr;
}
/* IP地址为单播IP地址 */
else {
/* IP地址不在当前网段 */
if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) {
/* 网络接口网关地址不为0 */
if (netif->gw.addr != 0) {
/* 将目的IP地址改为网关IP地址 */
ipaddr = &(netif->gw);
}
/* 网关地址为0,返回错误 */
else {
return ERR_RTE;
}
}
/* 查找ARP缓存表,并发送IP数据包 */
return etharp_query(netif, ipaddr, q);
}
/* 广播或组播(已知目的MAC地址),发送IP数据包 */
return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
}
/* 已知目的MAC地址(查ARP表或MAC地址可推算(广播/组播)),发送IP数据包 */
static err_t etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
{
struct eth_hdr *ethhdr = p->payload;
u8_t k;
/* 设置以太网头部源MAC地址和目的MAC地址 */
k = ETHARP_HWADDR_LEN;
while(k > 0) {
k--;
ethhdr->dest.addr[k] = dst->addr[k];
ethhdr->src.addr[k] = src->addr[k];
}
/* 设置硬件地址类型 */
ethhdr->type = htons(ETHTYPE_IP);
/* 发送数据包 */
return netif->linkoutput(netif, p);
}
同样分析完ARP缓存表的更新之后,继续来分析收到数据包(IP数据包或ARP数据包)后的更新步骤
/* 以太网数据包输入处理 */
err_t ethernet_input(struct pbuf *p, struct netif *netif)
{
struct eth_hdr* ethhdr;
u16_t type;
/* 以太网头部指针 */
ethhdr = p->payload;
/* 帧类型 */
type = htons(ethhdr->type);
/* 判断数据包帧类型 */
switch (type) {
/* IP数据包 */
case ETHTYPE_IP:
/* 收到IP数据包,更新ARP缓存表 */
etharp_ip_input(netif, p);
/* 向后调整剥掉以太网头部 */
if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) {
pbuf_free(p);
p = NULL;
}
else {
/* IP数据包输入处理 */
ip_input(p, netif);
}
break;
/* ARP数据包 */
case ETHTYPE_ARP:
/* ARP数据包输入处理 */
etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
break;
default:
pbuf_free(p);
p = NULL;
break;
}
return ERR_OK;
}
/* 收到IP数据包,更新ARP缓存表 */
void etharp_ip_input(struct netif *netif, struct pbuf *p)
{
struct eth_hdr *ethhdr;
struct ip_hdr *iphdr;
/* 以太网头部指针 */
ethhdr = p->payload;
/* IP头部指针 */
iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
/* 该IP不在当前网段,直接返回 */
if (!ip_addr_netcmp(&(iphdr->src), &(netif->ip_addr), &(netif->netmask))) {
return;
}
/* 更新ARP缓存表 */
update_arp_entry(netif, &(iphdr->src), &(ethhdr->src), 0);
}
/* ARP数据包输入处理 */
void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
{
struct etharp_hdr *hdr;
struct eth_hdr *ethhdr;
struct ip_addr sipaddr, dipaddr;
u8_t i;
u8_t for_us;
/* ARP数据包长度过短 */
if (p->len < SIZEOF_ETHARP_PACKET) {
/* 释放该数据包 */
pbuf_free(p);
return;
}
/* 以太网头部 */
ethhdr = p->payload;
/* ARP头部 */
hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
/* 不符合ARP数据包格式 */
if ((hdr->hwtype != htons(HWTYPE_ETHERNET)) || (hdr->_hwlen_protolen != htons((ETHARP_HWADDR_LEN << 8) | sizeof(struct ip_addr))) || (hdr->proto != htons(ETHTYPE_IP)) || (ethhdr->type != htons(ETHTYPE_ARP))) {
/* 释放该数据包 */
pbuf_free(p);
return;
}
/* 取出源IP地址和目的IP地址 */
SMEMCPY(&sipaddr, &hdr->sipaddr, sizeof(sipaddr));
SMEMCPY(&dipaddr, &hdr->dipaddr, sizeof(dipaddr));
/* 该网络接口没有配置IP地址 */
if (netif->ip_addr.addr == 0) {
for_us = 0;
}
/* 判断该ARP数据是不是发给自己的 */
else {
for_us = ip_addr_cmp(&dipaddr, &(netif->ip_addr));
}
/* 该ARP数据是发给自己的 */
if (for_us) {
/* 更新ARP缓存表。如果不存在则建立新表项,但是如果表已满,则建立失败 */
update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), ETHARP_TRY_HARD);
} else {
/* 更新ARP缓存表。如果不存在则建立新表项,如果表已满,则按一定规则回收旧表项再建立新表项 */
update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), 0);
}
/* 判断数据包是ARP请求还是ARP响应 */
switch (htons(hdr->opcode)) {
/* ARP请求 */
case ARP_REQUEST:
/* 该ARP数据是发给自己的 */
if (for_us) {
/* 构建ARP响应包 */
hdr->opcode = htons(ARP_REPLY);
/* 目的IP */
hdr->dipaddr = hdr->sipaddr;
/* 源IP */
SMEMCPY(&hdr->sipaddr, &netif->ip_addr, sizeof(hdr->sipaddr));
/* 源MAC地址、目的MAC地址 */
i = ETHARP_HWADDR_LEN;
while(i > 0) {
i--;
hdr->dhwaddr.addr[i] = hdr->shwaddr.addr[i];
ethhdr->dest.addr[i] = hdr->shwaddr.addr[i];
hdr->shwaddr.addr[i] = ethaddr->addr[i];
ethhdr->src.addr[i] = ethaddr->addr[i];
}
/* 发送ARP响应 */
netif->linkoutput(netif, p);
}
/* 该网络接口没有配置IP */
else if (netif->ip_addr.addr == 0) {
}
/* 不是发给自己的 */
else {
}
break;
/* ARP响应 */
case ARP_REPLY:
break;
default:
break;
}
/* 释放ARP数据包 */
pbuf_free(p);
}
总结一下ARP的处理流程