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

8.8.3 etharp_output_to_arp_index():需要维护arp entry的IP数据包转发函数etharp_output_to_arp_index()这个函数实现维护arp entry超时前更新,然后把数据包通过ethernet_output()转交给链路层 。
其源码参考前面缓存表状态 。
8.8.4 etharp_query():需要发起ARP请求的IP数据包转发函数如果IP数据包为单播包,且在 ARP 缓存表中没有找到对应的MAC地址 , 就需要调用etharp_query()函数发起ARP请求处理 。
主要内容:

  • IP地址校验 。只能是有效的单播IP地址 。
  • 找到可操作的arp entry 。
  • 发起ARP请求 。
  • 拷贝pbuf保存到arp entry缓存队列 。
/** * Send an ARP request for the given IP address and/or queue a packet. * * If the IP address was not yet in the cache, a pending ARP cache entry * is added and an ARP request is sent for the given address. The packet * is queued on this entry. * * If the IP address was already pending in the cache, a new ARP request * is sent for the given address. The packet is queued on this entry. * * If the IP address was already stable in the cache, and a packet is * given, it is directly sent and no ARP request is sent out. * * If the IP address was already stable in the cache, and no packet is * given, an ARP request is sent out. * * @param netif The lwIP network interface on which ipaddr * must be queried for. * @param ipaddr The IP address to be resolved. * @param q If non-NULL, a pbuf that must be delivered to the IP address. * q is not freed by this function. * * @note q must only be ONE packet, not a packet queue! * * @return * - ERR_BUF Could not make room for Ethernet header. * - ERR_MEM Hardware address unknown, and no more ARP entries available *to query for address or queue the packet. * - ERR_MEM Could not queue packet due to memory shortage. * - ERR_RTE No route to destination (no gateway to external networks). * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. * */err_tetharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q){struct eth_addr *srcaddr = (struct eth_addr *)netif->hwaddr;err_t result = ERR_MEM;int is_new_entry = 0;s16_t i_err;netif_addr_idx_t i;/* 如果是广播、组播或者无效的单播,不需要发起ARP请求,不用在继续往下处理 */if (ip4_addr_isbroadcast(ipaddr, netif) ||ip4_addr_ismulticast(ipaddr) ||ip4_addr_isany(ipaddr)) {LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));return ERR_ARG;}/* 按规则找到arp entry进行操作 */i_err = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);if (i_err < 0) { /* 没找到能用的arp entry */LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));if (q) {LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));ETHARP_STATS_INC(etharp.memerr);}return (err_t)i_err;}LWIP_ASSERT("type overflow", (size_t)i_err < NETIF_ADDR_IDX_MAX);i = (netif_addr_idx_t)i_err; /* 找到合法的arp entry */if (arp_table[i].state == ETHARP_STATE_EMPTY) { /* 是新的arp entry , 做下标记 */is_new_entry = 1; /* 标记下当前为新的arp entry */arp_table[i].state = ETHARP_STATE_PENDING; /* 更新为pending态 *//* 保存网卡到arp entry中 */arp_table[i].netif = netif;}/* arp请求中或者arp entry有效即可往下走 */LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",((arp_table[i].state == ETHARP_STATE_PENDING) ||(arp_table[i].state >= ETHARP_STATE_STABLE)));/* do we have a new entry? or an implicit query request? */if (is_new_entry || (q == NULL)) { /* 新的arp entry或者隐式ARP请求都需要重新发起ARP请求 *//* 发送ARP请求 */result = etharp_request(netif, ipaddr);if (result != ERR_OK) {/* ARP请求发送失败 *//* 该故障可能只是暂时的 , 而且在etharp_tmr()周期定时函数中会重新发出ARP请求,所以这里先跳过 */} else {/* ARP请求发送成功 */if ((arp_table[i].state == ETHARP_STATE_PENDING) && !is_new_entry) {/* 隐式ARP请求,发送请求成功,重置下ctime,不让其快过期 */LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: reset ctime for entry %"S16_F"\n", (s16_t)i));arp_table[i].ctime = 0;}}if (q == NULL) {return result; /* 隐式ARP请求的话,不需要往下保存数据了 */}}LWIP_ASSERT("q != NULL", q != NULL);if (arp_table[i].state >= ETHARP_STATE_STABLE) { /* 当前条目已经有效 *//* 更新下当前网卡发送IP数据包时使用的arp entry,方便下次优先遍历 */ETHARP_SET_ADDRHINT(netif, i);/* 可以直接把IP数据包交给链路层转发 */result = ethernet_output(netif, q, srcaddr, &(arp_table[i].ethaddr), ETHTYPE_IP);} else if (arp_table[i].state == ETHARP_STATE_PENDING) { /* ARP请求中 *//* 需要把当前IP数据包放到当前arp entry的缓存队列中 */struct pbuf *p;int copy_needed = 0;/* 如果q这个pbuf链中包含一个可改动的pbuf(即是PBUFF_ERF、PBUF_POOL、PBUF_RAM),都需要拷贝整个pbuf链到arp entry的缓存队列中. */p = q;while (p) {LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == NULL));if (PBUF_NEEDS_COPY(p)) { /* 遍历pbuf链中各个pbuf,是否含有可改动的pbuf */copy_needed = 1; /* 需要拷贝整个pbuf链 */break;}p = p->next;}if (copy_needed) {/* 将整个包复制到新的pbuf中 */p = pbuf_clone(PBUF_LINK, PBUF_RAM, q);} else {/* 引用旧的pbuf就足够了 */p = q;pbuf_ref(p);}if (p != NULL) {#if ARP_QUEUEINGstruct etharp_q_entry *new_entry;/* 申请一个新的 arp entry queue 节点资源 */new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);if (new_entry != NULL) {unsigned int qlen = 0; /* 记录队列节点数 */new_entry->next = NULL;new_entry->p = p; /* 把IP层需要发送的数据包先放到当前arp entry queue节点 */if (arp_table[i].q != NULL) {/* 队列已经存在,将新条目插入到队列尾 */struct etharp_q_entry *r;r = arp_table[i].q;qlen++;while (r->next != NULL) {r = r->next;qlen++;}r->next = new_entry;} else {/* 队列不存在,当前节点放到队列首部 */arp_table[i].q = new_entry;}#if ARP_QUEUE_LENif (qlen >= ARP_QUEUE_LEN) { /* 队列超员后,需要丢弃旧报文,为新报文腾位 */struct etharp_q_entry *old;old = arp_table[i].q;arp_table[i].q = arp_table[i].q->next; /* 删除最老的报文 */pbuf_free(old->p); /* 释放该报文资源 */memp_free(MEMP_ARP_QUEUE, old); /* 回收该节点资源 */}#endifLWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"U16_F"\n", (void *)q, i));result = ERR_OK; /* ARP请求处理完毕 */} else { /* ARP缓存节点MEMP_ARP_QUEUE内存池没有资源了 */pbuf_free(p); /* 释放拷贝的的pbuf资源 */LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));result = ERR_MEM; /* 返回内存不足 */}#else /* ARP_QUEUEING */ /* arp entry缓存队列只能是单包配置 *//* 对于每个ARP请求总是只排队一个包,释放之前排队的包 */if (arp_table[i].q != NULL) {LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"U16_F"\n", (void *)q, (u16_t)i));pbuf_free(arp_table[i].q);}arp_table[i].q = p;result = ERR_OK;LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"U16_F"\n", (void *)q, (u16_t)i));#endif /* ARP_QUEUEING */} else { /* 拷贝pbuf链表时PBUF_RAM内存堆空间不足 */ETHARP_STATS_INC(etharp.memerr);LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));result = ERR_MEM; /* 返回内存不足 */}}return result; /* 搞完 */}

推荐阅读