话说,不得不佩服写这些代码的,真是太巧妙了.
#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 */