百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

iptables -m connlimit导致内存不足

gudong366 2025-07-24 20:51 7 浏览

题描述:

Udp 高频攻击导致slab kmalloc-64 持续申请,导致内存不足。A7低版本内核无该问题,MA35/AM62在kernel6版本上也无该问题,此问题只出在A7 kernel6上。 问题环境(kernel6.6) iptables在不同环境下的版本相同

[root@evse ~]# uname -a
Linux evse 6.6.90-gf2b2a1246566 #1 SMP PREEMPT Wed Jun 4 15:06:07 CST 2025
armv7l GNU/Linux
[root@evse ~]# cat /proc/cpuinfo
processor : 0
model name : ARMv7 Processor rev 5 (v7l)
BogoMIPS : 64.00
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva
idivt vfpd32 lpae
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x0
CPU part : 0xc07
CPU revision : 5
Hardware : Freescale i.MX6 Ultralite (Device Tree)
Revision : 0000
Serial : d9656bca271e39d4
[root@evse ~]#

2. 测试命令

删除规则与查看规则:

iptables -D INPUT 1
iptables -L INPUT --line-numbers

两条问题规则(-m connlimit ):

iptables -A INPUT -p udp -m connlimit --connlimit-above 60 --connlimit-mask 32 -m limit --limit
2/min -j LOG --log-prefix "UDP Flood Limit Exceeded: "
 
iptables -A INPUT -p udp -m connlimit --connlimit-above 60 --connlimit-mask 32 -j DROP

虚拟机对目标机使用不同间隔进行攻击:

sudo hping3 --udp -s 6666 -p 443 --flood 192.168.88.206 kmalloc-64 持续增加
sudo hping3 --udp -s 6666 -p 443 -i u2000 192.168.88.206 kmalloc-64 不持续增加
sudo hping3 --udp -s 6666 -p 443 -i u200 192.168.88.206 kmalloc-64 不持续增加
sudo hping3 --udp -s 6666 -p 443 -i u20 192.168.88.206 kmalloc-64 持续增加 ,删除规则后,kmalloc-64恢复原来较低数值。若此时不删除规则,并且停止-i u20 攻击, kmalloc-64 会一直保持原高值不变,若此时再使用-i u200 进行攻击,kmalloc-64 会在原来高值情况下慢慢减小,最终回到内存正常状态。

通过如下命令可查看有问题时,可用内存下降, kmalloc-64 会持续增加

watch -n1 cat /proc/meminfo |grep "MemFree"
watch -n1 "awk 'NR==1; NR>1 {print | \"sort -k2 -nr | head -n 11\"}' /proc/slabinfo"
watch -n1 iptables -L -vn

3. kernel4.14.98 (无问题)

此内核无该类问题,规则生效,但内存不会增长。

注意:该环境iptables两条udp规则和洪涝攻击并不会导致内存泄露,但是产品线S83中的preconf会导致空闲内存越来越少。(因固件包过老,暂不查产品线的问题)

4. kernel6.6 (有问题)

4.1 kmalloc-64 内存异常

  • 第一列: slab 缓存的名称,
  • 第二列(active_objs) :当前活跃的对象数量,
  • 第三列(num_objs) :缓存中总对象数量(包括活跃和非活跃),
  • 第四列(objsize):每个对象的大小(字节),
  • 第五列(objperslab) :每个 slab 页能存放的对象数量,
  • 第六列(pagesperslab) :每个 slab 占用的内存页数, 通常为 1。

tunables 部分可调参数(通常为 0,表示使用默认值)slabdata 部分 o 第一个值:活跃的 slab 页数量。(因每个slab占用内存物理页为1个,也可认为此值是该slab的个 数) o 第二个值:总 slab 页数量。 o 第三个值:共享 slab 页数量 总对象个数=slab组数每组对象 如 82176=1284

64 其中81887为当前活跃的对象

slab内存回收是以slab单个节点来回收,一个slab控制器包含多个obj,只有所有obj都非活跃状态,slab节点才会被系统回收。所有只有释放obj,后续才有slab内存回收。

当删除规则,kmalloc-64立即回到原先正常大小。或者在不删除规则的环境下,此时再降速攻击,kmalloc-64便会慢慢释放,最终变回原先大小:

4.2 优化系统参数(缩减超时时间):无效

