用ipset替代iptables线性规则可大幅提升黑名单性能:hash:ip适用于纯IPv4,hash:net支持网段;需设maxelem、hashsize和timeout 0;批量导入应生成restore文件一次性加载。

选对类型和参数是性能基础
纯 IPv4 黑名单优先用 hash:ip;含网段(如 192.168.0.0/24)必须用 hash:net。别用 list:set(仍是链表)或 bitmap:ip(只适合极小地址空间)。
创建时必须显式设关键参数:
-
maxelem:预估最大 IP 数,比如 20 万就写
maxelem 200000,不设默认仅 65536,超限直接报 “Set is full” -
hashsize:建议设为 maxelem 的 2–4 倍(如
hashsize 524288),避免哈希冲突导致降速 - 加
timeout 0关闭自动过期,适合长期有效的黑名单
批量导入不能靠 shell 循环
用 while read ip; do ipset add blacklist $ip; done 导入 10 万行,实际要发起 10 万次系统调用,耗时几分钟且易中断。
正确做法是生成 restore 文件再一次性加载:
- 先清理原始列表:去掉空行、注释,
grep -v '^#|^$' blacklist.txt | sed 's/^/add blacklist /' > adds.txt - 拼成标准 restore 格式:
echo "create blacklist hash:ip hashsize 524288 maxelem 200000 timeout 0" > blacklist.restore && cat adds.txt >> blacklist.restore - 执行:
ipset restore
动态更新必须零停机切换
直接 ipset flush blacklist 再重加,清空瞬间所有黑名单 IP 都能通行,存在明显安全窗口。
安全更新流程如下:
- 后台构建新集合:
ipset create blacklist_new hash:ip ...,再用 restore 导入新名单 - 原子切换:
ipset swap blacklist blacklist_new,毫秒完成,旧集合自动变空 - 立刻清理:
ipset destroy blacklist_new,否则残留空集仍占内核内存 - 确保两个集合类型一致,否则 swap 报错 “Set cannot be swapped: different types”
规则生效前务必检查三处硬门槛
IP 明明在集合里却没被拦截?大概率卡在这几个环节:
-
iptables 规则位置太靠后:必须用
-I INPUT插入最前,若写在-j ACCEPT后面,匹配永远到不了它 - IPv4/IPv6 混用:hash:ip 集合无法匹配 IPv6 地址;脚本读取日志时注意过滤 ::ffff:192.168.1.1 这类映射地址
-
内核模块未加载:运行
lsmod | grep ip_set,应看到ip_set、ip_set_hash_ip等模块;缺失需手动modprobe ip_set_hash_ip


















