话说,不得不佩服写这些代码的,真是太巧妙了.
#include "lwip/opt.h" #if LWIP_ARP || LWIP_ETHERNET #include "lwip/etharp.h" #include "lwip/stats.h" #include "lwip/snmp.h" #include "lwip/dhcp.h" #include "lwip/autoip.h" #include "netif/ethernet.h" #include <string.h> #if LWIP_IPV4 && LWIP_ARP /* 编译开关 */ /* 默认配置 ARP_MAXAGE = 300,就是5分钟. */ /* 在ARP过期前一分钟请求ARP,这样防止通信中的连接因为ARP表失效而错误. */ #define ARP_AGE_REREQUEST_USED_UNICAST (ARP_MAXAGE - 30) #define ARP_AGE_REREQUEST_USED_BROADCAST (ARP_MAXAGE - 15) /** 处于等待状态的ARP表项最大等待时间,函数1秒调用一次,但是因为状态有EREQUESTING_1和REREQUESTING_2两个,调用5次是10秒. */ #define ARP_MAXPENDING 5 /** ARP states */ enum etharp_state { ETHARP_STATE_EMPTY = 0, /* 表项为空 */ ETHARP_STATE_PENDING, /* 等待中的表项 */ ETHARP_STATE_STABLE, /* 这个MAC可以直接用 */ ETHARP_STATE_STABLE_REREQUESTING_1, /* 请求中 */ ETHARP_STATE_STABLE_REREQUESTING_2 }; struct etharp_entry { struct pbuf *q; /* 如果携带数据包,这个就不是NULL. */ ip4_addr_t ipaddr; /* 对应的IP地址 */ struct netif *netif; /* 网络接口 */ struct eth_addr ethaddr; /* 对应的MAC地址 */ u16_t ctime; /* ARP表存在时间 */ u8_t state;/* ARP表状态,比如EMPTY,STABLE. */ }; static struct etharp_entry arp_table[ARP_TABLE_SIZE]; #if !LWIP_NETIF_HWADDRHINT static u8_t etharp_cached_entry; #endif /* !LWIP_NETIF_HWADDRHINT */ /** Try hard to create a new entry - we want the IP address to appear in the cache (even if this means removing an active entry or so). */ #define ETHARP_FLAG_TRY_HARD 1 #define ETHARP_FLAG_FIND_ONLY 2 #define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint)) static err_t etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr); 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 ip4_addr_t *ipsrc_addr, const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr, const u16_t opcode); /** Compatibility define: free the queued pbuf */ #define free_etharp_q(q) pbuf_free(q) /** Clean up ARP table entries */ static void etharp_free_entry(int i) { /* 如果这个entry是有待发数据的. */ if (arp_table[i].q != NULL) { /* 删除待发数据 */ free_etharp_q(arp_table[i].q); /* 把待发数据清空.其实就是pbuf_free,是个宏定义. */ arp_table[i].q = NULL; } /* 设置成EMPTY */ arp_table[i].state = ETHARP_STATE_EMPTY; } /** * 定时器的作用其实就是清理过期的ARP表,这个是1秒请求一次的. */ void etharp_tmr(void) { u8_t i; /* 遍历所有条目 */ for (i = 0; i < ARP_TABLE_SIZE; ++i) { u8_t state = arp_table[i].state; /* 每次都取出状态 */ if (state != ETHARP_STATE_EMPTY ) { arp_table[i].ctime++; /* 只要不是EMPTY项目,ctime就会增加,这个就是ARP表的存在时间. */ if ((arp_table[i].ctime >= ARP_MAXAGE) || /* 条件1:如果他存在太长时间,就是太老了,回收. */ ((arp_table[i].state == ETHARP_STATE_PENDING) && /* 条件2:如果他等待询问MAC,但是一直还没去询问,也得回收. */ (arp_table[i].ctime >= ARP_MAXPENDING))) { etharp_free_entry(i); /* 就是删除待发数据,设置EMPTY属性 */ } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_1) { /* 已经发出了查询信息. */ arp_table[i].state = ETHARP_STATE_STABLE_REREQUESTING_2; } else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING_2) { /* 返回到稳定状态.稳定状态的条目,会在下一个适当的时刻重新进入查询. */ arp_table[i].state = ETHARP_STATE_STABLE; } else if (arp_table[i].state == ETHARP_STATE_PENDING) { /* 他等待询问MAC,但是时间没超过等待时间,发送查询信息. */ etharp_request(arp_table[i].netif, &arp_table[i].ipaddr); } } } } /* 这个函数用查找一个IP信息.(或者查找存在的IP信息) 第一个参数就是指定IP. 第二个参数:ETHARP_FLAG_FIND_ONLY 就只在缓存找,找不到就返回错,意思就是FIND ONLY,只查找,ETHARP_FLAG_TRY_HARD会在找不到时候,删除最没用的一个,删掉. 第三个参数:单网卡没所谓. */ static s8_t etharp_find_entry(const ip4_addr_t *ipaddr, u8_t flags, struct netif* netif) { s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE; s8_t empty = ARP_TABLE_SIZE; u8_t i = 0; s8_t old_queue = ARP_TABLE_SIZE; u16_t age_queue = 0, age_pending = 0, age_stable = 0; /* 记录old_queue,pending,stable表项的存在时间,如果要删除项,这个就有用了 */ /************************************************************ * 以下代码要完成三个工作: * a) 在缓存表中查找,并记录候选表项 * b) 选择候选表项 * c) 创建新表项 ************************************************************ * 单一查找,记录以下信息 * 1) 第一个空表项的位置(如果存在) * 2) 存在时间最长的stable表项 (如果存在) * 3) 存在时间最长且缓冲队列为空的pending表项 (如果存在) * 4) 存在时间最长且缓冲队列不为空的pending表项 (如果存在) * 5) 查找与IP匹配的表项,不管该表项是pending还是stable状态 * 直到以上5项工作都完成,或者遍历了整个缓存表 ************************************************************/ for (i = 0; i < ARP_TABLE_SIZE; ++i) { u8_t state = arp_table[i].state; /* 当发现空表项时进入,并记录空表项的位置,刚开始时候empty是ARP_TABLE_SIZE.只要empty一旦被改写,就不会再进入. */ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) { /* 记录第一个空表项的位置 */ empty = i; } else if (state != ETHARP_STATE_EMPTY) { /* 检查IP是否匹配,匹配的话就直接返回引索 */ if (ipaddr && ip4_addr_cmp(ipaddr, &arp_table[i].ipaddr) ) { /* 找到了,就返回编号.已经存在的. */ return i; } if (state == ETHARP_STATE_PENDING) { /* 等待中的条目. */ /* q不为空,就是发送队列来的. */ if (arp_table[i].q != NULL) { if (arp_table[i].ctime >= age_queue) { old_queue = i; /* 更新old_queue */ age_queue = arp_table[i].ctime; /* 更新age_queue */ } } else /* q是空,不是发送队列. */ { if (arp_table[i].ctime >= age_pending) { old_pending = i; age_pending = arp_table[i].ctime; /* 最长的包含数据的条目,为ETHARP_FLAG_TRY_HARD准备的,ETHARP_FLAG_TRY_HARD是要删除一个条目的. */ } } } else if (state >= ETHARP_STATE_STABLE) { /* 不稳定的条目 */ { if (arp_table[i].ctime >= age_stable) { old_stable = i; age_stable = arp_table[i].ctime; } } } } } /* 只是搜索这一步就完成了,他不会删除一个老的条目. */ if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) || /* ETHARP_FLAG_FIND_ONLY为1,执行这个.就是找不到匹配的. */ /* ETHARP_FLAG_TRY_HARD为1,后半条件永远为假.所以只有未找到empty,ETHARP_FLAG_TRY_HARD为0,这个条件也为真.不管ETHARP_FLAG_FIND_ONLY是否置位. */ /* 为什么有这个判断,为了冗余ETHARP_FLAG_STATIC_ENTRY这个FLAG吧,虽然没用到.不然只要第一个FLAG就够了. */ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) { return (s8_t)ERR_MEM; } /* 下面这个可以执行,一定是设置了ETHARP_FLAG_TRY_HARD = 1. */ /********************************************************** * 选择对系统破坏最小的表项进行回收 * 1) 空表项 * 2) 存在时间最长的stable表项 * 3) 存在时间最长且缓冲队列为空的pending表项 * 4) 存在时间最长且缓冲队列挂载了数据包的pending表项 * * 注:以下操作建立在ETHARP_TRY_HARD设置为硬性建表的基础上 **********************************************************/ /* 1) 是否能找到空的表项? */ if (empty < ARP_TABLE_SIZE) { i = empty; } else { /* 2) 挨个寻找删除 */ if (old_stable < ARP_TABLE_SIZE) { /* 先删除最长stable的项目,可能他最近不会再用的.*/ i = old_stable; /* 3) 存在时间最长且缓冲队列无数据包的pending表项,q没内容的. */ } else if (old_pending < ARP_TABLE_SIZE) { i = old_pending; /* 4) 存在时间最长且缓冲队列挂载了数据包的pending表项,就是q有内容. */ } else if (old_queue < ARP_TABLE_SIZE) { i = old_queue; /* 实在找不到可以删的了. */ } else { return (s8_t)ERR_MEM; } etharp_free_entry(i); /* 就是删除待发数据,设置EMPTY属性 */ } /* 是否给出了IP */ if (ipaddr != NULL) { /* i是空表项,把空表项的ip写入去.但是这个时候MAC还是没进去的.等时机去更新,但是这个时候表项是EMPTY. */ /* etharp_query中会遍历EMPTY的,然后判断是否需要去QUERY. */ ip4_addr_copy(arp_table[i].ipaddr, *ipaddr); } arp_table[i].ctime = 0; return (err_t)i; } /** * 更新ARP表(IP/MAC) * * @param 指定网络接口(然而单网卡没用) * @param 指定IP地址 * @param 指定MAC地址 * @param 指定标志 * * @return * - ERR_OK 成功更新 * - ERR_ARG 给的是广播地址,不用更新. * */ static err_t etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) { s8_t i; /* 判断是不是广播IP */ if (ip4_addr_isany(ipaddr) || /* ANY,大概就是0.0.0.0 */ ip4_addr_isbroadcast(ipaddr, netif) || /* 广播 */ ip4_addr_ismulticast(ipaddr)) { /* 多播 */ return ERR_ARG; } /* 查找表项,成功反向所在项索引. */ i = etharp_find_entry(ipaddr, flags, netif); /* 如果i小于0就是找不到. */ if (i < 0) { return (err_t)i; } { /* 因为MAC和IP都知道了,把这个表项设置成STABLE,表示有效,有MAC,也有IP,自然有效了. */ arp_table[i].state = ETHARP_STATE_STABLE; } /* 其实是多网卡用的,设置这个MAC属于哪个网卡. */ arp_table[i].netif = netif; /* 复制MAC地址(memcpy) */ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); /* 复位时间,现在这个ctime记录的是stable时间. */ arp_table[i].ctime = 0; /* 查看下这个描述有没有正常数据包还要发. */ if (arp_table[i].q != NULL) { struct pbuf *p = arp_table[i].q; arp_table[i].q = NULL; /* 有就发送数据包.从(struct eth_addr*)(netif->hwaddr) => ethaddr,是IP包.*/ ethernet_output(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr, ETHTYPE_IP); /* 然后释放内存. */ pbuf_free(p); } return ERR_OK; } /** * 删除所有ARP表,就是arp -d * */ void etharp_cleanup_netif(struct netif *netif) { u8_t i; for (i = 0; i < ARP_TABLE_SIZE; ++i) { u8_t state = arp_table[i].state; /* 遍历不是EMPTY的数据然后删除 */ if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) { etharp_free_entry(i); /* 就是删除待发数据,设置EMPTY属性 */ } } } /** ? * 从ARP表中查找正确的IP/MAC对,然后调用netif->linkoutput()发送,这个函数会自己添加以太网头(MAC + Type) ? * @param 指定网络接口(然而单网卡没用) ? * @param 指定IP地址 ? * @param 从ARP表中的MAC地址的返回 ? * @param 从ARP表中的IP地址的返回 ? * @return 如果从ARP中找得到,那么返回表索引,否则返回-1. ?*/ s8_t etharp_find_addr(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr **eth_ret, const ip4_addr_t **ip_ret) { s8_t i; i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY, netif); if ((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) { *eth_ret = &arp_table[i].ethaddr; *ip_ret = &arp_table[i].ipaddr; return i; } return -1; } /** * 获取一个ARP表项 * * @param 索引项 * @param IP地址返回 * @param 网络接口返回 * @param 网络地址返回 * @return 对应项如果有效,返回1,无效返回0,无效通常是项没稳定(不可信项). */ u8_t etharp_get_entry(u8_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret) { if((i < ARP_TABLE_SIZE) && (arp_table[i].state >= ETHARP_STATE_STABLE)) { *ipaddr = &arp_table[i].ipaddr; *netif = arp_table[i].netif; *eth_ret = &arp_table[i].ethaddr; return 1; } else { return 0; } } /** * 有IP包进来,p就是包的内容. * * @param p 就是包的内容 * @param netif 对应的网络接口 * */ void etharp_input(struct pbuf *p, struct netif *netif) { struct etharp_hdr *hdr; /* 这里填充源IP和目标IP的临时变量. */ ip4_addr_t sipaddr, dipaddr; u8_t for_us; hdr = (struct etharp_hdr *)p->payload; /* 之前已经从IP头判断确定为ARP数据,ARP头,如果ARP头不符合,那么就free这个包.必须是合理的ARP包,不是ARP包就是错. */ if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) || (hdr->hwlen != ETH_HWADDR_LEN) || (hdr->protolen != sizeof(ip4_addr_t)) || (hdr->proto != PP_HTONS(ETHTYPE_IP))) { pbuf_free(p); return; } /* 提取出源IP和目标IP. */ IPADDR2_COPY(&sipaddr, &hdr->sipaddr); IPADDR2_COPY(&dipaddr, &hdr->dipaddr); /* 看看对应的网络接口是否已经配置? */ if (ip4_addr_isany_val(*netif_ip4_addr(netif))) { for_us = 0; } else { /* 看看目标IP是不是发给自己的,如果是发给自己的,for_us就是1了.对比目标IP和自己的IP. */ for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif)); } /* ARP 是发给我们的,我们就把他记录下来.如果是给我们,就是特别重要,用ETHARP_FLAG_TRY_HARD. */ /* ARP 不是发给我们的,但是广播出来,我们就把他记录下来.如果不是给我们,就不是特别重要,用ETHARP_FLAG_FIND_ONLY. */ /* ETHARP_FLAG_TRY_HARD 就是删除旧项,也要塞进去,ETHARP_FLAG_FIND_ONLY没空位就算了. */ etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr), for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY); /* 针对OP Code来分离. */ switch (hdr->opcode) { /* ARP 请求? */ case PP_HTONS(ARP_REQUEST): /* 看看是不是问我们,如果是问我们,我们就要回复.否则忽略. */ if (for_us) { /* 回复我自己的MAC地址. */ etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &hdr->shwaddr, (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), &hdr->shwaddr, &sipaddr, ARP_REPLY); } break; /* ARP 回复? */ case PP_HTONS(ARP_REPLY): /* 之前已经通过etharp_update_arp_entry写入了表,下面只是给DHCP用的.(具体仅供参考) */ #if (LWIP_DHCP && DHCP_DOES_ARP_CHECK) /* DHCP希望知道来自哪个主机的ARP答复,同时IP地址也由DHCP服务器提供给我们,网络上不能有重复的IP地址. */ /* 抓包发现,路由器也经常问,Who has xxx.xxx.xxx.xxx,Tell Gateway. */ dhcp_arp_reply(netif, &sipaddr); #endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */ break; /* ARP不是回复就是请求. */ default: break; } /* 删除这个数据包,释放内存. */ pbuf_free(p); } /** 辅助函数:把pbuf的内容发到对应的目标,这个目标是arp_idx指定的. */ static err_t etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx) { /* 判断对应的ARP键是否稳定. */ if (arp_table[arp_idx].state == ETHARP_STATE_STABLE) { /* 下面判断ctime是否到超时呢,如果数据没什么问题,ctime会清零重新计数.(具体就是收到ARP回复,然后到etharp_update_arp_entry,然后清零ctime) */ if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_BROADCAST) { /* 还有30秒过期 */ /* 使用广播发出标准请求 */ if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) { /* 提交一个广播请求,其实和etharp_request_dst一样的,至少目标地址是广播(不知道具体MAC) */ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1; } } else if (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED_UNICAST) { /* 还有15秒过期 */ /* 间隔,和上一个发标准请求至少15秒.防止不必要的广播.但是这个不是广播,是一个特别的request,以太网头的目标MAC是填入的,应该是为了避免因为广播而导致网络问题,或者确定主机还或者,单独发给他去问ARP. */ if (etharp_request_dst(netif, &arp_table[arp_idx].ipaddr, &arp_table[arp_idx].ethaddr) == ERR_OK) { arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING_1; } } } /* 通过ethernet_output输出数据. */ return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP); } /** * 查找并填充以太网头,因为那里需要MAC地址.只要给IP地址,就能填好MAC,这就是ARP表最主要作用. * * 广播和多播跟这个无关的. * * @param 具体的网络接口 * @param 需要发的数据包 * @param IP地址 * * @return * - ERR_RTE 没法到达. */ err_t etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) { const struct eth_addr *dest; struct eth_addr mcastaddr; const ip4_addr_t *dst_addr = ipaddr; /* 广播和多播不用经过ARP. */ if (ip4_addr_isbroadcast(ipaddr, netif)) { dest = (const struct eth_addr *)ðbroadcast; } else if (ip4_addr_ismulticast(ipaddr)) { /* 多播的MAC地址.*/ mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0; mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1; mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2; mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; mcastaddr.addr[4] = ip4_addr3(ipaddr); mcastaddr.addr[5] = ip4_addr4(ipaddr); dest = &mcastaddr; } else { /* 正常情况 */ s8_t i; /* 看看是不是局域网地址,ARP不穿透局域网(二层),由网关帮我代办. */ if (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) && !ip4_addr_islinklocal(ipaddr)) { { /* 确实是广域网 */ { /* 是否有默认网关? */ if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) { /* 获得网关的IP地址 */ dst_addr = netif_ip4_gw(netif); } else { /* 网关找不到,无法到达. */ return ERR_RTE; } } } } /* 现在dst_addr已经确认,要不是网关地址,要不是局域网别人的IP. */ if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) && /* 查找上一次找过的索引,看看这个索引还能不能用. */ (ip4_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { /* 调用ethernet_output发送,指定索引号! */ return etharp_output_to_arp_index(netif, q, etharp_cached_entry); } /* 遍历查找所有的索引 */ for (i = 0; i < ARP_TABLE_SIZE; i++) { if ((arp_table[i].state >= ETHARP_STATE_STABLE) && (ip4_addr_cmp(dst_addr, &arp_table[i].ipaddr))) { /* 调用ethernet_output发送,指定索引号! */ ETHARP_SET_HINT(netif, i); /* 把索引号缓存起来,因为通常连续多次调用都是同一个索引. */ return etharp_output_to_arp_index(netif, q, i); } } /* 实在没有,进入查找流程了.(可能在询问的过程中,键就STABLE了.) */ return etharp_query(netif, dst_addr, q); } /* 广播/多播直接发送,不用经过ARP */ return ethernet_output(netif, q, (struct eth_addr*)(netif->hwaddr), dest, ETHTYPE_IP); } /** * 发送一个ARP查询请求 * * 如果IP地址不在ARP缓存表中,一个新的ARP表项会被创建,并设置为pending状态,然后一个请求解析此IP地址对应MAC地址的ARP请求包会被广播出去,需要发送的数据包挂载在此ARP表项的q队列指针上. * * 如果IP地址在ARP缓存表中,并且为pending状态,那么也会发送一个ARP请求广播包出去,并且挂载要发送的数据到缓存项的q队列指针上. * * 如果IP地址在ARP缓存表中,并且为stable状态,如果有数据包需要发送,则直接发送数据包,不需要发送ARP请求包.如果没有数据包要发送,则发送ARP请求包. * * @param 对应的网络接口 * @param IP地址已被解释 * @param q 如果没有,就是没数据要发.比如从etharp_output调用的,那么他就有数据的.从netif->output调用, * * @note q 只能是一个数据包,不能是数据包队列. * * @return * - ERR_BUF 没空间生成以太网头. * - ERR_MEM 内存错误,比如q有问题,硬件地址未知. * - ERR_RTE 无路由. * - ERR_ARG 广播/多播地址. * */ err_t etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q) { struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr; err_t result = ERR_MEM; int is_new_entry = 0; s8_t i; /* ARP表索引 */ /* 非广播/多播地址? */ if (ip4_addr_isbroadcast(ipaddr, netif) || ip4_addr_ismulticast(ipaddr) || ip4_addr_isany(ipaddr)) { return ERR_ARG; } /* 从缓存里面,先找一下,并且找不到,也要给返回一个可以EMPTY的项. */ i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif); /* 查找没出错,至少是删除出来一个空位? */ if (i < 0) { if (q) { } return (err_t)i; } /* 刚才果然是通过删除方法获得的. */ if (arp_table[i].state == ETHARP_STATE_EMPTY) { is_new_entry = 1; arp_table[i].state = ETHARP_STATE_PENDING; /* 记录下网络接口,等下要发询问包. */ arp_table[i].netif = netif; } /* 如果是一个新的项(刚才查询是EMPTY这个置位) 或者 数据不存在 */ if (is_new_entry || (q == NULL)) { /* 尝试发送一个查询 */ result = etharp_request(netif, ipaddr); if (result != ERR_OK) { /* ARP查询没发出. */ } if (q == NULL) { return result; /* 没数据就不用继续了,直接返回,因为ARP查询已经发送了.但是因为对应机器还没应答,其实还没STABLE. */ } } /* STABLE了没有?有可能在我执行到这句的时候,他就STABLE了.也有可能本身找到的就是STABLE的. */ if (arp_table[i].state >= ETHARP_STATE_STABLE) { /* 记录查找的缓存的索引. */ ETHARP_SET_HINT(netif, i); /* 这个时候MAC和IP都确定了,可以发送数据了. */ result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP); /* 等待中,比如刚才发的询问,还没人回复我. */ } else if (arp_table[i].state == ETHARP_STATE_PENDING) { /* 因为能执行到这里,表明是有数据要发送的.只能挂载了. */ struct pbuf *p; int copy_needed = 0; /* 只有PBUF_RAM的串联一起才能发送. */ p = q; while (p) { if (p->type != PBUF_ROM) { /* 只要不是PBUF_ROM的都要拷贝. */ copy_needed = 1; break; } p = p->next; } if (copy_needed) { /* 复制数据包之前要申请一个足够大的缓冲区. */ p = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM); if (p != NULL) { if (pbuf_copy(p, q) != ERR_OK) { pbuf_free(p); /* 复制不OK,就删除. */ p = NULL; } } } else { /* 标志p被参考,ref计数加1. */ /* 不用复制,p直接指向q. */ p = q; pbuf_ref(p); } if (p != NULL) { /* 新申请的东西有没有数据,或者原来的q有没有数据. */ /* 原来的arp项里有数据包,删掉,因为目前只给挂一个数据包. */ if (arp_table[i].q != NULL) { pbuf_free(arp_table[i].q); } arp_table[i].q = p;/* ARP查询后挂载的数据包是这个. */ result = ERR_OK; } else { result = ERR_MEM; /* 在这里竟然没有数据包,数据包因为copy的原因,没法成功,被NULL了. */ } } return result; } /** * 发送一个自制ARP包 * * @param 选择发送用的网络接口 * @param ethsrc_addr 以太网头的源MAC * @param ethdst_addr 以太网头的目标MAC * @param hwsrc_addr ARP头的源MAC * @param ipsrc_addr ARP头的源IP * @param hwdst_addr ARP头的目标MAC * @param ipdst_addr ARP头的目标IP * @param 操作码(查询还是回复) * @return ERR_OK 发送成功 * ERR_MEM 可能是没内存啊之类的错误. */ 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 ip4_addr_t *ipsrc_addr, const struct eth_addr *hwdst_addr, const ip4_addr_t *ipdst_addr, const u16_t opcode) { struct pbuf *p; err_t result = ERR_OK; struct etharp_hdr *hdr; /* 申请一块内存用于发包.ARP包的大小就是以太网头大小 + ARP头大小就没了,ARP自身不带数据. */ p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM); /* 申请不到就删除. */ if (p == NULL) { return ERR_MEM; } /* 结构体映射,填数据. */ hdr = (struct etharp_hdr *)p->payload; hdr->opcode = lwip_htons(opcode); /* 写ARP地址 */ ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr); ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr); /* 写IP地址 */ IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr); IPADDR2_COPY(&hdr->dipaddr, ipdst_addr); hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET); hdr->proto = PP_HTONS(ETHTYPE_IP); /* 写长度和协议 */ hdr->hwlen = ETH_HWADDR_LEN; hdr->protolen = sizeof(ip4_addr_t); /* 发送一个ARP查询 */ { ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP); } /* 释放缓冲区 */ pbuf_free(p); p = NULL; /* 什么,释放后就是NULL啊,这个是多余的吗? */ return result; } /** * 发送一个ARP请求,如果是广播就是字节都不知道具体. * * @param 具体的网络接口 * @param 需要询问的IP地址 * @param 目标地址,是广播还是特定. * @return ERR_OK 发送成功 * ERR_MEM 可能是没内存啊之类的错误. */ static err_t etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr* hw_dst_addr) { return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, hw_dst_addr, (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif), ðzero, ipaddr, ARP_REQUEST); } /** * 不知道具体MAC时候,需要提供一个广播. */ err_t etharp_request(struct netif *netif, const ip4_addr_t *ipaddr) { return etharp_request_dst(netif, ipaddr, ðbroadcast); } #endif /* LWIP_IPV4 && LWIP_ARP */ #endif /* LWIP_ARP || LWIP_ETHERNET */