echo 300 > /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
echo 1 > /proc/sys/net/netfilter/nf_conntrack_tcp_be_liberal
echo 1 > /proc/sys/net/netfilter/nf_conntrack_generic_timeout
echo 5 > /proc/sys/net/netfilter/nf_conntrack_udp_timeout
echo 1 > /proc/sys/net/netfilter/nf_conntrack_icmp_timeout

4.3 内核调整:有效

CONFIG_HZ_1000 CONFIG_HZ=1000 ----- 在答疑Q2中详细说明原因
CONNCOUNT_GC_MAX_NODES 从8设置成64 ----- 增加单次释放节点的个数,查找资料64适合高并
发的攻击环境
if ((u32)jiffies == list->last_gc) 改成 if ((u32)jiffies == READ_ONCE(list->last_gc)) -------以保读取最新
值,

禁止编译器优化为重复读取或重排指令

单核设备应配置CONFIG_NR_CPUS=1 ,并关闭CONFIG_SMP 以匹配硬件 -----a7所有配置都应做此调整

4.4 代码追踪

iptables -A INPUT -p udp -m connlimit --connlimit-above 60 --connlimit-mask 32 -j DROP 

规则会 触发 connlimit_mt_check -> nf_conncount_init insert_tree

iptables -D INPUT 1删除上述规则,会触发connlimit_mt_destroy -> nf_conncount_destroy ->destroy_tree

使用sudo hping3 --udp -s 6666 -p 443 -i u2000000 192.168.88.206进行攻击: 因间隔为2秒,非高频攻击状态,无包被DROP, 此时无__nf_conncount_add 和conn_free 函数的 执行。

4.4.1 节点创建:

__nf_conncount_add -> kmem_cache_alloc(conncount_conn_cachep, GFP_ATOMIC) (此函数便是分配slab的缓存对象)

4.4.2 节点释放:

find_or_evict 用来查找过期节点,并将其释放。


  • 在添加节点时释放
__nf_conncount_add -> find_or_evict -> conn_free ->
kmem_cache_free(conncount_conn_cachep, conn) 

(此函数便是释放slab的缓存对象)


  • 在其他处释放
connlimit_mt ->
nf_conncount_count ->
count_tree -> nf_conncount_gc_list -> find_or_evict
   -> insert_tree -> nf_conncount_gc_list -> find_or_evict
        - > schedule_gc_worker -> schedule_gc_worker -> schedule_work

schedule_work 触发工作队列去执行 tree_gc_worker -> nf_conncount_gc_list -> find_or_evict

connlimit_mt 主要用于限制单个 IP 的连接数

nf_conncount_count 统计当前键值对应的连接数

4.5 攻击测试

4.5.1 使用sudo hping3 --udp -s 6666 -p 443 -i u200000 192.168.88.206 进行

攻击6秒左右:

Welcome to EVSE
evse login: [ 106.484184] **[connlimit_mt_check:95]**
[ 106.488585] **[nf_conncount_init:539]**
[ 110.288686] **[insert_tree:366]**
Welcome to EVSE
evse login:
Welcome to EVSE
evse login:
Welcome to EVSE
evse login:
Welcome to EVSE
evse login: [ 132.168009] **[insert_tree:366]**
[ 132.377315] **[__nf_conncount_add:186]**
[ 132.588389] **[__nf_conncount_add:186]**
[ 132.792491] **[__nf_conncount_add:186]**
[ 132.992824] **[__nf_conncount_add:186]**
[ 133.196611] **[__nf_conncount_add:186]**
[ 133.398087] **[__nf_conncount_add:186]**
[ 133.602361] **[__nf_conncount_add:186]**
[ 133.810499] **[__nf_conncount_add:186]**
[ 134.013903] **[__nf_conncount_add:186]**
[ 134.215048] **[__nf_conncount_add:186]**
[ 134.420325] **[__nf_conncount_add:186]**
[ 134.625043] **[__nf_conncount_add:186]**
[ 134.825720] **[__nf_conncount_add:186]**
[ 135.028171] **[__nf_conncount_add:186]**
[ 135.230085] **[__nf_conncount_add:186]**
[ 135.430352] **[__nf_conncount_add:186]**
[ 135.631057] **[__nf_conncount_add:186]**
[ 135.835375] **[__nf_conncount_add:186]**
[ 136.044689] **[__nf_conncount_add:186]**
[ 136.249517] **[__nf_conncount_add:186]**
[ 136.449621] **[__nf_conncount_add:186]**
[ 136.652949] **[__nf_conncount_add:186]**

