【lwip】08-ARP协议一图笔记及源码实现( 四 )

  • arp entry新建:ARP缓存表中没有找到对应entry时,新建一个 。
  • 新建arp entry逻辑:
    调用etharp_find_entry()函数获取一个entry 。
    该函数的参数u8_t flags;表示申请arp entry资源的方式:
    • ETHARP_FLAG_TRY_HARD:允许覆盖已有arp entry 。
      • 在需要新建arp entry,且没有发现空闲的arp entry时 , 强制覆盖一条 。优先被覆盖的顺序:empty entry > oldest stable entry > oldest pending entry without queued packets > oldest pending entry with queued packets
    • ETHARP_FLAG_FIND_ONLY:只读模式 。如果ARP缓存表中没有匹配IP(和网卡)的arp entry,则返回失败,数据包处理终止 。
    8.5 ARP协议超时机制框图
    【lwip】08-ARP协议一图笔记及源码实现

    文章插图
    8.6 ARP收发报文数据流图
    【lwip】08-ARP协议一图笔记及源码实现

    文章插图
    8.7 ARP报文组包源码实现8.7.1 ARP报文数据结构/** the ARP message, see RFC 826 ("Packet format") */struct etharp_hdr {PACK_STRUCT_FIELD(u16_t hwtype); /* 硬件类型 */PACK_STRUCT_FIELD(u16_t proto); /* 协议类型 */PACK_STRUCT_FLD_8(u8_thwlen); /* 硬件地址长度 */PACK_STRUCT_FLD_8(u8_tprotolen); /* 协议地址长度 */PACK_STRUCT_FIELD(u16_t opcode); /* 操作字段 */PACK_STRUCT_FLD_S(struct eth_addr shwaddr); /* 源硬件地址 */PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned sipaddr); /* 源协议地址 */PACK_STRUCT_FLD_S(struct eth_addr dhwaddr); /* 目标硬件地址 */PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned dipaddr); /* 目标协议地址 */} PACK_STRUCT_STRUCT;8.7.2 ARP报文组建发送函数(基函数)ARP请求包是通过etharp_raw()函数进行组包和发送的,然后通过封装该函数得出不同需求的ARP请求函数供给上层使用 。
    /** * Send a raw ARP packet (opcode and all addresses can be modified) * * @param netif the lwip network interface on which to send the ARP packet * @param ethsrc_addr the source MAC address for the ethernet header * @param ethdst_addr the destination MAC address for the ethernet header * @param hwsrc_addr the source MAC address for the ARP protocol header * @param ipsrc_addr the source IP address for the ARP protocol header * @param hwdst_addr the destination MAC address for the ARP protocol header * @param ipdst_addr the destination IP address for the ARP protocol header * @param opcode the type of the ARP packet * @return ERR_OK if the ARP packet has been sent *ERR_MEM if the ARP packet couldn't be allocated *any other err_t on failure */static err_tetharp_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;LWIP_ASSERT("netif != NULL", netif != NULL);/* 链路层组包,为ARP报文申请内存资源 */p = pbuf_alloc(PBUF_LINK, SIZEOF_ETHARP_HDR, PBUF_RAM);if (p == NULL) { /* 内存资源申请失败 */LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,("etharp_raw: could not allocate pbuf for ARP request.\n"));ETHARP_STATS_INC(etharp.memerr);return ERR_MEM;}LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",(p->len >= SIZEOF_ETHARP_HDR));hdr = (struct etharp_hdr *)p->payload;LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));hdr->opcode = lwip_htons(opcode); /* 操作字段 */LWIP_ASSERT("netif->hwaddr_len must be the same as ETH_HWADDR_LEN for etharp!",(netif->hwaddr_len == ETH_HWADDR_LEN));SMEMCPY(&hdr->shwaddr, hwsrc_addr, ETH_HWADDR_LEN); /* 源MAC字段 */SMEMCPY(&hdr->dhwaddr, hwdst_addr, ETH_HWADDR_LEN); /* 目标MAC字段 */IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->sipaddr, ipsrc_addr); /* 源IP字段 */IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->dipaddr, ipdst_addr); /* 目标IP字段 */hdr->hwtype = PP_HTONS(LWIP_IANA_HWTYPE_ETHERNET); /* 硬件类型 */hdr->proto = PP_HTONS(ETHTYPE_IP); /* 协议类型 */hdr->hwlen = ETH_HWADDR_LEN; /* 硬件地址长度 */hdr->protolen = sizeof(ip4_addr_t); /* 协议地址长度 *//* 发送ARP包 */#if LWIP_AUTOIP/* 如果我们源IP为本地链路的IP,说明本ARP报文为ARP探测包,用于检查当前链路这个目标IP是否被占用了 。所以需要用以太网帧的目标MAC需要设置为广播MAC(参见RFC3927第2.5节最后一段)*/if (ip4_addr_islinklocal(ipsrc_addr)) {ethernet_output(netif, p, ethsrc_addr, &ethbroadcast, ETHTYPE_ARP); /* 发送以太网帧 */} else#endif /* LWIP_AUTOIP */{ /* 非ARP探测包,可按指定MAC封装以太网首部的目标MAC */ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP); /* 发送以太网帧 */}/* lwip状态记录 */ETHARP_STATS_INC(etharp.xmit);/* 释放当前ARP报文资源 */pbuf_free(p);p = NULL;return result;}

    推荐阅读