iptables -A 阻止上万IP会卡死,因其规则为链表结构,匹配复杂度O(n),而ipset用哈希表实现O(1)查找;应选用hash:ip或hash:net类型,并用ipset restore批量导入。

为什么直接用 iptables -A 阻止上万 IP 会卡死
iptables 本身不擅长处理海量离散 IP 或 CIDR。每加一条规则,内核都要线性遍历匹配链;10 万条规则可能让 iptables -L 卡住几十秒,更别说实时匹配开销。这不是配置问题,是数据结构限制——iptables 规则本质是链表,而 ipset 底层用哈希表或 bitmap,查找复杂度接近 O(1)。
- 实际中,单个
ipset存 50 万 IPv4 地址仍能保持毫秒级匹配 - 若混用大量
-s 1.2.3.4 -j DROP规则,conntrack 表压力、规则加载延迟、重启防火墙失败风险都会陡增 - 不要试图用 shell 循环反复调
iptables -A—— 每次调用都触发一次内核规则重载
创建高性能黑名单 set 的关键参数选择
ipset 类型选错,性能和内存占用差十倍。针对纯黑名单(只匹配源 IP),优先用:
-
hash:ip:最常用,适合纯 IPv4 地址列表,内存占用低,插入/查询快 -
hash:net:当你要封整个网段(如192.168.0.0/16)时必须用它,hash:ip不支持 CIDR 匹配 - 避免用
list:set或bitmap:ip:前者是链表结构,后者只适用于极小地址范围(如内网 10.0.0.0/8)
创建示例:
ipset create blackv4 hash:ip hashsize 65536 maxelem 500000-
hashsize 建议设为预期元素数的 2–4 倍(默认 1024 太小,易哈希冲突降速)
- maxelem 必须显式设大,否则添加第 65537 条时直接报 IP set full
- 加 timeout 0 可禁用自动过期(黑名单通常长期有效)
如何把现有 IP 列表高效导入 ipset
别用 while read ip; do ipset add blackv4 $ip; done < list.txt —— 每行一次 syscall,10 万行要跑好几分钟。
正确做法是批量加载:
ipset restore < blackv4.restore其中
blackv4.restore 格式为:
create blackv4 hash:ip hashsize 65536 maxelem 500000 add blackv4 1.1.1.1 add blackv4 2.2.2.2 add blackv4 192.168.1.0/24
- 所有
add行必须在create之后,且不能有空行 - 如果原始列表含注释或空行,先用
grep -v '^#|^$' list.txt | sed 's/^/add blackv4 /' > adds.txt生成 add 行,再拼到 restore 文件里 - 导入前确保
ipset未被 iptables 引用(否则restore会失败),可先iptables -D INPUT -m set --match-set blackv4 src -j DROP
iptables 怎么安全关联 ipset 且避免规则断裂
关联不是简单写一条规则就完事。常见断裂点:
-
iptables -I INPUT -m set --match-set blackv4 src -j DROP是对的,但必须保证该规则在所有允许规则之前(比如在ACCEPT established之前),否则连接已建立的流量绕过检测 - 不要用
-A追加,避免因规则顺序错位导致漏封 - 如果用
nftables,对应语法是ip saddr @blackv4 drop,但注意 nftables 的 set 必须用nft add set创建,不能复用 ipset 的 set
额外防护建议:
- 给 set 加
comment描述用途:ipset create blackv4 hash:ip comment(需内核 ≥ 4.17 + ipset ≥ 7.0) - 定期用
ipset list blackv4 | head -20抽样检查内容,避免脚本误写入非法 IP(如123)导致后续操作全挂 - 删除整个 set 前,务必先从 iptables 中解绑,否则
ipset destroy会报Busy
实际运维中最容易被忽略的是 hashsize 和 maxelem 的初始设定——它们一旦 set 创建就无法动态调整,填小了只能 destroy 重建,而重建期间防火墙策略真空。宁可预估高些,也别等线上突增 20 万 IP 后发现加不进去了。


