4.5.2 上一步前提下,过一段时间再使用sudo hping3 --udp -s 6666 -p 443 -i u200000 192.168.88.206 进行攻击2秒左右,从打印可看到先释放原先创建的节点:

[ 246.340577] **[conn_free:92]**
[ 246.343988] **[conn_free:92]**
[ 246.347228] **[conn_free:92]**
[ 246.350448] **[conn_free:92]**
[ 246.353663] **[conn_free:92]**
[ 246.356968] **[conn_free:92]**
[ 246.360190] **[conn_free:92]**
[ 246.363408] **[conn_free:92]**
[ 246.366622] **[conn_free:92]**
[ 246.369831] **[__nf_conncount_add:186]**
[ 246.544568] **[conn_free:92]**
[ 246.547849] **[conn_free:92]**
[ 246.551030] **[conn_free:92]**
[ 246.554201] **[conn_free:92]**
[ 246.557367] **[conn_free:92]**
[ 246.560535] **[conn_free:92]**
[ 246.563701] **[conn_free:92]**
[ 246.566866] **[conn_free:92]**
[ 246.570031] **[conn_free:92]**
[ 246.573193] **[__nf_conncount_add:186]**
[ 246.749883] **[conn_free:92]**
[ 246.753196] **[conn_free:92]**
[ 246.756645] **[conn_free:92]**
[ 246.759876] **[conn_free:92]**
[ 246.763095] **[conn_free:92]**
[ 246.766407] **[__nf_conncount_add:186]**
[ 246.961836] **[__nf_conncount_add:186]**
[ 247.162273] **[__nf_conncount_add:186]**
[ 247.362697] **[__nf_conncount_add:186]**
[ 247.563052] **[__nf_conncount_add:186]**

4.5.3 断电重启后,使用sudo hping3 --udp -s 6666 -p 443 -i u200000 192.168.88.206 进行攻击30秒左右

可以看到先连续创建前60个(200ms一帧,12秒正好60个),期间没有conn_free,这是因为之前**-- connlimit-above 60** 参数的原因,可以看到后期每增加一个节点,便会释放一个节点。内核的nf_conncount 模块会优先释放最早创建的连接 (FIFO策略)。

