击左上方蓝色“一口Linux”,选择“设为星标”
第一时间看干货文章 ☞【干货】嵌入式驱动工程师学习路线 ☞【干货】Linux嵌入式知识点-思维导图-免费获取 ☞【就业】一个可以写到简历的基于Linux物联网综合项目 ☞【就业】简历模版
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 ~]#
删除规则与查看规则:
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
此内核无该类问题,规则生效,但内存不会增长。
注意:该环境iptables两条udp规则和洪涝攻击并不会导致内存泄露,但是产品线S83中的preconf会导致空闲内存越来越少。(因固件包过老,暂不查产品线的问题)
tunables 部分可调参数(通常为 0,表示使用默认值)slabdata 部分 • 第一个值:活跃的 slab 页数量。(因每个slab占用内存物理页为1个,也可认为此值是该slab的个 数) • 第二个值:总 slab 页数量。 • 第三个值:共享 slab 页数量 总对象个数=slab组数每组对象 如 82176=128464 其中81887为当前活跃的对象
slab内存回收是以slab单个节点来回收,一个slab控制器包含多个obj,只有所有obj都非活跃状态,slab节点才会被系统回收。所有只有释放obj,后续才有slab内存回收。
当删除规则,kmalloc-64立即回到原先正常大小。或者在不删除规则的环境下,此时再降速攻击,kmalloc-64便会慢慢释放,最终变回原先大小:
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
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所有配置都应做此调整
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 函数的 执行。
__nf_conncount_add -> kmem_cache_alloc(conncount_conn_cachep, GFP_ATOMIC) (此函数便是分配slab的缓存对象)
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 统计当前键值对应的连接数
攻击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]**
[ 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]**
可以看到先连续创建前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]**
我们可以看到创建节点速度要明显快于释放。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]**
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持续增加。
高频攻击,有一个新连接便生成一个节点(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;
}
iptables -D INPUT 1删除规则,会触发connlimit_mt_destroy -> nf_conncount_destroy ->destroy_tree -> kmem_cache_free ,便会把所用的slab对象全部释放,所以kmalloc-64恢复原来较低 数值。
find_or_evict函数是用来释放无效过期节点,虽然find_or_evict除了在我们介绍的 __nf_conncount_add函数里被调用,还会在其他几个函数中被调用。
但是通过日志追踪,实际find_or_evict只在__nf_conncount_add中执行。那么只有新节点过来,才会进入find_or_evict函数来释放内存,所以暂停攻击,kmalloc-64 就会一直保持原高值不变。
当低频攻击时,__nf_conncount_add的在10ms内直接add_new_node就会少,同时find_or_evict函数一次最多可以释放9个对象。这样释放节点数快于新节点的创建,所以kmalloc-64 会在原来高值情况下慢慢减小,最终会回到内存正常状态。
之前过分析的代码:
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 为何没进入上述释放节点的逻辑:
end
一口Linux
关注,回复【1024】海量Linux资料赠送
精彩文章合集
文章推荐