无法访问你试图使用的功能所在的网络位置。是什么原因?
911
2022-05-29
5 问题修复
当一切的分析都站稳了脚跟的时候,修复问题便是水到渠成的事情。
这里的修复,其实主要是两个方面:
5.1 解决链接标准C库的rand函数的问题
在原生的lwip组件代码中,随机数的适配本身就是移植的一部分,很遗憾,在我们出问题的芯片上看到的随机数还是原生态的rand函数。
要想解决掉这个问题,有两个思路:
一个是直接在lwip组件中的arch.h里宏定义LWIP_RAND的地方直接切换成芯片的硬随机数接口xxx_rand,这种是在预编译阶段就完成的;
还有一种就是在链接阶段处理的,把原本链接rand函数直接替换链接成芯片平台的硬随机数接口xxx_rand,gcc编译就一个这样的链接选项支持这样的功能。
为了最大化保持lwip组件的代码完整,我们不想改它一行代码,所以我们采用第二种解决思路,只需要在编译构建的全局链接参数中加上以下参数即可:
# enable hal-rand GLOBAL_LDFLAGS += -Wl,--wrap=rand
同时在hal层的C文件中,添加实现一个叫**__warp_rand**的函数即可。
/* wrap hal TRNG function */ int __wrap_rand(void) { extern int xxx_trng_rand(void); int ret = (int)xxx_trng_rand(); //call TRNG API return ret; }
经以上修改之后,lwip组件中调用的rand函数,最终就会调到xxx_trng_rand接口了,基本就解决了随机数的问题。
5.2 解决芯片硬件随机数不随机的问题
但是,上面的分析部分,我们也提到了,这个芯片居然还出现了硬件随机数不随机的问题,准确说,它获取的不是随机数,而是一个无序的存储序列,这无疑是一个重大bug。
就像这样:
第一次开机:随便获取8个随机数,初步得到 12345 2345 3456 6789 5678 9867 234 567
第二次开机:随便获取8个随机数,还是得到 12345 2345 3456 6789 5678 9867 234 567
第三次开机:随便获取8个随机数,依然得到 12345 2345 3456 6789 5678 9867 234 567
这显然是不随机的,是会出问题的。由于我们没有芯片的datasheet以及不能完全掌握其TRNG的工作原理,我们把问题抛给了原厂,幸运的是,原厂很快给我们打了个小patch。
这个小patch说简单是真的简单,就仅仅是加了一个延时;但是这个延时,代价有点大!!!
以下是伪代码,但足以展示这个代价的威力!
uint32_t xxx_trng_rand(void) { //enable TRNG register //patch here msleep(10); //dealy 10ms //read TRNG register data uint32_t ret = read_TRNG(); //disable TRNG register return ret; }
what???每获取一个随机数都要延时10ms?那我在某次网络通讯中,可能要获取成百上千个随机数啊?这积累的延时简直不能接受啊!
按理说,芯片不能“弱”成这样,也没有这么不合理的设计!看它这个延时,无非的意思就是说,我的TRNG寄存器不是一上电就可以工作的,你得先给它预热下,稍后再来取嘛。
OK,既然你是这样的特性,那么我们可不可以在驱动初始化的时候就给你预热呢?获取随机数的时候就不预热了哇?
试试看,于是有了这样的伪代码:
void sys_driver_init(void) { //normal driver init //special for TRNG warm up //step1. enable TRNG register //step2. msleep(10); //dealy 10ms //step3. disable TRNG register } uint32_t xxx_trng_rand(void) { //enable TRNG register //read TRNG register data uint32_t ret = read_TRNG(); //disable TRNG register return ret; }
这样代码一测试,完美!至少不需要每次获取随机数都dealy啊!其实在写这段热身代码的时候,也踩了些坑的,比如没有先enable TRNG寄存器就去delay,这无疑是delay了个寂寞啊?
至少,所以需要修正的代码已经修正完成。值得注意的是,我们没有改一行应用层及组件层的代码,那么修复后的情况究竟如何,下一章节我们来验证验证。
6 问题验证
6.1 随机数的问题验证
这里的验证,其实是要一层层来验证,由于问题的根源在于随机数的不随机导致,那么我们有限要验证的应该是芯片TRNG的随机性。
幸运的是,通过第5部分的patch代码,我们有效地看到了TRNG的随机性基本满足了我们的要求,我们的验证方法很简单,就是开机完成初始化之后就获取一组随机数,然后就重启;不断地测试,观察1000左右的数据。
从1000的数据,初步是可以看出去随机性的,但如果需要过随机数认证的话,还得使用NIST专门的测试工具做更进一步的验证测试,这里就不展开论述了,有兴趣的可以自行去了解下,像金融领域的PCI安全认证,随机数的测试是非常关键的一环。
6.2 偶发的网络掉线问题验证
这个验证就要回到issue本身了,虽然我们在上面的分析阶段,其实也做了部分边分析边修正边验证的工作,但上面的场景更加侧重的是在不清楚稳定的复现路径的情况下不停地试错。
所以,回归验证这个issue还是需要根据稳定的复现路径,做一些控制变量来单项验证,比如每次重启后就固定使用12345端口发起MQTT链接,观察其复现情况;恢复正常修复后的随机端口号,观测其情况。
同时,还得把压测环境搭建好,同步进行压力测试,观测其情况。
只有以上几点都通过验证后,我们才有扎实的信心说:“这个issue可以close了”!
7 经验总结
当你对一个网络问题靠逻辑思考解决不了的时候,第一要想到的方法就是抓包分析;
抓包分析有方法,优先排查上层网络协议的报文,比如TCP/TLS等;当上层协议包分析不出问题的时候,尝试抓空口包;
TCP链接的状态切换是所有基于TCP/IP协议的网络通讯的基础,重点分析它有助于打开你的分析思路;
所有偶现的问题,一定有复现路劲;如果你还没出现,仅仅是你的测试次数还不够多;
嵌入式里面的C库,往往不是标准的,不能太轻易相对传统意义上的C标准接口,多持怀疑的态度;
随机数的随机性在网络通讯中非常重要,当你的网络通讯超出你的想象的时候,不妨想想随机数的可能性;
lwip协议栈的实现在嵌入式设备中太常用了,关于它的移植不能简单的”拿来主义“,需要系统地、全面地了解其工作原理、代码架构和适配的基本工作,了解其可能出问题的点和常规的解决方法;
技术偷懒使不得,技术债迟早都是要还的;
疑难bug的解决要及时总结和复盘,形成一定的分析方法论,指不定哪天就帮助你解决其他相关的疑难问题。
8 参考链接
在排查过程,我参考了一些辅助资料文档,也请教了一些网路大神,这里不一一列举,仅把一些极具参考价值的文档链接归放在这里,以表感谢,感兴趣的可以自行研究研究。
基于RT-Thread 使用 wireshark 抓取 HTTPS 数据包
C++的RAND函数生成的值为什么存在严重的不随机性?
lwip协议栈的init分析
lwip的实现梳理
TCP状态转换图分析
TCP标志位详解
已处于链接状态的TCP链路收到SYN报文,服务器会怎么样?
图解TLS握手链接
MQTT-v3.1.1规范英文版本
MQTT-v3.1.1规范中文版本
9 更多分享
欢迎关注我的github仓库01workstation,日常分享一些开发笔记和项目实战,欢迎指正问题。
同时也非常欢迎关注我的CSDN主页和专栏:
【CSDN主页:架构师李肯】
【RT-Thread主页:架构师李肯】
【C/C++语言编程专栏】
【GCC专栏】
【信息安全专栏】
【RT-Thread开发笔记】
【freeRTOS开发笔记】
【BLE蓝牙开发笔记】
【ARM开发笔记】
【RISC-V开发笔记】
有问题的话,可以跟我讨论,知无不答,谢谢大家。
单片机 网络
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。