问题描述
1、Uboot下执行tftp命令,在交换机上可以下载升级;在tp-link路由器上失败。
2、Kernel下执行tftp命令,交换机上和tp-link路由器上均可成功。
3、Uboot下,tp-link路由器上一个包也抓不到,TFTP_RRQ包也没发过来。
4、均可以ping通。
uboot下现象:
HKVS # updateb
*******************************************************
* ATTENTION: PLEASE READ THIS NOTICE CAREFULLY! *
* DO NOT reset the device, or disrupt this process. *
* If this process fails, the device might be unusable.*
* If you find this too risky, power off device now. *
* or press the SPACE key to start the process now *
*******************************************************
Trying eth0
link up on port 0, speed 100, full duplex
Using eth0 device
TFTP from server 10.15.2.44; our IP address is 10.15.2.45
Filename 'u-boot_8127.bin'.
Load address: 0x81000000
Loading: T T T T T T T T T T T T T T T T T T T
wireshark抓包:
Wireshark抓不到包
问题重现
使用TP-link路由器,uboot下用tftp进行升级可重现此问题
排查过程
原因分析
- 抓包分析
因为uboot下TP-link路由器上是抓不到包的,所以我们在交换机上进行抓包进行对比,为了使我们抓的包有对比性,主要抓了下面两个包:
1、在交换机上,uboot下,执行tftp命令进行抓包
2、在交换机上,kernel下,执行tftp命令进行抓包
我们选取这两个包是因为:
1、在交换机上,uboot、kernel下执行tftp命令均可以的;
2、在路由器上,uboot下执行tftp命令不通,kernel下执行tftp命令可以。
这样我们可以分析出kernel下的tftp包和uboot下tftp包的区别。
- 原因一:uboot问题
1、cpsw模块
这个模块主要的功能是初始化硬件,我们对8147芯片的配置和PHY模块(Realtek RTL8201)配置主要在这里执行。
在TCP/IP网络中,数据包通过socket传入到传输层中,再经过网络层最后传入数据链路层,数据链路层通过EMAC的GMII/RGMII/RMII端口进行数据通信,通过MDIO读写PHY寄存器,数据流的传输参考下图:
…
2、eth模块
主要注册一个eth设备,提供eth_write,eth_read,eth_init,eth_halt等操作,这个模块一般屏蔽了硬件的差异,为上层提供统一的API
3、net模块
这个模块主要实现一个简单的协议栈,主要是构建相应的包,例如我们使用的tftp协议,是一个udp包,这里主要就实现了udp包的构建及解析。
4、tftp模块
这个模块主要是应用层的传输数据的协议
这几个模块的具体的关系及调用关系可以参考下图:
…
- 原因二:TP-link路由器问题
1、把tftp包抛弃,包过不了路由器因为在OS下,tftp在TP-link路由器,交换机上均可以通;在Uboot下路由器上不通,交换机上通。所以在OS下抓路由器的包与Uboot下抓交换机的包,进行对比,找出两个包的差别,下图分别是OS下路由器的包,与uboot下交换机的包:
OS下包:
…
Uboot下包:
…
这两个包进行对比可以发现主要有以下不同:
IP地址不同
端口号不同
Checksum不同,OS下做了Checksum的校验,Uboot下没有做checksum的校验
这样我们就大概知道在路由器上不通的原因可能是因为没有做Checksum的计算的缘故
解决方案
基于以上的分析,我们在协议栈构建UDP包的时候做UDP的校验和的计算,然后进行测试,结果在路由器上和交换机上tftp均可使用,至此问题解决。
总结
1、通过我们的分析,可以发现uboot下在TP-link上路由器上不通,主要是因为Uboot下没有做UDP的校验和,而是默认赋值0,也就是不使能udp校验和,UDP协议规定UDP校验和的计算是可选的,这也是在交换机上可以通的原因。
2、但是在TP-link上不通,说明TP-link上的做法是必须进行UDP校验和的计算,否则包就给丢弃,这也是TP-link路由器的一个缺陷。
附件:
CheckSum(ushort *buffer,uint size)
{
ulong cksum = 0;
while(size > 1){
cksum += *buffer++;
size -= sizeof(ushort);
}
if(size){
cksum += *(uchar*)buffer; <span style="white-space: pre;"> </span>//奇数
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);<span style="white-space: pre;"> </span> <span style="white-space: pre;"> </span>//进位
return ~((ushort)cksum);<span style="white-space: pre;"> </span>//取反
}
unsigned
UdpCheckSum(uint saddr,uint daddr, ushort *buffer, uint size)
{
uchar Buf[1024];
typedef struct pseudo_hdr
{
uint src;
uint dst;
uchar mbz;
uchar proto;
ushort len;
}pseudo_hdr;
ulong sum = 0;
pseudo_hdr *ph;
uint ph_len = sizeof(struct pseudo_hdr);
ph = (pseudo_hdr*)Buf;
ph->src = saddr;
ph->dst = daddr;
ph->mbz = 0;
ph->proto = IPPROTO_UDP;
ph->len = htons(size); <span style="white-space: pre;"> </span>//size = Udp header + payload
/*******************************************************************************
buffer = udpheader + payload, size = sizeof(udpheader + payload)
ph_len + size = persudo_udp_head_len + UDP_head_len + payload_len
********************************************************************************/
memcpy(Buf + ph_len, buffer, size);
sum = CheckSum((ushort*)Buf, ph_len + size);
return sum;
}