Welcome to EVSE
evse login: [ 6588.953200] **[connlimit_mt_check:95]**
[ 6588.964224] **[nf_conncount_init:539]**
Welcome to EVSE
evse login: [ 6623.889501] **[insert_tree:366]**
[ 6624.092922] **[__nf_conncount_add:186]**
[ 6624.293990] **[__nf_conncount_add:186]**
[ 6624.496559] **[__nf_conncount_add:186]**
[ 6624.704156] **[__nf_conncount_add:186]**
[ 6624.905262] **[__nf_conncount_add:186]**
[ 6625.117401] **[__nf_conncount_add:186]**
[ 6625.332664] **[__nf_conncount_add:186]**
[ 6625.534785] **[__nf_conncount_add:186]**
[ 6625.735500] **[__nf_conncount_add:186]**
[ 6625.936776] **[__nf_conncount_add:186]**
[ 6626.142550] **[__nf_conncount_add:186]**
[ 6626.464095] **[__nf_conncount_add:186]**
[ 6626.672264] **[__nf_conncount_add:186]**
[ 6626.876006] **[__nf_conncount_add:186]**
[ 6627.080153] **[__nf_conncount_add:186]**
[ 6627.280599] **[__nf_conncount_add:186]**
[ 6627.485330] **[__nf_conncount_add:186]**
[ 6627.688502] **[__nf_conncount_add:186]**
[ 6627.890940] **[__nf_conncount_add:186]**
[ 6628.093806] **[__nf_conncount_add:186]**
[ 6628.313874] **[__nf_conncount_add:186]**
[ 6628.515991] **[__nf_conncount_add:186]**
[ 6628.720403] **[__nf_conncount_add:186]**
[ 6628.924391] **[__nf_conncount_add:186]**
[ 6629.126770] **[__nf_conncount_add:186]**
[ 6629.330207] **[__nf_conncount_add:186]**
[ 6629.533838] **[__nf_conncount_add:186]**
[ 6629.734090] **[__nf_conncount_add:186]**
[ 6629.934281] **[__nf_conncount_add:186]**
[ 6630.135378] **[__nf_conncount_add:186]**
[ 6630.335804] **[__nf_conncount_add:186]**
[ 6630.537249] **[__nf_conncount_add:186]**
[ 6630.740110] **[__nf_conncount_add:186]**
[ 6630.942853] **[__nf_conncount_add:186]**
[ 6631.143030] **[__nf_conncount_add:186]**
[ 6631.346782] **[__nf_conncount_add:186]**
[ 6631.547754] **[__nf_conncount_add:186]**
[ 6631.747852] **[__nf_conncount_add:186]**
[ 6631.948470] **[__nf_conncount_add:186]**
[ 6632.152779] **[__nf_conncount_add:186]**
[ 6632.364964] **[__nf_conncount_add:186]**
[ 6632.576077] **[__nf_conncount_add:186]**
[ 6632.796017] **[__nf_conncount_add:186]**
[ 6632.997078] **[__nf_conncount_add:186]**
[ 6633.203338] **[__nf_conncount_add:186]**
[ 6633.406403] **[__nf_conncount_add:186]**
[ 6633.607636] **[__nf_conncount_add:186]**
[ 6633.812599] **[__nf_conncount_add:186]**
[ 6634.024786] **[__nf_conncount_add:186]**
[ 6634.225224] **[__nf_conncount_add:186]**
[ 6634.427251] **[__nf_conncount_add:186]**
[ 6634.642583] **[__nf_conncount_add:186]**
[ 6634.842924] **[__nf_conncount_add:186]**
[ 6635.042996] **[__nf_conncount_add:186]**
[ 6635.243920] **[__nf_conncount_add:186]**
[ 6635.461265] **[__nf_conncount_add:186]**
[ 6635.670469] **[__nf_conncount_add:186]**
[ 6635.871427] **[__nf_conncount_add:186]**
[ 6636.079939] **[__nf_conncount_add:186]**
[ 6636.280148] **[__nf_conncount_add:186]**
[ 6636.481285] **[conn_free:92]**
[ 6636.484515] **[__nf_conncount_add:186]**
[ 6636.682712] **[conn_free:92]**
[ 6636.686019] **[__nf_conncount_add:186]**
[ 6636.883322] **[conn_free:92]**
[ 6636.886627] **[__nf_conncount_add:186]**
[ 6637.093071] **[conn_free:92]**
[ 6637.096471] **[__nf_conncount_add:186]**
[ 6637.308856] **[conn_free:92]**
[ 6637.312146] **[__nf_conncount_add:186]**
[ 6637.509549] **[conn_free:92]**
[ 6637.512862] **[__nf_conncount_add:186]**
[ 6637.715686] **[conn_free:92]**
[ 6637.718980] **[__nf_conncount_add:186]**
[ 6637.918133] **[conn_free:92]**
[ 6637.921462] **[__nf_conncount_add:186]**
[ 6638.122847] **[conn_free:92]**
[ 6638.126249] **[__nf_conncount_add:186]**
[ 6638.346137] **[conn_free:92]**
 
[ 6638.349367] **[__nf_conncount_add:186]**
[ 6638.562855] **[conn_free:92]**
[ 6638.566084] **[__nf_conncount_add:186]**

4.5.4 若使用sudo hping3 --udp -s 6666 -p 443 -i u20 192.168.88.206 进行高频攻击

我们可以看到创建节点速度要明显快于释放。Kernel 6.6对连接跟踪模块进行了重构,新增了更严格的连接状态检查机制,导致处理每个连接需要更多CPU周期。

创建快是因为仅需分配内存,而释放需完成哈希表操作、状态清理等耗时步骤。

