uip源码分析[分享借鉴]_第1页
uip源码分析[分享借鉴]_第2页
uip源码分析[分享借鉴]_第3页
已阅读5页,还剩48页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、uIP的ARP协议代码分析之一 ARP请求(是根据IP地址获取物理地址的一个TCP/IP协议 同时将IP地址和硬件地址存入本机ARP缓存中,下次请求时直接查询ARP缓存。)对于一个设备用的ARP协议而言,重要的东西包括三方面:1. 一个本地的IP与MAC地址的缓存表.以有对应的更新和查询操作. 2. 一个发送ARP请求的函数. 3. 一个处理ARP回复的函数.下面我们来看uIP中是如何实现的(代码见uip_arp.c:首先,定义一个缓存表的数据结构,99行起:struct arp_entry u16_t ipaddr2;struct uip_eth_addr ethaddr;u8_t time

2、;只有三个项,很简单第一项是ip地址,16*2=4*8位的,保存四个八位组.第二项是MAC地址.第三项是缓存更新时间.下来是ARP请求发送函数:uip_arp.c L325/*-*/* Prepend Ethernet header to an outbound IP packet and see if we need* to send out an ARP request.*为传出的IP包添加以太网头并看是否需要发送ARP请求.* This function should be called before sending out an IP packet. The* function che

3、cks the destination IP address of the IP packet to see* what Ethernet MAC address that should be used as a destination MAC* address on the Ethernet.*此函数应该在发送IP包时调用,它会检查IP包的目的IP地址,看看以太网应该使用什么目的MAC地址.* If the destination IP address is in the local network (determined* by logical ANDing of netmask and

4、our IP address), the function* checks the ARP cache to see if an entry for the destination IP* address is found. If so, an Ethernet header is prepended and the* function returns. If no ARP cache entry is found for the* destination IP address, the packet in the uip_buf is replaced by* an ARP request

5、packet for the IP address. The IP packet is dropped* and it is assumed that they higher level protocols (e.g., TCP)* eventually will retransmit the dropped packet.*如果目的IP地址是在局域网中(由IP地址与子网掩码的与逻辑决定),函数就会从ARP缓存表中查找有*无对应项.若有,就取对应的MAC地址,加上以太网头,并返回,否则uip_buf中的数据包会被替换成一个*目的IP在址的ARP请求.原来的IP包会被简单的仍掉,此函数假设高层协

6、议(如TCP)会最终重传扔掉的包.* If the destination IP address is not on the local network, the IP* address of the default router is used instead.*如果目标IP地址并非一个局域网IP,则会使用默认路由的IP地址.* When the function returns, a packet is present in the uip_buf* buffer, and the length of the packet is in the global variable* uip_le

7、n.函数返回时,uip_buf中已经有了一个包,其长度由uip_len指定.*/*-*/voiduip_arp_out(void)struct arp_entry *tabptr;/* Find the destination IP address in the ARP table and constructthe Ethernet header. If the destination IP addres isnt on thelocal network, we use the default routers IP address instead./在ARP表中找到目的IP地址,构成以太网头.

8、如果目的IP地址不在局域网中,则使用默认/路由的IP.If not ARP table entry is found, we overwrite the original IPpacket with an ARP request for the IP address. */如果ARP表中找不到,则将原来的IP包替换成一个ARP请求./* First check if destination is a local broadcast. 首先检查目标是不是广播*/if(uip_ipaddr_cmp(IPBUF-destipaddr, broadcast_ipaddr) memcpy(IPBUF-e

9、thhdr.dest.addr, broadcast_ethaddr.addr, 6); else /* Check if the destination address is on the local network. 检查目标地址是否在局域网内 */if(!uip_ipaddr_maskcmp(IPBUF-destipaddr, uip_hostaddr, uip_netmask) /* Destination address was not on the local network, so we need touse the default routers IP address inst

10、ead of the destinationaddress when determining the MAC address. 目的地址不在局域网内,所以保用默认路由器的地址来确在MAC地址*/uip_ipaddr_copy(ipaddr, uip_draddr); else /* Else, we use the destination IP address. 否则,使用目标IP地址*/uip_ipaddr_copy(ipaddr, IPBUF-destipaddr);for(i = 0; i ipaddr) break;if(i = UIP_ARPTAB_SIZE) /* The dest

11、ination address was not in our ARP table, so weoverwrite the IP packet with an ARP request. 如果遍历到头没找到,将原IP包替换为ARP请求并返回*/memset(BUF-ethhdr.dest.addr, 0xff, 6);memset(BUF-dhwaddr.addr, 0x00, 6);memcpy(BUF-ethhdr.src.addr, uip_ethaddr.addr, 6);memcpy(BUF-shwaddr.addr, uip_ethaddr.addr, 6);uip_ipaddr_co

12、py(BUF-dipaddr, ipaddr);uip_ipaddr_copy(BUF-sipaddr, uip_hostaddr);BUF-opcode = HTONS(ARP_REQUEST); /* ARP request. */BUF-hwtype = HTONS(ARP_HWTYPE_ETH);BUF-protocol = HTONS(UIP_ETHTYPE_IP);BUF-hwlen = 6;BUF-protolen = 4;BUF-ethhdr.type = HTONS(UIP_ETHTYPE_ARP);uip_appdata = &uip_bufUIP_TCPIP_HLEN +

13、 UIP_LLH_LEN;uip_len = sizeof(struct arp_hdr);return;/* Build an ethernet header. 如果是在局域网中,且在ARP缓存中找到了(如果没找到进行不到这一步,在上面就返回了),则构建以太网头*/memcpy(IPBUF-ethhdr.dest.addr, tabptr-ethaddr.addr, 6);memcpy(IPBUF-ethhdr.src.addr, uip_ethaddr.addr, 6);IPBUF-ethhdr.type = HTONS(UIP_ETHTYPE_IP);uip_len += sizeof(

14、struct uip_eth_hdr);以上内容自325行起.下面再总结一下其基本顺序:用IPBUF-ethhdr.dest.addr来存储目的IP地址,它有可能是局域网内一主机IP,也可能是路由器IP(如果是发往外网,即原来的目的IP不在局域网内),还有可能是广播专用的IP.先看是不是在广播:如果是广播,将IPBUF-ethhdr.dest.addr设为广播IP.再看是不是在局域网内:如果不是,则将IPBUF-ethhdr.dest.addr设为路由器IP.如果在局域网内,查看是否已经存在于ARP缓存表中:如果不在,将要发送的换成ARP请求,返回.如果已存在,则查找使用查找到的MAC地址为I

15、P包添加以太网头.这里还要解释一些细节问题,主要是:1. 如何在IP包上添加以太网头 2. 如果将IP包替换成ARP请求,ARP请求的格式是什么. 3. 广播地址这些问题将在二楼来说.将IP包替换成ARP请求部分代码(实际上IP包是放在uip_buf里的,这里只是将uip_buf填充上ARP请求即可),于uip_arp.c的388行:/* The destination address was not in our ARP table, so we overwrite the IP packet with an ARP request. */ memset(BUF-ethhdr.dest.ad

16、dr, 0xff, 6); memset(BUF-dhwaddr.addr, 0x00, 6); memcpy(BUF-ethhdr.src.addr, uip_ethaddr.addr, 6); memcpy(BUF-shwaddr.addr, uip_ethaddr.addr, 6); uip_ipaddr_copy(BUF-dipaddr, ipaddr); uip_ipaddr_copy(BUF-sipaddr, uip_hostaddr); BUF-opcode = HTONS(ARP_REQUEST); /* ARP request. */ BUF-hwtype = HTONS(A

17、RP_HWTYPE_ETH); BUF-protocol = HTONS(UIP_ETHTYPE_IP); BUF-hwlen = 6; BUF-protolen = 4; BUF-ethhdr.type = HTONS(UIP_ETHTYPE_ARP); uip_appdata = &uip_bufUIP_TCPIP_HLEN + UIP_LLH_LEN; uip_len = sizeof(struct arp_hdr); return;首先解释这里的BUF(于uip_arp.c的116行):#define BUF (struct arp_hdr *)&uip_buf0)可见这里的BUF就是

18、uip_buf,只不过这里将它取做一个struct arp_hdr的结构体:struct arp_hdr struct uip_eth_hdr ethhdr; u16_t hwtype; /硬件类型 u16_t protocol; /协议类型 u8_t hwlen; u8_t protolen; u16_t opcode; /操作码 struct uip_eth_addr shwaddr; /源以太网地址 u16_t sipaddr2; /源IP地址 struct uip_eth_addr dhwaddr; /目的以太网地址 u16_t dipaddr2; /目的IP地址;struct uip

19、_eth_hdr struct uip_eth_addr dest;struct uip_eth_addr src;u16_t type;这是arp_hdr的第一个成员ethhdr的类型定义,对应图片中的前三项:6+6+2,目的以太网地址,源以太网地址,2字节数据类型(ARP请求和应答为0x0806).struct arp_hdr的第二个成员u16_t hwtype,对应图片中第三项,2字节硬件类型(值为1表示以太网).struct arp_hdr的第三个成员u16_t protocol,对应图片中第四项,2字节要映射的协议地址类型(ip地址为0x0800).struct arp_hdr的第四

20、个成员u8_t hwlen,对应图片中第五项,1字节硬件地址长度(对MAC地址来说为6).struct arp_hdr的第五个成员u8_t protolen,对应图片中第六项,1字节协议地址长度(对IP地址来说为4).struct arp_hdr的第六个成员u16_t opcode,对应图片中第七项,2字节操作码(1 ARP请求,2 ARP应答,3 RARP请求,4 RARP应答,必须字段).struct arp_hdr的第七个成员struct uip_eth_addr shwaddr,对应图片中第八项,6字节源以太网地址.struct arp_hdr的第八个成员u16_t sipaddr2;

21、,对应图片中第九项,2字节源IP地址.struct arp_hdr的第九个成员struct uip_eth_addr dhwaddr,对应图片中第十项,6字节目标以太网地址.struct arp_hdr的第十个成员u16_t dipaddr2;,对应图片中第十一项,2字节目标IP地址.上面绿色的表示已经详解的,红字的表示要进一步说明的.这就是一个ARP帧的结构,可以看到,里面的源和目的以太网地址都是重复的.我们再看函数中的代码:memset(BUF-ethhdr.dest.addr, 0xff, 6);memset(BUF-dhwaddr.addr, 0x00, 6);memcpy(BUF-e

22、thhdr.src.addr, uip_ethaddr.addr, 6);memcpy(BUF-shwaddr.addr, uip_ethaddr.addr, 6);这里四个memset,重复的源和目的以太网地址一起设置了,四个memset对应图片中的1,10,2,8项.但是:对1和10两项,都是源以太网地址,但置的值是不同的,分别为0xff*6和0x00*6.为什么会这样呢?因为他们的用处不一样,见:【相关资料】ARP分组格式(帧格式,报文格式)6+6 以太网的源地址和目的地址,目的地址为全1的为广播地址注意这里说,目的地址为全为1的广播地址.什么意思?当你发送一个ARP请求是,你是想知道一

23、个IP对应的以太网地址(即MAC地址),但是你现在不知道目的主机的以太网地址,你问谁啊?不知道问谁,这种情况下,只能是广播一下了,0xff*6就是广播地址.从图片中可以看到,ARP包是分成两部分的,前6+6+2叫做以太网首部,它的意义就是分组是从谁(源地址)发给谁(目的地址)的什么类型(帧类型,请求和应答为0x0806),第二部分则是内容.来看这个例子:请求帧如下(为了清晰在每行的前面加了字节计数,每行16个字节):以太网首部(14字节)0000: ff ff ff ff ff ff 00 05 5d 61 58 a8 08 06ARP帧(28字节)0000: 00 010010: 08 00

24、 06 04 00 01 00 05 5d 61 58 a8 c0 a8 00 370020: 00 00 00 00 00 00 c0 a8 00 02填充位(18字节)0020: 00 77 31 d2 50 100030: fd 78 41 d3 00 00 00 00 00 00 00 00以太网首部: 目的主机采用广播地址,源主机的MAC地址是00:05:5d:61:58:a8,上层协议类型0x0806表示ARP。ARP帧: 硬件类型0x0001表示以太网, 协议类型0x0800表示IP协议, 硬件地址(MAC地址)长度为6, 协议地址(IP地址)长度为4, op为0x0001表示请

25、求目的主机的MAC地址, 源主机MAC地址为00:05:5d:61:58:a8,源主机IP地址为c0 a8 00 37(192.168.0.55), 目的主机MAC地址全0待填写,目的主机IP地址为c0 a8 00 02(192.168.0.2)。由于以太网规定最小数据长度为46字节,ARP帧长度只有28字节,因此有18字节填充位,填充位的内容没有定义,与具体实现相关。 uIP的ARP协议代码分析之二 ARP应答 如果读懂了上面的文章,这里的理解就相对容易了.ARP应答部分代码为uip_arp.c中的void uip_arp_arpin(void)函数.这个函数是在设备接收到ARP包时,由驱动

26、程序调用的.如果收到是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 rec

27、eived. 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 pa

28、cket 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

29、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

30、 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 opcode) 37. case HTONS(ARP_REQUEST):38.

31、/* 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.首

32、先,我们将发送请求的主机注册到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_eth

33、addr.addr, 6);52. memcpy(BUF-ethhdr.dest.addr, BUF-dhwaddr.addr, 6);53.54. BUF-dipaddr0 = BUF-sipaddr0;55. BUF-dipaddr1 = BUF-sipaddr1;56. BUF-sipaddr0 = uip_hostaddr0;57. BUF-sipaddr1 = uip_hostaddr1;58.59. BUF-ethhdr.type = HTONS(UIP_ETHTYPE_ARP);60. uip_len = sizeof(struct arp_hdr);61. 62. break;

34、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. 复制代码这里有一件事是很有意思的,就是说如果

35、某个主机请求得到我们的MAC的地址,我们先把它的MAC地址加入到自己的表中.就好比社交网络中,别人请求加我们为好友,如果我们接收的话,也自动加对方为好友一样.既然对方找上我们了,肯定是要做进一步的交流,互加MAC地址也很自然的.有了上一篇文章,这里似乎不必做过多的解释了.但还是说一下吧,看看我们怎么做应答的.如果收到了一个请求,我们要做应答:1. /* The reply opcode is 2. */2. BUF-opcode = HTONS(2);3.4. memcpy(BUF-dhwaddr.addr, BUF-shwaddr.addr, 6);5. memcpy(BUF-shwaddr

36、.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-dipaddr0 = BUF-sipaddr0;10. BUF-dipaddr1 = BUF-sipaddr1;11. BUF-sipaddr0 = uip_hostaddr0;12. BUF-sipaddr1 = uip_hostaddr1;13.14. BUF-ethhdr.type = HTONS(UIP_E

37、THTYPE_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. regi

38、ster 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 ipaddr0 != 0 &14. tabptr-ipaddr1 != 0) 15.16. /* Check if

39、the source IP address of the incoming packet matches17. the IP address in this ARP table entry. 看看传入的IP有没有匹配项.*/18. if(ipaddr0 = tabptr-ipaddr0 &19. ipaddr1 = tabptr-ipaddr1) 20.21. /* An old entry found, update this and return. 如果有己存的匹配项,修改该项的MAC地址和更新时间.*/22. memcpy(tabptr-ethaddr.addr, ethaddr-add

40、r, 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 ipaddr0 = 0 &37. tabptr-ipaddr1 =

41、 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 time tmpage) 50. tmpage = arptime - tabptr-time;51. c = i;52. 53. 54. i = c;55. tabpt

42、r = &arp_tablei;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.

43、/*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. voi

44、d12. uip_arp_timer(void)13. 14. struct arp_entry *tabptr;15. 16. +arptime;17. for(i = 0; i ipaddr0 | tabptr-ipaddr1) != 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.这个

45、周期性处理函数的作用就是每隔一段时间把超过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 0) 3. uip_input();4. if(uip_len 0) 5. devicedriver_send();6. 7. 注意:如果你写的uip设备驱动需要使用ARP协议(Address Resolution Protocal),比如说在以太网内使用uip的时候,你就得在

46、调用此函数之前先调用uip的ARP代码:1. #define BUF (struct uip_eth_hdr *)&uip_buf0)2. uip_len = ethernet_devicedrver_poll();3. if(uip_len 0) 4. if(BUF-type = HTONS(UIP_ETHTYPE_IP) 5. uip_arp_ipin();6. uip_input();7. if(uip_len 0) 8. uip_arp_out();9. ethernet_devicedriver_send();10. 11. else if(BUF-type = HTONS(UIP_

47、ETHTYPE_ARP) 12. uip_arp_arpin();13. if(uip_len 0) 14. ethernet_devicedriver_send();15. 16. 使用例程:example-mainloop-with-arp.c, and example-mainloop-without-arp.c. 此接口定义于uip.h的第257行。2. #define uip_periodic(conn)周期性处理一连接,需借助其连接号。此函数对uip的tcp连接进行一些必要的周期性处理(定时器,轮询等),它应该在周期性uip定时器消息到来时被调用。它应该对每一个连接都得到调用,不管连接是打开的还是关闭的。此函数返回时,可能会有要传送出去的数据包等待得到服务,包内容会被放到uip包缓冲区中。如果真的有数据包需要送出,则uip_len的值会被置为一个大于零的数,此时设备驱动应该将数据包发送出去。(这一段与(1)中相应段落的意思应该是一致的。)通常调用此函数时,会使用一个如下的for循环:1. for(i = 0; i 0) 4. devicedriver_send();5. 6. 注意:如果你所写的设备驱动需要使用ARP协

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论