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


/** * 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);}

经验总结扩展阅读