ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
如果在ARP缓存表中没有找到目标IP地址对应的表项,那么ARP协议就会创建一个表项,这也是ARP协议的核心处理,对于刚创建的表项,它在初始化网卡信息后会被设置为ETHARP\_STATE\_PENDING状态,与此同时一个ARP请求包将被广播出去,这个时候的表项是无法发送数据的,只有等待到目标主机回应了一个ARP应答包才能发送数据,那么这些数据在这段时间中将被挂到表项的等待队列上,在ARP表项处于ETHARP\_STATE\_STABLE状态完成数据的发送,函数源码具体见代码清单 10‑13。 ``` 1 err_t 2 etharp_query(struct netif *netif, 3 const ip4_addr_t *ipaddr, 4 struct pbuf *q) 5 { 6 struct eth_addr *srcaddr = (struct eth_addr *)netif->hwaddr; 7 err_t result = ERR_MEM; 8 int is_new_entry = 0; 9 s16_t i_err; 10 netif_addr_idx_t i; 11 12 /* 检是否为单播地址 */ 13 if (ip4_addr_isbroadcast(ipaddr, netif) || 14 ip4_addr_ismulticast(ipaddr) || 15 ip4_addr_isany(ipaddr)) 16 { 17 return ERR_ARG; 18 } 19 20 /* 在ARP缓存中查找表项,如果没有则尝试创建表项 */ 21 i_err = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);(1) 22 23 /* 没有发现表项或者没有创建表项成功 */ 24 if (i_err < 0) 25 { 26 LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, 27 ("etharp_query: could not create ARP entry\n")); 28 if (q) 29 { 30 LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, 31 ("etharp_query: packet dropped\n")); 32 ETHARP_STATS_INC(etharp.memerr); 33 } 34 return (err_t)i_err; //返回错误代码 35 } 36 LWIP_ASSERT("type overflow", (size_t)i_err < NETIF_ADDR_IDX_MAX); 37 38 //找到对应的表项或者创建表项成功 39 i = (netif_addr_idx_t)i_err; 40 41 /* 将新表项标记为待处理 */ 42 if (arp_table[i].state == ETHARP_STATE_EMPTY) 43 { 44 is_new_entry = 1; 45 arp_table[i].state = ETHARP_STATE_PENDING; 46 /* 记录网络接口 */ 47 arp_table[i].netif = netif; (2) 48 } 49 50 /* 是否有新的表项 */ 51 if (is_new_entry || (q == NULL)) (3) 52 { 53 /* 发送ARP请求包*/ 54 result = etharp_request(netif, ipaddr); 55 if (result != ERR_OK) 56 { 57 /* 无法发送ARP请求 */ 58 } 59 if (q == NULL) 60 { 61 return result; (4) 62 } 63 } 64 65 LWIP_ASSERT("q != NULL", q != NULL); 66 /* 表项状态是否稳定 */ 67 if (arp_table[i].state >= ETHARP_STATE_STABLE) 68 { 69 ETHARP_SET_ADDRHINT(netif, i); 70 /* 发送数据包 */ 71 result = ethernet_output(netif, q, 72 srcaddr, 73 &(arp_table[i].ethaddr), 74 ETHTYPE_IP); (5) 75 } 76 /* 如果表项是ETHARP_STATE_PENDING状态 */ 77 else if (arp_table[i].state == ETHARP_STATE_PENDING) 78 { 79 /* 将给数据包'q'排队 */ 80 struct pbuf *p; 81 int copy_needed = 0; 82 /* 如果q包含必须拷贝的pbuf,请将整个链复制到一个新的PBUF_RAM */ 83 p = q; 84 while (p) 85 { 86 LWIP_ASSERT("no packet queues allowed!", 87 (p->len != p->tot_len) || (p->next == 0)); 88 if (PBUF_NEEDS_COPY(p)) (6) 89 { 90 //需要拷贝 91 copy_needed = 1; 92 break; 93 } 94 p = p->next; 95 } 96 if (copy_needed) 97 { 98 /* 将整个数据包复制到新的pbuf中 */ 99 p = pbuf_clone(PBUF_LINK, PBUF_RAM, q); (7) 100 } 101 else 102 { 103 /* 引用旧的pbuf就足够了 */ 104 p = q; 105 pbuf_ref(p); 106 } 107 108 if (p != NULL) 109 { 110 /* 如果使用队列 */ 111 #if ARP_QUEUEING 112 struct etharp_q_entry *new_entry; 113 /* 分配一个新的arp队列表项 */ (8) 114 new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE); 115 if (new_entry != NULL) 116 { 117 unsigned int qlen = 0; 118 new_entry->next = 0; 119 new_entry->p = p; 120 if (arp_table[i].q != NULL) 121 { 122 /* 队列已经存在,将新数据包插入队列后面 */ 123 struct etharp_q_entry *r; 124 r = arp_table[i].q; 125 qlen++; 126 while (r->next != NULL) 127 { 128 r = r->next; 129 qlen++; 130 } 131 r->next = new_entry; (9) 132 } 133 else 134 { 135 /* 队列不存在,数据包就是队列的第一个节点 */ 136 arp_table[i].q = new_entry; (10) 137 } 138 #if ARP_QUEUE_LEN 139 if (qlen >= ARP_QUEUE_LEN) 140 { 141 struct etharp_q_entry *old; 142 old = arp_table[i].q; 143 arp_table[i].q = arp_table[i].q->next; 144 pbuf_free(old->p); 145 memp_free(MEMP_ARP_QUEUE, old); 146 } 147 #endif 148 result = ERR_OK; 149 } 150 else 151 { 152 /* 申请内存失败 */ 153 pbuf_free(p); 154 result = ERR_MEM; 155 } 156 #else 157 /* 如果只是挂载单个数据包,那么始终只为每个ARP请求排队一个数据包, 158 就需要释放先前排队的数据包 */ 159 if (arp_table[i].q != NULL) 160 { 161 pbuf_free(arp_table[i].q); (11) 162 } 163 arp_table[i].q = p; 164 result = ERR_OK; 165 166 #endif 167 } 168 else 169 { 170 ETHARP_STATS_INC(etharp.memerr); 171 result = ERR_MEM; 172 } 173 } 174 return result; 175 } ``` (1)(2):函数的处理逻辑是很清晰的,首先调用etharp_find_entry()函数在ARP缓存表中查找表项,如果没有找到就尝试创建表项并且返回表项的索引,当然ARP缓存表中可能存在表项,可能为新创建的表项(ETHARP_STATE_EMPTY),也可能为ETHARP_STATE_PENDING或者ETHARP_STATE_STABLE状态。如果是新创建的表项,那么表项肯定没有其他信息,LwIP就会初始化一些信息,如网卡,然后就将表项设置为ETHARP_STATE_PENDING状态。 (3):如果表项是刚创建的或者数据包是空的,那么就会调用etharp_request()函数发送一个ARP请求包。 (4):如果数据包是空的,直接返回结果 (5):如果表项的状态大于等于 ETHARP_STATE_STABLE,表示表项已经是稳定状态了,就调用ethernet_output()函数发送数据包。 (6):通过宏定义PBUF_NEEDS_COPY(p)对数据包的类型进行判断,如果需要拷贝则将变量copy_needed设置为1,表示需要拷贝。 (7):将整个数据包复制到新的pbuf中。 (8):如果ARP_QUEUEING宏定义为1,则表示使用队列,那么LwIP会分配一个新的ARP数据包队列节点,然后插入队列中。 (9):如果队列已经存在,将新数据包插入队列后面。 (10):如果队列不存在,数据包就是队列的第一个节点。 (11):如果只是挂载单个数据包,就需要释放先前排队的数据包,然后再挂载新的数据包。 挂载的这些数据在等待到目标主机产生ARP应答的时候会发送出去,此时的发送就是延时了,所以在没有ARP表项的时候,发送数据会产生延时,在指定等待ARP应答时间内如果等不到目标主机的应答,那么这个表项将被系统回收,同时数据也无法发送出去。 上层数据包通过ARP协议进行发送数据的流程示意图具体见图 10 10。 ![](https://box.kancloud.cn/7be7cb313de98579977fa8d30e419390_862x768.png) 整个ARP协议运作示意图具体见图 10‑11。 ![](https://box.kancloud.cn/d0498f6171fe0c5e644cc791a2174e26_1162x752.png)