LwIP之ARP协议

在网络层,源主机与目的主机之间是通过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, &ethbroadcast, (struct eth_addr *)netif->hwaddr, &netif->ip_addr, &ethzero, 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 *)&ethbroadcast;
  } 
  /* 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的处理流程

发布了208 篇原创文章 · 获赞 90 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/lushoumin/article/details/103033955