[10060.558838] **[__nf_conncount_add:186]**
[10060.558854] **[__nf_conncount_add:186]**
[10060.558873] **[__nf_conncount_add:186]**
[10060.558889] **[__nf_conncount_add:186]**
[10060.558907] **[__nf_conncount_add:186]**
[10060.559033] **[__nf_conncount_add:186]**
[10060.559058] **[__nf_conncount_add:186]**
[10060.559081] **[__nf_conncount_add:186]**
[10060.559098] **[__nf_conncount_add:186]**
[10060.559116] **[__nf_conncount_add:186]**
[10060.559134] **[__nf_conncount_add:186]**
[10060.559152] **[__nf_conncount_add:186]**
[10060.559170] **[__nf_conncount_add:186]**
[10060.559294] **[__nf_conncount_add:186]**
[10060.559318] **[__nf_conncount_add:186]**
[10060.559342] **[__nf_conncount_add:186]**
[10060.559360] **[__nf_conncount_add:186]**
[10060.559380] **[__nf_conncount_add:186]**
[10060.559399] **[__nf_conncount_add:186]**
[10060.559417] **[__nf_conncount_add:186]**
[10060.559436] **[__nf_conncount_add:186]**
[10060.559562] **[__nf_conncount_add:186]**
[10060.559587] **[__nf_conncount_add:186]**
[10060.559607] **[__nf_conncount_add:186]**
[10060.559624] **[__nf_conncount_add:186]**
[10060.559642] **[__nf_conncount_add:186]**
[10060.559660] **[__nf_conncount_add:186]**
[10060.559678] **[__nf_conncount_add:186]**
[10060.559696] **[__nf_conncount_add:186]**
[10060.559833] **[__nf_conncount_add:186]**
[10060.559861] **[__nf_conncount_add:186]**
[10060.559880] **[__nf_conncount_add:186]**
[10060.559899] **[__nf_conncount_add:186]**
[10060.559918] **[__nf_conncount_add:186]**
[10060.559936] **[__nf_conncount_add:186]**
[10060.559954] **[__nf_conncount_add:186]**
[10060.559972] **[__nf_conncount_add:186]**
[10060.560100] **[__nf_conncount_add:186]**
[10060.560125] **[__nf_conncount_add:186]**
[10060.560147] **[__nf_conncount_add:186]**
[10060.560164] **[__nf_conncount_add:186]**
[10060.560182] **[__nf_conncount_add:186]**
[10060.560201] **[__nf_conncount_add:186]**
[10060.560219] **[__nf_conncount_add:186]**
[10060.560239] **[__nf_conncount_add:186]**
[10060.560371] **[__nf_conncount_add:186]**
[10060.560394] **[__nf_conncount_add:186]**
[10060.560413] **[__nf_conncount_add:186]**
[10060.560429] **[__nf_conncount_add:186]**
[10060.560445] **[__nf_conncount_add:186]**
[10060.560482] **[__nf_conncount_add:186]**
[10060.560500] **[__nf_conncount_add:186]**
[10060.560518] **[__nf_conncount_add:186]**
[10060.560646] **[__nf_conncount_add:186]**
[10060.560671] **[__nf_conncount_add:186]**
[10060.560688] **[__nf_conncount_add:186]**
[10060.560705] **[__nf_conncount_add:186]**
[10060.560842] **[conn_free:92]**
[10060.560870] **[conn_free:92]**
[10060.560883] **[conn_free:92]**
[10060.560895] **[conn_free:92]**
[10060.560906] **[conn_free:92]**
[10060.560917] **[conn_free:92]**
[10060.560929] **[conn_free:92]**
[10060.560940] **[conn_free:92]**
[10060.560952] **[conn_free:92]**
[10060.560961] **[__nf_conncount_add:186]**
[10060.560982] **[__nf_conncount_add:186]**
[10060.561001] **[__nf_conncount_add:186]**
[10060.561018] **[__nf_conncount_add:186]**
[10060.561171] **[__nf_conncount_add:186]**
[10060.561201] **[__nf_conncount_add:186]**
[10060.561222] **[__nf_conncount_add:186]**
[10060.561240] **[__nf_conncount_add:186]**
[10060.561257] **[__nf_conncount_add:186]**
[10060.561274] **[__nf_conncount_add:186]**
[10060.561290] **[__nf_conncount_add:186]**
[10060.561307] **[__nf_conncount_add:186]**
[10060.561439] **[__nf_conncount_add:186]**
 
[10060.561463] **[__nf_conncount_add:186]**
[10060.561480] **[__nf_conncount_add:186]**
[10060.561497] **[__nf_conncount_add:186]**
[10060.561513] **[__nf_conncount_add:186]**
[10060.561531] **[__nf_conncount_add:186]**
[10060.561547] **[__nf_conncount_add:186]**
[10060.561563] **[__nf_conncount_add:186]**
[10060.561688] **[__nf_conncount_add:186]**
[10060 561712] **[ f t dd:186]**

5 答疑

Q1: 从下面的代码可以看到slab描述符的名称应为nf_conncount_tuple,为何在proc/slabinfo中没有?而出现增长的slab名称确是malloc-64?

