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

8.7.3 发送ARP请求包etharp_request_dst()

  • 发起ARP请求 。
  • 可指定目标MAC 。
/** * Send an ARP request packet asking for ipaddr to a specific eth address. * Used to send unicast request to refresh the ARP table just before an entry times out. * * @param netif the lwip network interface on which to send the request * @param ipaddr the IP address for which to ask * @param hw_dst_addr the ethernet address to send this packet to * @return ERR_OK if the request has been sent *ERR_MEM if the ARP packet couldn't be allocated *any other err_t on failure */static err_tetharp_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); /* ARP请求 */}etharp_request_dst()
  • 发起ARP标准请求包 。(广播包)
/** * Send an ARP request packet asking for ipaddr. * * @param netif the lwip network interface on which to send the request * @param ipaddr the IP address for which to ask * @return ERR_OK if the request has been sent *ERR_MEM if the ARP packet couldn't be allocated *any other err_t on failure */err_tetharp_request(struct netif *netif, const ip4_addr_t *ipaddr){LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));return etharp_request_dst(netif, ipaddr, &ethbroadcast);}8.7.4 发送ARP IP探测包etharp_acd_probe()
  • 发起ARP IP探测 。
  • 用于发送探测消息进行地址冲突检测 。
/** * Send an ARP request packet probing for an ipaddr. * Used to send probe messages for address conflict detection. * * @param netif the lwip network interface on which to send the request * @param ipaddr the IP address to probe * @return ERR_OK if the request has been sent *ERR_MEM if the ARP packet couldn't be allocated *any other err_t on failure */err_tetharp_acd_probe(struct netif *netif, const ip4_addr_t *ipaddr){return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,(struct eth_addr *)netif->hwaddr, IP4_ADDR_ANY4, &ethzero,ipaddr, ARP_REQUEST);}8.7.5 发送ARP IP宣告包etharp_acd_announce()
  • 发起ARP IP宣告 。
  • 用于发送地址冲突检测的公告消息 。
/** * Send an ARP request packet announcing an ipaddr. * Used to send announce messages for address conflict detection. * * @param netif the lwip network interface on which to send the request * @param ipaddr the IP address to announce * @return ERR_OK if the request has been sent *ERR_MEM if the ARP packet couldn't be allocated *any other err_t on failure */err_tetharp_acd_announce(struct netif *netif, const ip4_addr_t *ipaddr){return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,(struct eth_addr *)netif->hwaddr, ipaddr, &ethzero,ipaddr, ARP_REQUEST);}8.8 数据包发送分析8.8.1 数据发包处理简述(ARP相关)主要分析ARP协议层的处理 。
IP层数据包通过ip4_output()函数传递到ARP协议处理 。通过ARP协议获得目标IP主机的MAC地址才能封装以太网?。诹绰凡阕?。
etharp_output()收到IP层发来的数据包后按一下逻辑分支处理:
  • 对于广播或者多播的数据包:调用ethernet_output()函数直接把数据包丢给网卡即可 。
    • MAC的多播地址范围:01:00:5E:00:00:00 —— 01:00:5E:7F:FF:FF
  • 对于单播数据包:
    • 遍历ARP缓存表:遍历时 , 可以从当前网卡上次发送数据包使用的arp entry开始查起 , 找到就调用etharp_output_to_arp_index()把IP数据包转交给链路层转发 。
      • etharp_output_to_arp_index()概述里面会更新维护ARP缓存表当前arp entry 。
    • 发起ARP请求:如果缓存表中没有当前IP数据包目标IP映射的MAC地址,就需要调用etharp_query() , 把IP数据包转交给ARP协议处理 。
      • etharp_query()会发起ARP请求,在ARP请求过程中 , 把这些IP层的数据包保存到当前ARP条目的entry的挂起缓存队列中 。直到收到ARP响应或者ARP请求超时为止 。
