欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

LWIPARP协议(5数据包接收)

程序员文章站 2024-03-18 08:49:58
...

ARP数据包处理

以太网是有自己独立的寻址方式(MAC地址),而对于TCP/IP的上层协议(如TCP协议、IP协议),它们是以IP地址作为网络的标识,如果没有IP地址则无法进行收发数据。当数据通过网卡中接收回来的时候,LwIP内核就需要将数据进行分解,如果是IP数据报则递交给IP协议去处理,如果是ARP数据包则交由ARP协议去处理。
真正让LwIP内核去处理接收到的数据包是ethernet_input()函数。代码太多了,简单截取部分代码。

err_t
ethernet_input(struct pbuf *p, struct netif *netif)
{
  struct eth_hdr *ethhdr;
  u16_t type;

  LWIP_ASSERT_CORE_LOCKED();
  
  //校验数据长度
  if (p->len <= SIZEOF_ETH_HDR) {
    ETHARP_STATS_INC(etharp.proterr);
    ETHARP_STATS_INC(etharp.drop);
    MIB2_STATS_NETIF_INC(netif, ifinerrors);
    goto free_and_return;
  }

  if (p->if_idx == NETIF_NO_INDEX) {
    p->if_idx = netif_get_index(netif);
  }

  /* ethhdr指针指向以太网帧头部,并且强制转换成eth_hdr结构 */
  ethhdr = (struct eth_hdr *)p->payload;

  //获取类型
  type = ethhdr->type;

  if (ethhdr->dest.addr[0] & 1) 
  {
    /*  这可能是多播或广播数据包,如果目标IP地址的第一个字节的bit0是1,
		那么有可能是多播或者是广播数据包,所以,还需要进行判断,
		如果是多播的,就将pbuf标记为链路层多播。 */
    if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) {
      if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) &&
          (ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) 
	  {
       /* 将pbuf标记为链路层多播 */
        p->flags |= PBUF_FLAG_LLMCAST;
      }
    }
    else if (eth_addr_cmp(ðhdr->dest, ðbroadcast)) 
	{
      /* 将pbuf标记为链路层广播 */
      p->flags |= PBUF_FLAG_LLBCAST;
    }
  }

  switch (type) {
     /* 如果是IP数据报 */
    case PP_HTONS(ETHTYPE_IP):
      if (!(netif->flags & NETIF_FLAG_ETHARP)) {
        goto free_and_return;
      }
      /* 去掉太网首部 */
      if (pbuf_remove_header(p, next_hdr_offset)) 
	  {
        goto free_and_return;
      } 
	  else
	  {
        /* 递交到IP层处理 */
        ip4_input(p, netif);
      }
      break;

	//对于是ARP包
    case PP_HTONS(ETHTYPE_ARP):
      if (!(netif->flags & NETIF_FLAG_ETHARP)) 
	  {
        goto free_and_return;
      }
      /* 去掉太网首部 */
      if (pbuf_remove_header(p, next_hdr_offset)) 
	  {
        ETHARP_STATS_INC(etharp.lenerr);
        ETHARP_STATS_INC(etharp.drop);
        goto free_and_return;
      } 
	  else 
	  {
        /* 传递到ARP协议处理 */
        etharp_input(p, netif);
      }
      break;

    default:
      ETHARP_STATS_INC(etharp.proterr);
      ETHARP_STATS_INC(etharp.drop);
      MIB2_STATS_NETIF_INC(netif, ifinunknownprotos);
      goto free_and_return;
  }

  return ERR_OK;

free_and_return:
  pbuf_free(p);
  return ERR_OK;
}

ARP数据包的处理

重点来了,我们主要是讲解对收到的ARP数据包处理
ARP数据包的处理函数为etharp_input(),在这里它完成两个任务:

如果收到的是ARP应答包,说明本机之前发出的ARP请求包有了回应,就根据应答包更新自身的ARP缓存表;
如果收到的是ARP请求包,如果包中的目标IP地址与主机IP地址匹配,除了记录原主机的IP与MAC地址,更新自身的ARP表外,还要向源主机发送一个ARP应答包。但是如果如果包中目标IP地址与主机IP地址不匹配,则尽可能记录源主机的IP与MAC地址,更新自身的ARP表,并丢弃该请求包,为什么说是尽可能呢,因为主机的ARP缓存表是有限的,不可能记录太多的ARP表项,所以在有空闲的表项时才记录,如果没有空闲的表项,ARP觉得它自己已经尽力了,也记不住那么多表项。