conncount_conn_cachep = kmem_cache_create("nf_conncount_tuple",
sizeof(struct nf_conncount_tuple),
0, 0, NULL);

slab描述符nf_conncount_tuple未在/proc/slabinfo显示,因为对象尺寸与通用缓存匹配且未强制独 立分配,内核会复用 malloc-64 缓存,所以在高频攻击下是malloc-64持续增加。

Q2: 为何Udp 高频攻击导致slab kmalloc-64 持续申请,导致内存不足?

高频攻击,有一个新连接便生成一个节点(add_new_node),就会申请一个slab对象(kmem_cache_alloc) 。 其中find_or_evict函数是用来释放无效过期节点,注意if ((u32)jiffies == READ_ONCE(list->last_gc))的判断逻辑,A7原CONFIG_HZ=100,也就是一个jiffies的单位就是10ms,当在这10ms时间内,这个函数直接到add_new_node出申请对象,而调过了释放无效过期的逻辑。当高频下(比如u20),10ms内就有500个节点要连续申请。

在下一个10ms才会到find_or_evict的释放逻辑,这样内存申请过于密集。

find_or_evict的释放逻辑还有注意点,每次它只释放9个节点,当释放完9个后,find_or_evict函数就返回。

这样就可知道高频攻击下,因节点在短时间创建过多节点,而一次释放最多9个,随着时间的积累,内存会逐渐被kmalloc-64所消耗。

所以kernel6需做如下调整:CONFIG_HZ_1000 CONFIG_HZ=1000 CONNCOUNT_GC_MAX_NODES从8设置成64(64值适合高并发环境)。 (后期其他平台用非rt内核kernel6,也需关注此处)

static int __nf_conncount_add(struct net *net,
struct nf_conncount_list *list,
const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_zone *zone)
{
 const struct nf_conntrack_tuple_hash *found;
 struct nf_conncount_tuple *conn, *conn_n;
 struct nf_conn *found_ct;
 unsigned int collect = 0;
 if ((u32)jiffies == READ_ONCE(list->last_gc))
  goto add_new_node;
  /* check the saved connections */
  list_for_each_entry_safe(conn, conn_n, &list->head, node) {
   if (collect > CONNCOUNT_GC_MAX_NODES)
    break;
   found = find_or_evict(net, list, conn);
   if (IS_ERR(found)) {
    /* Not found, but might be about to be confirmed */
    if (PTR_ERR(found) == -EAGAIN) {
     if (nf_ct_tuple_equal(&conn->tuple, tuple) &&
     nf_ct_zone_id(&conn->zone, conn->zone.dir)
     ==
     nf_ct_zone_id(zone, zone->dir))
    return 0; /* already exists */
   } else {
    collect++;
   }
  continue;
  }
  found_ct = nf_ct_tuplehash_to_ctrack(found);
  
  if (nf_ct_tuple_equal(&conn->tuple, tuple) &&
  nf_ct_zone_equal(found_ct, zone, zone->dir)) {
   /*
   * We should not see tuples twice unless someone hooks
   * this into a table without "-p tcp --syn".
   *
   * Attempt to avoid a re-add in this case.
   */
   nf_ct_put(found_ct);
   return 0;
  } else if (already_closed(found_ct)) {
   /*
   
   * we do not care about connections which are
   * closed already -> ditch it
   */
   nf_ct_put(found_ct);
   conn_free(list, conn);
   collect++;
   continue;
  }
  nf_ct_put(found_ct);
 }
 add_new_node:
 if (WARN_ON_ONCE(list->count > INT_MAX))
  return -EOVERFLOW;
 conn = kmem_cache_alloc(conncount_conn_cachep, GFP_ATOMIC);
 if (conn == NULL)
  return -ENOMEM;
 conn->tuple = *tuple;
 conn->zone = *zone;
 conn->cpu = raw_smp_processor_id();
 conn->jiffies32 = (u32)jiffies;
 list_add_tail(&conn->node, &list->head);
 list->count++;
 list->last_gc = (u32)jiffies;
 return 0;
}

Q3:删除规则后,kmalloc-64恢复原来较低数值?

iptables -D INPUT 1删除规则,会触发connlimit_mt_destroy -> nf_conncount_destroy ->destroy_tree -> kmem_cache_free ,便会把所用的slab对象全部释放,所以kmalloc-64恢复原来较低 数值。

