zfl9 / chinadns-ng

chinadns 重构增强版,支持域名分流、ipset/nftset、UDP/TCP/DoT
GNU Affero General Public License v3.0
1.14k stars 188 forks source link

能否优化下dns查询的分组机制,防止ddos。 #179

Closed hapood closed 4 months ago

hapood commented 4 months ago

当网络状况突然变差时,可能会出现某些APP或者机器疯狂请求DDNS的情况。导致chinadns-ng持续报错too many pending requests,干扰了其他应用的DNS请求。

能否对同一域名的DNS请求做一个单独LIST,而不是把所有的DNS请求放入同一个LIST。

zfl9 commented 4 months ago

能提供下当时的日志吗?我需要从日志分析,是什么情况导致你说的这个错误。

hapood commented 4 months ago

能提供下当时的日志吗?我需要从日志分析,是什么情况导致你说的这个错误。

chinadns.log 这是完整的日志,可以看到很多too many pending requests

zfl9 commented 4 months ago
    456 [server.zig:129 QueryCtx.List.add] too many pending requests: 65536
   1126 [server.zig:391 on_query] dns.check_query(fd:6) failed: invalid query msg
   6646 [Upstream.zig:396 TCP.push_qmsg] too many pending queries: 1000
      4 [Upstream.zig:551 TCP.on_error] recv(tcp://8.8.8.8) failed: (104) Connection reset by peer

简单统计了一下日志,我感觉这个与 #180 是类似的问题,也许可以一并讨论。

如果可以复现,建议开 verbose log,这样就能看到 too many pending requests 之前的日志,得知查询者的 ip:port,查询的域名。

这个看起来确实很像 DDoS 攻击(我猜是某个程序的 Bug,一直在死循环解析某些域名,毕竟内网应该不应该存在 DDoS 攻击),短时间内 chinadns-ng 收到了巨量的 dns query,因为 qctx_list 容量是有限的(65535),所以可能导致正常请求无法得到处理。

invalid query msg 这个错误我估计也是上述 Bug程序 触发的,具体见 #180。

zfl9 commented 4 months ago

能否对同一域名的DNS请求做一个单独LIST,而不是把所有的DNS请求放入同一个LIST。

问题没有这么简单,这种类似“DDoS”的攻击实际上很难防御,且听我道来。


为什么 qctx_list 有容量限制,最多只能持有 65536 个未完成的 dns 查询。

首先需要介绍一些 DNS 知识:

chinadns-ng 与每个 upstream 都是单个“持久的” TCP/UDP 会话,因此 qctx_list 最多可容纳 65536 个未完成的查询,如果 qctx_list 满了,那么新来的 query 就无法得到处理,因为没有 msg-id 可用了。无论你怎么“优化”qctx_list的结构,都受到这个 msg-id 的限制,所以你提议的方法没什么效果。

有人会问,是否可以换个思路:让 chinadns-ng 对于收到的每个 “DNS查询” 都使用一个 “单独的TCP/UDP会话” 与 upstream 进行通信,这样就可以绕过 dns msg-id 的限制了。

这样确实绕过了 msg-id 的限制,但陷入了另一个限制:“五元组”的限制,或者说可同时打开的 socket 数的限制。

zfl9 commented 4 months ago

所以我的想法是,找出这个有问题的程序/主机,看看查询的是什么域名,为什么它会疯狂发DNS查询。从这个方向入手。