利用iptables的nfqueue将流量导向自定义安全过滤程序需两步:先用iptables规则(如iptables -i input -p tcp -m state --state new -j nfqueue --queue-num 1)将匹配包送入指定队列,再在用户态通过libnetfilter_queue(c/c++)或nfqueue(python)接收、解析并执行accept/drop等判决,注意权限、校验和重算及性能优化。

要利用 iptables 的 NFQUEUE 将流量导向自定义安全过滤程序,核心是两步:先用 iptables 规则把目标数据包“引”进队列,再在用户态通过 libnetfilter_queue(C/C++)或 nfqueue(Python)接收、解析、修改或放行/丢弃包。
设置iptables规则,触发NFQUEUE
iptables 需将匹配的数据包跳转到 NFQUEUE 目标,而不是直接 DROP 或 ACCEPT。关键点:
- 必须指定队列编号(
--queue-num N),N 通常为 0~65535,默认队列号是 0 - 链的选择取决于你要拦截的流量方向:INPUT(进本机)、OUTPUT(出本机)、FORWARD(经本机转发)
- 建议加
-m state --state NEW或具体协议端口限制,避免误拦大量回包或 ACK
示例命令(拦截所有进本机的新 TCP 连接):
iptables -I INPUT -p tcp -m state --state NEW -j NFQUEUE --queue-num 1若需清理规则,可用:
iptables -D INPUT -p tcp -m state --state NEW -j NFQUEUE --queue-num 1
编写用户态处理程序(以Python为例)
Python 可借助 python-nfqueue 库(底层调用 libnetfilter_queue)实现快速原型。安装后,程序需:
- 创建并绑定队列句柄(queue number 必须与 iptables 中一致)
- 注册回调函数,在其中解析原始数据包(如用
dpkt或scapy) - 根据业务逻辑决定 verdict:ACCEPT(放行)、DROP(丢弃)、REPEAT(重入队列)、STOLEN(接管不返回)等
最小可行代码片段:
import nfqueuefrom dpkt import ip
def cb(_, payload):
data = payload.get_payload()
# 解析 IP 层
try:
ip_pkt = ip.IP(data)
if ip_pkt.p == ip.IP_PROTO_TCP and ip_pkt.data.dport == 80:
payload.set_verdict(nfqueue.NF_DROP) # 拦截所有进站 HTTP
return
except Exception:
pass
payload.set_verdict(nfqueue.NF_ACCEPT)
q = nfqueue.queue()
q.open()
q.bind(socket.AF_INET)
q.set_callback(cb)
q.create_queue(1) # 注意:必须与 --queue-num 1 对应
q.try_run()
关键注意事项
NFQUEUE 不是万能透明代理,实际部署中容易踩坑:
- 内核需加载
nfnetlink_queue模块(lsmod | grep nfnetlink_queue,未加载可modprobe nfnetlink_queue) - 用户态程序必须有 CAP_NET_ADMIN 权限(一般需 root 运行)
- 队列满时新包会被内核默认丢弃(可通过
sysctl -w net.netfilter.nfnetlink_max_queues=1024调整) - 不要在回调里做耗时操作(如网络请求、文件写入),否则阻塞整个队列,导致连接超时甚至系统卡顿
- 若修改了数据包内容(如重写 IP 或端口),需重新计算校验和(TCP/UDP/IP checksum),否则包会被接收方丢弃
进阶:C/C++ 实现更高性能过滤
对吞吐量要求高的场景(如千兆以上流量),推荐用 C 编写处理逻辑,直接链接 libnetfilter_queue。相比 Python:
- 零解释器开销,单核轻松处理 10 万+ PPS
- 可精细控制内存分配、复用缓冲区、批量收发(
nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff)) - 支持更底层操作,如直接 mangle 包头、注入自定义载荷
典型流程:初始化 queue → 设置拷贝模式 → 注册 callback → 循环调用 nfq_handle_packet() → 在 callback 中解析、判断、调用 nfq_set_verdict() 或 nfq_set_verdict2() 带修改数据。










