解决eCos系统的lwIP存在的bug,该bug导致TCP重发失效,最终导致TCP拒绝服务

news/2024/5/17 19:25:13 标签: ecos, lwip, tcp

问题描述

跑 eCos + lwIP 的设备作为 TCP 服务器,在网络不繁忙不丢包的情况下,一切正常,在网络繁忙会出现丢包的情况下,重试几次后 TCP 拒绝服务(对 SYN 包都不会有任何响应, ping 功能可能正常也可能无响应),其它任务正常。能够承受的重试次数和选项 lwIP networking stack >> Protocols >> TCP support >> Sender pbufs (CYGNUM_LWIP_TCP_SND_QUEUELEN) 有关(据我推测,没有实测)。

抓到的数据包如下:
在这里插入图片描述

  • Time 栏是两个数据包的间隔。
  • 835 号数据包是客户端向服务器发起的请求,跑 eCos + lwIP 的设备作为服务器端。
  • 836 号数据包是服务器在接收到请求后给出的响应。836 号同时还携带了 835 号包的 ACK。
  • 837 号数据包是 835 号数据包的重发包,显然客户端没有收到 836 号数据包,836 号数据包丢失了。客户端既没有收到响应数据,也没有收到 835 号包的 ACK,经过 250 ms 后重发请求数据包,即 837 号数据包。
  • 838 号数据包是服务器告诉客户端已经接收到 837 号包了,不用再重发了,注意这个包只有 ACK 没有响应数据。
  • 10 秒后,客户端没有收到响应,关闭了连接,839 号包 ~ 841 号包是关闭连接三次握手。

看出来了吗?

客户端没有收到服务器的响应,服务器也没有重发!这不是 TCP 啊!TCP 是会重发的,对方没有收到就会重发,但是这里就是没有重发。

解决办法

原因是定时器资源不够,TCP 没有申请到定时器,没法处理任何和超时有关的事物。先给出解决办法,问题分析在后面。有两个办法可以解决这个问题,二选一,第二方法更彻底。

一、在配置工具中增加一个用户定时器

在这里插入图片描述

将上图所示的 Simultaneous active timeouts by user modules 选项值加1。这样 lwIP 就有足够的定时器可用了,TCP 所需要的定时器也能正确分配到。

lwip_netcdl_25">二、修改 lwip_net.cdl

这个办法更加地彻底,从根本上解决了缺少定时器的问题。

关于超时定时器的配置部分(lwip_net.cdl:1342):

        cdl_option CYGNUM_LWIP_MEMP_NUM_CORE_SYS_TIMEOUT {
            display         "Simultaneous active timeouts by core modules"
            flavor          data
            calculated      { CYGPKG_LWIP_TCP + CYGFUN_LWIP_IP_REASSEMBLY +
                              CYGPKG_LWIP_ARP + (CYGPKG_LWIP_DHCP * 2) +
                              CYGPKG_LWIP_AUTOIP + CYGPKG_LWIP_IGMP +
                              CYGPKG_LWIP_DNS + CYGPKG_LWIP_PPP }
            description     "
                The number of simulateously active timeouts used by the lwIP
                core modules."
        }

从这个选项的脚本可以看出,lwIP 所需要的定时器数量是根据所选择的功能自动算出来的,但是少了 lwip_select 所需要的那个超时定时器。

将该脚本改成如下:

        cdl_option CYGNUM_LWIP_MEMP_NUM_CORE_SYS_TIMEOUT {
            display         "Simultaneous active timeouts by core modules"
            flavor          data
            calculated      { CYGPKG_LWIP_TCP + CYGFUN_LWIP_IP_REASSEMBLY +
                              CYGPKG_LWIP_ARP + (CYGPKG_LWIP_DHCP * 2) +
                              CYGPKG_LWIP_AUTOIP + CYGPKG_LWIP_IGMP +
                              CYGPKG_LWIP_DNS + CYGPKG_LWIP_PPP +
                              CYGPKG_LWIP_SOCKET_API }
            description     "
                The number of simulateously active timeouts used by the lwIP
                core modules."
        }

即在选项的计算式中加入 + CYGPKG_LWIP_SOCKET_APIlwip_selectCYGPKG_LWIP_SOCKET_API 组件提供的一个函数。

修复后抓取的包:
在这里插入图片描述

  • 204 号包是客户端发起的请求。
  • 205 号包是我们的设备返回的响应,这个包没有到达客户端,中途丢失了。
  • 206 号包是 204 号包的重发。
  • 207 号包是对 206 号包的确认,客户端接收到这个 ACK 包后,知道服务器已经接收到请求包了,不再重发请求包。
  • 208 号包是对 205 号的重发,我们的设备一直没有收到客户端对 205 号包的 ACK ,因此重发此包。
  • 209 号包是客户端确认接收到了我们的设备返回的响应。
  • 这个处理丢包的流程是正确的。

原因分析

lwIP 有个很好的特性,那就是 Traffic statistics,打开这个特性,lwIP 可以统计数据包的收发以及内存的使用情况。在资源足够的情况下,强烈建议打开该选项。在 eCos 中,这个选项是 lwIP networking stack >> Traffic statistics (CYGPKG_LWIP_STATS)

调试是查找问题的好帮手,一定要留调试接口,一定要掌握调试这么手艺。

将设备连接调试器,连续运行直到故障重现,暂停程序执行,这个时候就可以检查 statistics 了,查看 lwip_tcpip >> current >> src >> core >> stats.c 文件中的 lwip_stats 结构。

检查的结果就是 SYS_TIMEOUT 类型的 LWIP_MEMPOOL 发生了错误,只分配了 6 个,但是实际最多需要 7 个。

