LwIP 代码分析(ARP代码中文注释) – 第十五集

/ 0评 / 1

话说,不得不佩服写这些代码的,真是太巧妙了.

#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), &ethzero,
                    ipaddr, ARP_REQUEST);
}
/**
 * 不知道具体MAC时候,需要提供一个广播.
 */
err_t
etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
{
  return etharp_request_dst(netif, ipaddr, &ethbroadcast);
}
#endif /* LWIP_IPV4 && LWIP_ARP */
#endif /* LWIP_ARP || LWIP_ETHERNET */

 

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注