对于PBUFF_ERFPBUF_POOLPBUF_RAM类型的数据包是不允许直接挂到ARP entry的挂起缓存队列上的,因为内核等待目标主机的ARP应答期间,这些数据有可能会被上层改动,所以LwIP需要将这些pbuf数据包拷贝到新的空间,等待发送 。
8.8.2 etharp_output():IP数据包是否ARP协议处理解析和填写以太网地址头为传出IP数据包 。
/** * Resolve and fill-in Ethernet address header for outgoing IP packet. * * For IP multicast and broadcast, corresponding Ethernet addresses are selected and the packet is transmitted on the link. * * For unicast addresses, the packet is submitted to etharp_query(). In case the IP address is outside the local network, the IP address of the gateway is used. * * @param netif The lwIP network interface which the IP packet will be sent on. * @param q The pbuf(s) containing the IP packet to be sent. * @param ipaddr The IP address of the packet destination. * * @return * - ERR_RTE No route to destination (no gateway to external networks), * or the return type of either etharp_query() or ethernet_output(). */err_tetharp_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;/* tcpip内核锁上锁检查 */LWIP_ASSERT_CORE_LOCKED();/* 参数校验 */LWIP_ASSERT("netif != NULL", netif != NULL);LWIP_ASSERT("q != NULL", q != NULL);LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);if (ip4_addr_isbroadcast(ipaddr, netif)) {/* 目标IP为广播地址 *//* 目标MAC也设置为广播地址:FF-FF-FF-FF-FF-FF-FF */dest = (const struct eth_addr *)&ethbroadcast;} else if (ip4_addr_ismulticast(ipaddr)) {/* 目标IP为多播地址 *//* 目标MAC也设置为多播地址:01:00:5E:00:00:00 —— 01:00:5E:7F:FF:FF */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 { /* 目标IP为单播地址 */netif_addr_idx_t i;/* 判断目标IP是否和原IP主机处于同一个子网上 , 如果不是,则修改要查找MAC的的IP为当前网卡的网关IP */if (!ip4_addr_net_eq(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&!ip4_addr_islinklocal(ipaddr)) { /* 不是同一个子网,也不是本地链路地址,需要把数据包转发到网关 */#if LWIP_AUTOIPstruct ip_hdr *iphdr = LWIP_ALIGNMENT_CAST(struct ip_hdr *, q->payload);/* 根据RFC 3297 2.6.2章(转发规则),如果IP地址为本地链路地址(169.254.0.0/16),这样的IP数据包是不能通过路由器转发的 */if (!ip4_addr_islinklocal(&iphdr->src)) /* 源IP地址不是本地链路地址 */#endif /* LWIP_AUTOIP */{#ifdef LWIP_HOOK_ETHARP_GET_GW/* 网关钩子函数,可以自定义选择网关 */dst_addr = LWIP_HOOK_ETHARP_GET_GW(netif, ipaddr);if (dst_addr == NULL)#endif /* LWIP_HOOK_ETHARP_GET_GW */{/* 查看网卡是否有默认网关 */if (!ip4_addr_isany_val(*netif_ip4_gw(netif))) {/* 获取网卡默认网关 */dst_addr = netif_ip4_gw(netif);} else { /* 没找到有效网关 *//* 没有路由到目的地错误(缺省网关丢失) */return ERR_RTE;}}}}#if LWIP_NETIF_HWADDRHINTif (netif->hints != NULL) {/* 网卡中上次发包时使用的arp entry优先遍历 */netif_addr_idx_t etharp_cached_entry = netif->hints->addr_hint;if (etharp_cached_entry < ARP_TABLE_SIZE) {#endif /* LWIP_NETIF_HWADDRHINT */if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) && /* arp entry有效 */#if ETHARP_TABLE_MATCH_NETIF(arp_table[etharp_cached_entry].netif == netif) && /* arp entry对应网卡匹配 */#endif(ip4_addr_eq(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) { /* 找到目标IP的MAC映射 */ETHARP_STATS_INC(etharp.cachehit); /* 记录lwip arp相关状态 *//* 发送IP数据包 */return etharp_output_to_arp_index(netif, q, etharp_cached_entry);}#if LWIP_NETIF_HWADDRHINT}}#endif /* LWIP_NETIF_HWADDRHINT *//* 如果网卡指定先遍历的arp entry没有找到合法映射,就需要遍历ARP缓存表 */for (i = 0; i < ARP_TABLE_SIZE; i++) {if ((arp_table[i].state >= ETHARP_STATE_STABLE) && /* arp entry有效 */#if ETHARP_TABLE_MATCH_NETIF(arp_table[i].netif == netif) && /* 网卡匹配对应 */#endif(ip4_addr_eq(dst_addr, &arp_table[i].ipaddr))) { /* 目标IP对应 *//* 找到目标IP的MAC映射 *//* 把当前arp entry索引保存到网卡中 , 以便下次快速遍历 */ETHARP_SET_ADDRHINT(netif, i);/* 发送IP数据包 */return etharp_output_to_arp_index(netif, q, i);}}/* 在ARP缓存表中没有找到对应的ARP entry,就需要发起ARP 请求 */return etharp_query(netif, dst_addr, q);}/* 对于广播或组播的数据包,直接知道了目标硬件字段的MAC地址,可以直接往链路层发送 */return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), dest, ETHTYPE_IP);}

推荐阅读