void
etharp_input(struct pbuf *p, struct netif *netif)
{
  struct etharp_hdr *hdr;
  /* these are aligned properly, whereas the ARP header fields might not be */
  ip4_addr_t sipaddr, dipaddr;
  u8_t for_us;

  LWIP_ASSERT_CORE_LOCKED();

  LWIP_ERROR("netif != NULL", (netif != NULL), return;);

  hdr = (struct etharp_hdr *)p->payload;

  /* 判断ARP包的合法性,判断ARP包的合法性,已经类型是否为以太网、硬件地址长度是否为ETH_HWADDR_LEN、
     协议地址长度是否为sizeof(ip4_addr_t)以及协议是否为ARP协议,如果都满足则表示ARP包合法。 */
  if ((hdr->hwtype != PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET)) ||
      (hdr->hwlen != ETH_HWADDR_LEN) ||
      (hdr->protolen != sizeof(ip4_addr_t)) ||
      (hdr->proto != PP_HTONS(ETHTYPE_IP)))  {
    ETHARP_STATS_INC(etharp.proterr);
    ETHARP_STATS_INC(etharp.drop);
    pbuf_free(p);
    return;
  }
  ETHARP_STATS_INC(etharp.recv);


  //拷贝源IP地址与目标IP地址
  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
  IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);

  /* 看看主机网卡是否配置了IP地址 */
  if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
    for_us = 0;
  } 
  else 
  {
     /* 判断ARP数据包的目标IP地址与主机IP地址是否一样 */
    for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif));
  }

  /* 更新ARP缓存表项 */
  etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
                          for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);

 /* 更新完毕,根据包的类型处理 */
  switch (hdr->opcode) 
  {
    /* ARP请求包 */
    case PP_HTONS(ARP_REQUEST):
      if (for_us) {
         /* 是请求自己的,那就要做出应答 */
        etharp_raw(netif,
                   (struct eth_addr *)netif->hwaddr, &hdr->shwaddr,
                   (struct eth_addr *)netif->hwaddr, netif_ip4_addr(netif),
                   &hdr->shwaddr, &sipaddr,
                   ARP_REPLY);
				   
      } 
	   /* 不是给自己的,如果不是给自己的,原因有两种,一种是网卡自身尚未配置IP地址,这样子就只打印相关调试信息。
	      另一种是ARP包中的目标IP地址与主机IP地址不符合,也不用做出回应,直接丢弃即可,并输出相关调试信息*/
	  else if (ip4_addr_isany_val(*netif_ip4_addr(netif))) 
	  {
        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: we are unconfigured, ARP request ignored.\n"));
      }
	  else
	  {
        LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_input: ARP request was not for us.\n"));
      }
      break;
	  
	/* 对于ARP应答包 不用处理,前面已经更新ARP表项了*/
    case PP_HTONS(ARP_REPLY):
      break;
    default:
      ETHARP_STATS_INC(etharp.err);
      break;
  }
  pbuf_free(p);
}

更新ARP表项

etharp_update_arp_entry()函数是用于更新ARP缓存表的,它会在收到一个ARP数据包的时候被调用,它会先查找一个ARP表项,如果没有找到这个ARP表项的记录,就会去新建一个ARP表项,然后重置ARP表项的参数(状态、网卡。IP地址与对应的MAC地址以及生存时间等),然后检测ARP表项中是否挂载数据包,如果有就将这些数据包发送出去。
表项的更新方式,动态表项有两种方式,分别为ETHARP_FLAG_TRY_HARD和ETHARP_FLAG_FIND_ONLY。前者表示无论如何都要创建一个表项,如果ARP缓存表中没有空间了,那就需要回收较老的表项,将他们删除,然后建立新的表项。而如果是后者,就让内核尽量更新表项,如果ARP缓存表中没有空间了,那么也无能为力,实在是添加不了新的表项。

static err_t
etharp_update_arp_entry(struct netif *netif, const ip4_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
{
  s16_t i;
  if (ip4_addr_isany(ipaddr) ||
      ip4_addr_isbroadcast(ipaddr, netif) ||
      ip4_addr_ismulticast(ipaddr)) {
    return ERR_ARG;
  }
  
 /* 查找或者创建ARP表项,并且返回索引值 */
  i = etharp_find_entry(ipaddr, flags, netif);
  
  /* 如果索引值不合法,更新ARP表项失败 */
  if (i < 0) {
    return (err_t)i;
  }
  
  /* 设置表项状态为ETHARP_STATE_STABLE */
  arp_table[i].state = ETHARP_STATE_STABLE;

  /* 记录网卡 */
  arp_table[i].netif = netif;
  
  /* 插入ARP索引树 */
  mib2_add_arp_entry(netif, &arp_table[i].ipaddr);

  LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", i));
  
  /* 更新缓存表中的MAC地址 */
  SMEMCPY(&arp_table[i].ethaddr, ethaddr, ETH_HWADDR_LEN);
  
  /* 重置生存时间 */
  arp_table[i].ctime = 0;
  
  /* 如果表项上与未发送的数据包,那就将这些数据包发送出去 */
#if ARP_QUEUEING		//使用队列方式
  while (arp_table[i].q != NULL) 
  {
    struct pbuf *p;
    /* 定义q指向ARP表项中的数据包缓存队列 */
    struct etharp_q_entry *q = arp_table[i].q;
	
    /* 指向下一个数据包节点 */
    arp_table[i].q = q->next;
	
    /* 获取pbuf数据包 */
    p = q->p;
	
    /* 释放MEMP_ARP_QUEUE类型的内存块 */
    memp_free(MEMP_ARP_QUEUE, q);
#else 
  if (arp_table[i].q != NULL) {
    struct pbuf *p = arp_table[i].q;
    arp_table[i].q = NULL;
#endif 
     /* 发送缓存队列的数据包 */
    ethernet_output(netif, p, (struct eth_addr *)(netif->hwaddr), ethaddr, ETHTYPE_IP);
    /* free the queued IP packet */
    pbuf_free(p);
  }
  return ERR_OK;
}
相关标签: 网络之旅 lwip