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的處理流程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章