Q4:若此时不删除规则,暂停攻击, kmalloc-64 会一直保持原高值不变?

find_or_evict函数是用来释放无效过期节点,虽然find_or_evict除了在我们介绍的 __nf_conncount_add函数里被调用,还会在其他几个函数中被调用。

但是通过日志追踪,实际find_or_evict只在__nf_conncount_add中执行。那么只有新节点过来,才会进入find_or_evict函数来释放内存,所以暂停攻击,kmalloc-64 就会一直保持原高值不变。

Q5:若此时再使用低频进行攻击, kmalloc-64 会在原来高值情况下慢慢减小,最终回到内存正常状态?

当低频攻击时,__nf_conncount_add的在10ms内直接add_new_node就会少,同时find_or_evict函数一次最多可以释放9个对象。这样释放节点数快于新节点的创建,所以kmalloc-64 会在原来高值情况下慢慢减小,最终会回到内存正常状态。

Q6:find_or_evict会在多个函数被调用,为何实际只在__nf_conncount_add中执行?如果其他函数也会调用,这样释放节点就加快了,也可以避免该问题。

之前过分析的代码:

connlimit_mt ->
nf_conncount_count ->
count_tree -> nf_conncount_gc_list -> find_or_evict
 -> insert_tree -> nf_conncount_gc_list -> find_or_evict
  - > schedule_gc_worker -> schedule_gc_worker -> schedule_work

schedule_work 触发工作队列去执行tree_gc_worker -> nf_conncount_gc_list -> find_or_evict

首先对Q4的补充: connlimit_mt 和 __nf_conncount_add 均包含节点释放逻辑,但两者的触发条件均依赖数据包的主动进入。 当高频攻击停止且无新数据包进入时,由于缺乏触发条件,已分配的 kmalloc-64 内存(存储连接计数节点)将无法释放,导致内存占用维持高位。

static unsigned int
count_tree(struct net *net,

struct nf_conncount_data *data,
const u32 *key,
const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_zone *zone)
{
 struct rb_root *root;
 struct rb_node *parent;
 struct nf_conncount_rb *rbconn;
 unsigned int hash;
 printk("**[%s:%d]** \n", __func__, __LINE__);
 hash = jhash2(key, data->keylen, conncount_rnd) % CONNCOUNT_SLOTS;
 root = &data->root[hash];
 parent = rcu_dereference_raw(root->rb_node);
 while (parent) {
  int diff;
  rbconn = rb_entry(parent, struct nf_conncount_rb, node);
  diff = key_diff(key, rbconn->key, data->keylen);
  if (diff < 0) {
   parent = rcu_dereference_raw(parent->rb_left);
  } else if (diff > 0) {
   parent = rcu_dereference_raw(parent->rb_right);
  } else {
   int ret;
   printk("**[%s:%d]** \n", __func__, __LINE__);
   if (!tuple) {
    printk("**[%s:%d]** \n", __func__, __LINE__);
    nf_conncount_gc_list(net, &rbconn->list);
    return rbconn->list.count;
   }
   spin_lock_bh(&rbconn->list.list_lock);
   /* Node might be about to be free'd.
   * We need to defer to insert_tree() in this case.
   */
   if (rbconn->list.count == 0) {
    spin_unlock_bh(&rbconn->list.list_lock);
    break;
   }
   /* same source network -> be counted! */
   ret = __nf_conncount_add(net, &rbconn->list, tuple,
   zone);
   spin_unlock_bh(&rbconn->list.list_lock);
   if (ret)
    return 0; /* hotdrop */
   else
    return rbconn->list.count;
   }
 }
 printk("**[%s:%d]** \n", __func__, __LINE__);
 if (!tuple)
  return 0;
 printk("**[%s:%d]** \n", __func__, __LINE__);
 return insert_tree(net, data, root, hash, key, tuple, zone);
}

count_tree 为何没进入上述释放节点的逻辑:


  • 因调用时始终传入有效的 tuple 参数,跳过了直接调用 nf_conncount_gc_list 的路径。

  • 进入__nf_conncount_add逻辑后,直接返回,不再进入 insert_tree 函数调用,后续的释放节点都走不到。

相关推荐

一文讲清怎么利用Python实现Linux系统日志检索分析管理系统

