ARP应答部分代码为uip_arp.c中的void uip_arp_arpin(void)函数.这个函数是在设备接收到ARP包时,由驱动程序调用的.如果收到是ARP包是一个对本地主机上次发送的ARP请求的应答,那么就从包中取得自己想要的主机的MAC地址,加入自己的ARP缓存表中.如果收到是一个ARP请求,那就把自己的MAC地址打包成一个ARP应答,发送给请求的主机. 看代码uip_arp.c的254行:1./*-----------------------------------------------------------------------------------*/2./**3.* ARP processing for incoming ARP packets.4.*对传入的ARP包的处理.5.* This function should be called by the device driver when an ARP6.* packet has been received. The function will act differently7.* depending on the ARP packet type: if it is a reply for a request8.* that we previously sent out, the ARP cache will be filled in with9.* the values from the ARP reply. If the incoming ARP packet is an ARP10.* request for our IP address, an ARP reply packet is created and put11.* into the uip_buf[] buffer.12.*此函数在收到ARP包时由设备驱动调用,函数行为会因包类型而有不同.如果收到的是一个对前先发送的请求的应答13.*则根据应答的值填充缓存.如果传入的包是对我们的IP的请求,则创建一个ARP应答,并放入uip_buf[]中.14.* When the function returns, the value of the global variable uip_len15.* indicates whether the device driver should send out a packet or16.* not. If uip_len is zero, no packet should be sent. If uip_len is17.* non-zero, it contains the length of the outbound packet that is18.* present in the uip_buf[] buffer.19.*函数返回时,全局变量uip_len的值指明了设备驱动要不要发送包.若uip_len为0,则不需发送,若uip_len不是0,20.* 则其值是uip_buf[]中包含的要传出的包的大小.21.* This function expects an ARP packet with a prepended Ethernet22.* header in the uip_buf[] buffer, and the length of the packet in the23.* global variable uip_len.此函数预期中的uip_buf中有一个带以太网头的ARP包.其长度存为uip_len中.24.*/25./*-----------------------------------------------------------------------------------*/26.void27.uip_arp_arpin(void)28.{29.30.if(uip_len < sizeof(struct arp_hdr)) {31.uip_len = 0;32.return;33.}34.uip_len = 0;35.36.switch(BUF->opcode) {37.case HTONS(ARP_REQUEST):38./* ARP request. If it asked for our address, we send out a39.reply. 如果是一个ARP请求,则发送应答.*/40.if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) {41./* First, we register the one who made the request in our ARP42.table, since it is likely that we will do more communication43.with this host in the future.首先,我们将发送请求的主机注册到ARP缓存表中,因为我们很可能要跟它要有更多的交流 */44.uip_arp_update(BUF->sipaddr, &BUF->shwaddr);45.46./* The reply opcode is 2. 应答的操作码为2*/47.BUF->opcode = HTONS(2);48.49.memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6);50.memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);51.memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);52.memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6);53.54.BUF->dipaddr[0] = BUF->sipaddr[0];55.BUF->dipaddr[1] = BUF->sipaddr[1];56.BUF->sipaddr[0] = uip_hostaddr[0];57.BUF->sipaddr[1] = uip_hostaddr[1];58.59.BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);60.uip_len = sizeof(struct arp_hdr);61.}62.break;63.case HTONS(ARP_REPLY):64./* ARP reply. We insert or update the ARP table if it was meant65.for us. 如果收到的是一个ARP应答,而且也是我们所要的应答的话,就插件并更新ARP缓存表*/66.if(uip_ipaddr_cmp(BUF->dipaddr, uip_hostaddr)) {67.uip_arp_update(BUF->sipaddr, &BUF->shwaddr);68.}69.break;70.}71.72.return;73.}复制代码这里有一件事是很有意思的,就是说如果某个主机请求得到我们的MAC的地址,我们先把它的MAC地址加入到自己的表中.就好比社交网络中,别人请求加我们为好友,如果我们接收的话,也自动加对方为好友一样.既然对方找上我们了,肯定是要做进一步的交流,互加MAC地址也很自然的.有了上一篇文章,这里似乎不必做过多的解释了.但还是说一下吧,看看我们怎么做应答的. 如果收到了一个请求,我们要做应答:1./* The reply opcode is2. */2.BUF->opcode = HTONS(2);3.4.memcpy(BUF->dhwaddr.addr, BUF->shwaddr.addr, 6);5.memcpy(BUF->shwaddr.addr, uip_ethaddr.addr, 6);6.memcpy(BUF->ethhdr.src.addr, uip_ethaddr.addr, 6);7.memcpy(BUF->ethhdr.dest.addr, BUF->dhwaddr.addr, 6);8.9.BUF->dipaddr[0] = BUF->sipaddr[0];10.BUF->dipaddr[1] = BUF->sipaddr[1];11.BUF->sipaddr[0] = uip_hostaddr[0];12.BUF->sipaddr[1] = uip_hostaddr[1];13.14.BUF->ethhdr.type = HTONS(UIP_ETHTYPE_ARP);15.uip_len = sizeof(struct arp_hdr);复制代码由于请求和应答包很多地方是相同的,如上文中的绿色部分.我们只需将收到的请求稍加修改就可以发送回去了.首先,要改一下MAC地址,四个地方.然后要将目标主机IP设为设为请求包的源主机IP,目的主机IP设为我们的IP.就可以了.另外说下对ARP缓存表的设置:1.这个函数是集插入,更新一体的,有两个参数,IP地址,MAC地址.2.static void3.uip_arp_update(u16_t *ipaddr, struct uip_eth_addr *ethaddr)4.{5.register struct arp_entry *tabptr;6./* Walk through the ARP mapping table and try to find an entry to7.update. If none is found, the IP -> MAC address mapping is8.inserted in the ARP table. 遍历ARP映射表,看看有没有需要更新的表项,如果没有,就将新的表项插入.*/9.for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {10.11.tabptr = &arp_table[i];12./* Only check those entries that are actually in use. 所谓更新就是传入参数IP在表中己经存在了,这时不需要新建表项,而是要修改己存表项.只查使用中的表项,如果有些表项IP是0,那么就不是使用中的*/13.if(tabptr->ipaddr[0] != 0 &&14.tabptr->ipaddr[1] != 0) {15.16./* Check if the source IP address of the incoming packet matches17.the IP address in this ARP table entry. 看看传入的IP有没有匹配项.*/18.if(ipaddr[0] == tabptr->ipaddr[0] &&19.ipaddr[1] == tabptr->ipaddr[1]) {20.21./* An old entry found, update this and return. 如果有己存的匹配项,修改该项的MAC地址和更新时间.*/22.memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);23.tabptr->time = arptime;24.25.return;26.}27.}28.}29.30./* If we get here, no existing ARP table entry was found, so we31.create one. 如果运行到这里,说明没有己存的表项,则创建一个.*/32.33./* First, we try to find an unused entry in the ARP table. 先看看有没有空表项可用.*/34.for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {35.tabptr = &arp_table[i];36.if(tabptr->ipaddr[0] == 0 &&37.tabptr->ipaddr[1] == 0) {38.break;39.}40.}41.42./* If no unused entry is found, we try to find the oldest entry and43.throw it away. 如果没空表项,就找到一个最旧的表项,扔掉它,换成我们的.*/44.if(i == UIP_ARPTAB_SIZE) {45.tmpage = 0;46.c = 0;47.for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {48.tabptr = &arp_table[i];49.if(arptime - tabptr->time > tmpage) {50.tmpage = arptime - tabptr->time;51.c = i;52.}53.}54.i = c;55.tabptr = &arp_table[i];56.}57. /* Now, i is the ARP table entry which we will fill with the new58. information. 现在i就是我们最终得到的表项,我们把新的信息插入到这里.*/59. memcpy(tabptr->ipaddr, ipaddr, 4);60. memcpy(tabptr->ethaddr.addr, ethaddr->addr, 6);61. tabptr->time = arptime;复制代码OK,uip_arp.c中还有两个函数,楼下继续.1.看代码:1./*-----------------------------------------------------------------------------------*/2./**3.* Periodic ARP processing function.4.*ARP周期性处理函数.5.* This function performs periodic timer processing in the ARP module6.* and should be called at regular intervals. The recommended interval7.* is 10 seconds between the calls.8.*此函数在ARP模块中施行周期性处理,它应该每隔一段时间就调用一次.推荐为10秒.9.*/10./*-----------------------------------------------------------------------------------*/11.void12.uip_arp_timer(void)13.{14. struct arp_entry *tabptr;15.16. ++arptime;17. for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {18. tabptr = &arp_table[i];19. if((tabptr->ipaddr[0] | tabptr->ipaddr[1]) != 0 &&20. arptime - tabptr->time >= UIP_ARP_MAXAGE) {21. memset(tabptr->ipaddr, 0, 4);22. }23. }24.}复制代码1.#define UIP_ARP_MAXAGE 120//即20分钟.复制代码从这里可以看出,ARP表项中的更新时间是一个数,每调用一个上面这个函数,这个数就加1. 这个周期性处理函数的作用就是每隔一段时间把超过20分钟都没有更新过的表项扔掉.2.看代码:1./*-----------------------------------------------------------------------------------*/2./**3.* Initialize the ARP module.初始化ARP模块.4.*5.*/6./*-----------------------------------------------------------------------------------*/7.void8.uip_arp_init(void)9.{10.for(i = 0; i < UIP_ARPTAB_SIZE; ++i) {11.memset(arp_table[i].ipaddr, 0, 4);12.}13.}复制代码一目了解,所谓初始化就是把数组中很一个表项都扔掉,清空成0,变成空表项.到这里,并于ARP的代码就说到这里,欢迎提问.。