uIP的ARP协议分析
地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址(MAC)的一个TCP/IP协议。在三层网络层我们确定了IP地址后,信息来到了二层进行再封装,在二层需要MAC地址,这时就通过三层的IP去寻找二层的MAC。
信息沿着下图的黑线路径进行传播,从高层往低层是不断封包,从低层往高层是不断解包。
下图是ARP的帧格式
结构ether_header定义了以太网帧首部;结构arphdr定义了其后的5个字段,其信息用于在任何类型的介质上传送ARP请求和回答;ether_arp结构除了包含arphdr结构外,还包含源主机和目的主机的地址。
分析一下ARP包头。头两个字节是硬件类型0x0001,接着两个字节是协议类型,即ARP使用的是IP协议代号0x0800。硬件地址长度和协议地址长度分别是6和4。这与ARP报文格式是对应的。后面的2个字节OP指示当前包是请求包还是应答包,对应的值分别是0x0001和0x0002。
下图是用Wireshark抓取的ARP包,上图是请求包(PC发往uIP),请求时,是向局域网询问,而不是向整个Internet询问,下图是应答包(uIP发往PC),可以注意到的是,请求包是42字节,而应答包是经过填充的60字节。至于原因,其实还没有想清楚。
下面是uIP的主要结构,是官方提供的主要使用格式,可以将while中的函数封装成一个轮询函数uip_polling(),需要循环起来。uIP在不同的应用中,函数uip_polling()都不需要改,需要改的是宏UIP_APPCALL,在uIP中,使用的都是UIP_APPCALL,若改成自己的函数,就会运行我们的函数啦。
时钟对于uIP来说是非常重要的,如果缺少了时钟,那么uIP只会ping和arp,TCP和UDP都需要时钟的支持。时钟的完成我们需要用到定时器中断,如果用uCOS的话,就可以把systick中断定时拿来当做uIP时钟。
//将以下声明放入uip_app.h,然后在uip_conf.h中,加入#include"uip_app.h"
void tcp_appcall(void);
#ifndef UIP_APPCALL
#define UIP_APPCALL tcp_appcall
#endif
//uIP初始化
int i;
uip_ipaddr_t ipaddr;
struct timer periodic_timer, arp_timer;
timer_set(&periodic_timer, CLOCK_SECOND / 2);
timer_set(&arp_timer, CLOCK_SECOND * 10);
tapdev_init();
uip_init();
uip_ipaddr(ipaddr, 192,168,0,2);
uip_sethostaddr(ipaddr);
uip_ipaddr(ipaddr, 192,168,0,1);
uip_setdraddr(ipaddr);
uip_ipaddr(ipaddr, 255,255,255,0);
uip_setnetmask(ipaddr);
while(1) {
//从网络设备读取一个IP包,数据长度uip_len在uip.c中定义
uip_len = tapdev_read();
if(uip_len > 0) {
//处理IP数据包(只有校验通过的IP包才会被接收)
if(BUF->type == htons(UIP_ETHTYPE_IP)){//是否是IP包,这里的类型指的是以太帧结构中的类型,即MAC地址后面的类型
uip_arp_ipin();//空
uip_input();//IP包处理,若有需要返回的数据,则存入uip_buf,长度为uip_len,这两个变量是全局变量
if(uip_len > 0) {
//目的地址是否在一个局域网,若是,查ARP表;若不是,MAC地址填写默认路由器地址(网关)。
//查ARP表,若IP在表中,取MAC地址即可;若IP不在表中,将发送ARP包来取代本来要发送的IP包
uip_arp_out();
tapdev_send();//发送数据到以太网
}
}
else if(BUF->type == htons(UIP_ETHTYPE_ARP)){//是否是ARP包
//如果收到的ARP为请求,就想自己的信息放入uip_buf并顺便根据请求者的信息更新ARP表;
//如果是回应包,就更新ARP表
uip_arp_arpin();
if(uip_len > 0) {
tapdev_send();//发送数据到以太网
}
}
}
else if(timer_expired(&periodic_timer)) {//定时器定时,可以为0.5s
timer_reset(&periodic_timer);//复位定时器
for(i = 0; i < UIP_CONNS; i++) {//轮流处理每个TCP连接
uip_periodic(i);//处理TCP通信事件,若有需要返回的数据,则存入uip_buf,长度为uip_len,这两个变量是全局变量
if(uip_len > 0) {
uip_arp_out();
tapdev_send();
}
}
#if UIP_UDP
for(i = 0; i < UIP_UDP_CONNS; i++) {//轮流处理每个UDP连接
uip_udp_periodic(i);//处理UDP通信事件
if(uip_len > 0) {
uip_arp_out();
tapdev_send();
}
}
#endif /* UIP_UDP */
if(timer_expired(&arp_timer)) {//建议为每隔10s调用一次ARP更新
timer_reset(&arp_timer);
uip_arp_timer();
}
}
}
【Reference】http://www.cnblogs.com/laojie4321/archive/2012/04/12/2444187.html