摘要:在现代IT运营与开发中,日志分析早已成为不可或缺的核心环节。无论是排查系统故障、进行安全审计,还是优化服务性能,日志文件始终是最真实、最权威的信息来源。Linux系统作为主流的服务器操作系统,其...

Linux 思维导图整理(建议收藏)(linux知识点总结思维导图)

今天整理了一下收集的Linux思维导图。Linux学习路径Linux桌面介绍FHS:文件系统目录标准Linux需要特别注意的目录Linux内核学习路线地址:https://www.jiansh...

什么是操作系统(什么叫做操作系统)

Linux也是众多操作系统之一,要想知道Linux是什么,首先得说一说什么是操作系统。计算机是一台机器,它按照用户的要求接收信息、存储数据、处理数据,然后再将处理结果输出(文字、图片、音频、视频...

Windows操作系统和Linux操作系统有什么不同?

每天一分钟,关注我学更多今天的内容是Windows操作系统和Linux操作系统在多个方面存在显著差异,主要体现在用户界面、开源性、稳定性和安全性等方面。用户界面:Windows操作系统提供直观高效的图...

每日学习“IT”是什么呢?(it学习网站)

IT是信息技术(InformationTechnology)的简称,它是一个广泛的领域,涉及到利用计算机、网络通信技术、软件等来存储、处理、传输和获取信息。计算机硬件硬件组成部分:包括中央处理器(...

CAD是什么?如何选择最适合你的CAD软件?

CAD(计算机辅助设计)是建筑、机械、电子等行业的核心工具,通过数字化手段实现高精度设计与协作。传统CAD软件如AutoCAD功能强大,但操作复杂、成本高昂,而轻量化工具又难以满足专业需求。元图CAD...

Linux是什么?(linux是什么意思)

在今天的时代,计算机系统已经成为了我们生活中不可或缺的一部分。而Linux则是目前世界上最为流行和免费的操作系统之一,它以其自由和开放源代码的特点,吸引了全球大量的程序员和开发者前来使用和修改。那么,...

牛人带你通透理解高可用网络基础架构的关键组件:负载均衡机制

上篇给大家介绍的内容是微服务网关:Zuul源码解析,相信大家已经领会并且贯通了;那么本文将给大家介绍的内容是负载均衡:负载均衡机制。负载均衡负载均衡(LoadBalance)是分布式网络环境中的重要...

Linux运维网络篇(linux运维网站)

Linux运维过程中,我们会遇到各种形形色色的网络问题,今天我们就常见的检测手段以及处理办法给大家做一下统一分享。第一节:网络探测首先,我们需要通过各种命令来探测网络是否畅通,进而跟踪锁定到实际...

Nginx正向代理、反向代理、负载均衡及性能优化

一、Nginx是什么Nginx是一款高性能的HTTP和反向代理服务器,由俄罗斯人IgorSysoev(伊戈尔·赛索耶夫)为俄罗斯网站Rambler.ru开发的,在Rambler.ru网站平稳的运...

nginx负载均衡配置(nginx负载均衡配置教程)

Nginx是什么没有听过Nginx?那么一定听过它的“同行”Apache吧!Nginx同Apache一样都是一种WEB服务器。基于REST架构风格,以统一资源描述符(UniformResources...

Springmvc使用Nginx负载均衡session共享

上一节,我们讲到nginx的结构组成,已经把模块、还有之前谈及负载均衡知识时候遗留的东西进行了讲解,那么今天我们继续把使用nginx做负载均衡的时候,如何处理session的方法做个解析,如何有需要对...

Linux 系统卡顿问题的排查思路(linux系统突然非常卡)

#Linux系统卡顿问题排查思路当Linux系统出现卡顿问题时,可以按照以下系统性排查思路进行分析和解决:##1.快速检查系统整体状态###查看系统负载```bashuptimetophto...

一文搞懂LVS负载均衡工作原理 :NAT、DR、TUN模式

大家好,我是IT售前工程师Bernie。LVS(LinuxVirtualServer)是企业中常用的负载均衡方案,是一种基于Linux虚拟服务器,也是Linux标准内核的一部分。它能够实现高性...

从零构建高性能 LVS + Keepalived 四层负载均衡集群实战指南

一、前言在大型网站架构中,四层负载均衡是流量调度的第一道防线。相比七层(如Nginx、HAProxy),四层(基于IP/端口转发)在性能上更具优势。LVS(LinuxVirtualServer...