给 lwIP 多增加几个定时器资源,再执行就不会出现故障了。

因此可以肯定 TCP 处理超时重发的定时器分配失败了,也就没有 TCP 的超时处理了,也就不会重发了。lwIP 所需要的定时器,大部分都在 tcpip_thread 的开始处申请完了,TCP 的定时器是在有需要的时候调用 tcp_timer_needed 函数申请的。在 eCos 中,lwIP 所需要的定时器个数是自动计算的:

CYGNUM_LWIP_MEMP_NUM_CORE_SYS_TIMEOUT = 
                              CYGPKG_LWIP_TCP + CYGFUN_LWIP_IP_REASSEMBLY +
                             CYGPKG_LWIP_ARP + (CYGPKG_LWIP_DHCP * 2) +
                             CYGPKG_LWIP_AUTOIP + CYGPKG_LWIP_IGMP +
                             CYGPKG_LWIP_DNS + CYGPKG_LWIP_PPP

经检查发现,除了上面引用到的特性会使用定时器外,socket 的 lwip_select 函数也会使用到定时器,因此这里算式少算了一个定时器,问题就出在这。

lwip_select 函数通过调用 sys_sem_wait_timeout 函数间接地使用到了定时器。

我们的程序使用了 lwip_select 函数,而且还带超时,所以触发了这个bug。

缺少 TCP 定时器引起TCP拒绝服务的原因:

  • TCP 发送一个数据包后不会立即释放该数据包的内存资源,因为该数据包可能还需要重发。
  • 网络不丢包的情况下,对方能正确地接收数据包,并返回 ACK 包,lwip 在接收到 ACK 包后释放发送包的内存资源。所以在不丢包的情况下,不会有问题。
  • 网络丢包的情况下,超时定时器会重发没有被 ACK 的数据包,直到接收到 ACK 或发送超时再释放发送包的内存资源,超时定时器未被开启的情况下,发送的数据包丢失以后,因为没有了超时重发,对方永远都接收不到数据包,lwip 也不会接收到该数据包的ACK而释放资源,同样也不会因为发送超时而释放资源(因为定时器没开启,所有超时机制都失效了),因此这个数据包将永远占据着资源而不释放。发生多次这样的情况以后,内存资源被耗尽,lwip 已经申请不到内存来处理新的数据包了,开始出现拒绝服务。
  • lwip 使用的内存资源有多种,看哪种资源首先被耗尽,可能会导致ping不通的情况。

还有更快捷地发现问题的途径:启用断言!

lwIP 的断言设计也是很完善的,完全可以使用断言来捕获资源不够的问题,查看定时器申请的代码(sys_timeout函数):

  timeout = memp_malloc(MEMP_SYS_TIMEOUT);
  if (timeout == NULL) {
    LWIP_ASSERT("sys_timeout: timeout != NULL", timeout != NULL);
    return;
  }

如果启用了断言,当资源分配失败,这里的 LWIP_ASSERT 就被触发了!

但是断言有一点不好,非调试状态下它会引起系统死机或复位,而不仅仅是网络功能失效,对产品的口碑而言,网络功能失效比死机或复位要好一些。

要打开 eCos 中 lwIP 的断言,那么就要打开整个 eCos 的断言。

嗯 …… 下次找问题,先打开断言!


http://www.niftyadmin.cn/n/1107059.html

相关文章

DSL与GPL

一、DSL 与 GPL DSL(Domain-Specified Language 领域特定语言),而与 DSL 相对的就是 GPL,最常见的 DSL 包括 Regex 以及 HTML & CSS 等。 没有计算和执行的概念;其本身并不需要直接表示计算;使用时只需…

微软WGA出错 正版用户无法通过验证(转)

微软WGA出错 正版用户无法通过验证(转)[more]当发现正版软件用户被认为在使用盗版软件时,微软可能遇到了麻烦。问题出在微软的许可工具“Windows正版增值”(WGA)计划上。WGA是微软说服更多用户尽可能地使用正版、恰当地许可的软件战略的关键部…

抓取嵌入式网络设备的以太网数据包

上位机抓包很简单,安装 WireShark。其它嵌入式网络设备怎么抓包?可没法安装WireShark! Hub 买不到 本来可以用 Hub,将被监控设备、计算机接入同一个 Hub,然后在计算机上安装 WireShark 就可以检测到经过 Hub 的所有数…

1.cassandra的搭建

参考: https://blog.csdn.net/ch648966459/article/details/51671276 转载于:https://www.cnblogs.com/caimuqing/p/9185160.html

Spring Boot集成Spring Security

https://www.jianshu.com/p/08cc28921fd0转载于:https://www.cnblogs.com/tanhao/p/10948650.html

微软Vista将采用新的反盗版技术(转)

微软Vista将采用新的反盗版技术(转)[more]  使用者在安装软件后的三十天内未进行授权登录或注册,那么软件功能性就会逐渐减低。微软也针对大量授权版本提出Microsoft Volume Activation 2.0工具,用来防止大量授权被非法使用。 微软在周三(1…

Javascript脱离回调地狱,利用生成器串行处理业务逻辑

这里给出一个例子,这个例子使用 node 作为 web 后台,处理用户添加,添加之前先检查数据库,查看该用户是否已经存在,如果存在汇报错误后直接退出,如果该用户不存在,再次操作数据库插入新用户。 e…

软件版本号的规范

一、版本号命名规范 主版本号 . 子版本号 [. 修正版本号 [. 编译版本号 ]]Major_Version_Number.Minor_Version_Number[.Revision_Number[.Build_Number]]示例 : 1.2.1, 2.0, 5.0.0 build-13124管理策略:项目初版本时,版本号可以为 0.1 或 0.1.0&#xf…