news 2026/4/26 21:48:38

深入浅出Linux内核级防火墙:IP/端口黑白名单的高性能实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
深入浅出Linux内核级防火墙:IP/端口黑白名单的高性能实现

在Linux系统中,防火墙是网络安全的核心组件,而内核级防火墙相比用户态防火墙(如iptables用户态工具),能直接在网络数据包流经内核协议栈时进行处理,避免了用户态-内核态的频繁切换,具备更低的延迟和更高的吞吐量。本文将从核心概念、设计思路、实现原理等角度,拆解一款支持IP(单IP+CIDR网段)、端口(单端口+端口范围)黑白名单管控的低级Linux防火墙,理解内核态网络管控的核心逻辑。

一、先搞懂:这些核心概念到底是什么?

在看具体实现前,先把基础概念掰扯清楚,避免被专业术语绕晕:

1. 核心框架:Netfilter

Netfilter是Linux内核内置的网络数据包处理框架,相当于在内核的网络数据通路上预埋了多个“钩子点”(Hook Point)。我们可以把自己的过滤逻辑挂到这些钩子上,数据包经过时就会触发我们的代码——本文的防火墙就是把过滤逻辑挂在了NF_INET_PRE_ROUTING钩子点(数据包进入协议栈、路由决策前),优先级略高于连接跟踪(CONNTRACK),保证先做黑白名单过滤,再处理连接跟踪。

2. 关键数据结构(为什么选这些?)

  • Radix树(基数树):用来存单IP的黑白名单。Radix树是内核里专门做“键值对”快速查找的结构,单IP作为键(32位无符号整数),IP的黑白名单标记作为值,查一个IP是否在名单里的效率接近O(1),比链表遍历快得多。
  • 哈希链表+RCU:用来存CIDR网段(如192.168.1.0/24)。先通过哈希函数把网段的网络地址映射到哈希桶,再用链表存同一个桶里的CIDR规则;RCU(读-拷贝更新)是内核的并发安全机制,保证多CPU核同时读规则时不阻塞,写规则时不影响读。
  • 位图(Bitmap)+链表:用来存端口规则。端口号范围是0-65535,用位图的每2位分别标记一个端口是否在白名单/黑名单(比如第port<<1位标记白名单,port<<1 +1位标记黑名单),查端口是否命中规则只需一个位运算,速度极快;链表则用来记录端口范围(如4-55),方便规则的添加/删除。
  • Per-CPU缓存:内核每个CPU核都有独立的缓存,存最近匹配过的IP/CIDR规则,避免同一个IP每次都去查Radix树/哈希链表,进一步降低开销。

3. 内核态-用户态交互:ProcFS

ProcFS是Linux内核提供的伪文件系统(/proc目录),可以通过创建自定义的文件节点,让用户态程序(比如echo、cat)直接读写内核数据。本文的防火墙就是通过ProcFS创建了一系列文件节点,用户只需往/proc下的特定文件写规则(如echo "192.168.1.100" > /proc/xxx/ip_whitelist/add),就能动态添加/删除黑白名单,无需重启内核模块。

二、防火墙的核心设计思路

整体设计遵循“高性能、低开销、可动态配置”三个原则,核心架构分为三层:

读写ProcFS文件

解析规则/同步数据

提供匹配接口

Netfilter钩子

ACCEPT/DROP

用户态

ProcFS交互层

规则存储层

数据包过滤层

网络数据包

内核协议栈后续处理

If you need the complete source code, please add the WeChat number (c17865354792)

核心决策逻辑

数据包进来后,防火墙按这个顺序判断,只要匹配到一条规则就立刻决策,不做多余检查:

  1. 先查是否命中CIDR黑名单 → 命中就丢弃(DROP);
  2. 再查是否命中单IP黑名单 → 命中就丢弃;
  3. 接着查是否命中CIDR白名单 → 命中就放行(ACCEPT);
  4. 再查是否命中单IP白名单 → 命中就放行;
  5. 如果是TCP/UDP包,查目标端口:
    • 命中端口白名单 → 放行;
    • 命中端口黑名单 → 丢弃;
  6. 以上都没命中 → 默认丢弃(可根据需求调整)。

补充:先查黑名单再查白名单,是为了保证“黑名单优先级更高”——比如一个IP既在白名单又在黑名单,最终会按黑名单处理。

三、核心实现原理拆解

1. 数据包拦截:Netfilter钩子注册

想要处理数据包,第一步是把我们的过滤函数挂到Netfilter的钩子上:

// 定义钩子操作结构体staticconststructnf_hook_opsfw_ops={.hook=fw_filter,// 数据包过滤的核心函数.pf=NFPROTO_IPV4,// 只处理IPv4数据包.hooknum=NF_INET_PRE_ROUTING,// 钩子点:路由前.priority=NF_IP_PRI_CONNTRACK+1,// 优先级:比连接跟踪高一点};// 初始化时注册钩子voidfw_net_init(void){nf_register_net_hook(&init_net,&fw_ops);}

这段代码的作用就是告诉内核:“凡是IPv4数据包经过PRE_ROUTING阶段时,先调用我的fw_filter函数处理”。

2. 核心过滤函数:fw_filter

这是数据包处理的核心逻辑,我们用大白话拆解关键步骤:

staticunsignedintfw_filter(void*priv,structsk_buff*skb,conststructnf_hook_state*state){structiphdr*ip_header=ip_hdr(skb);// 提取IP头u32 src_ip=ntohl(ip_header->saddr);// 把网络序的源IP转成主机序intret;// 第一步:跳过已建立连接的数据包(连接跟踪标记过的),提升性能structnf_conn*ct=nf_ct_get(skb,&ctinfo);if(ct)returnNF_ACCEPT;// 第二步:检查CIDR黑名单 → IP黑名单 → CIDR白名单 → IP白名单ret=ip_in_cidr_blacklist(src_ip);if(ret)returnNF_DROP;ret=ip_in_blacklist(src_ip);if(ret)returnNF_DROP;ret=ip_in_cidr_whitelist(src_ip);if(ret)returnNF_ACCEPT;ret=ip_in_whitelist(src_ip);if(ret)returnNF_ACCEPT;// 第三步:处理TCP/UDP端口规则if(ip_header->protocol==IPPROTO_TCP){structtcphdr*tcp_header=tcp_hdr(skb);u16 dst_port=ntohs(tcp_header->dest);// 提取目标端口if(port_in_whitelist(dst_port))returnNF_ACCEPT;if(port_in_blacklist(dst_port))returnNF_DROP;}elseif(ip_header->protocol==IPPROTO_UDP){// UDP端口处理逻辑和TCP一致}// 都没匹配到,默认丢弃returnNF_DROP;}

核心逻辑总结:先跳过已连接的包(避免重复过滤),再按“黑名单优先”的顺序查IP/CIDR规则,最后查端口规则,匹配到就立刻返回放行/丢弃。

3. 规则存储:不同场景选不同结构

(1)单IP规则:Radix树

Radix树的优势是“键值对快速查找”,适合单IP这种精准匹配的场景:

// 定义Radix树根节点structradix_tree_rootip_tree;// 初始化Radix树voidfw_ip_init(void){INIT_RADIX_TREE(&ip_tree,GFP_KERNEL);}// 查找IP是否在白名单intip_in_whitelist(u32 ip){ip_desc*desc=radix_tree_lookup(&ip_tree,ip);// 核心查找操作if(desc&&(desc->flags&IP_WHITELIST_MASK)){this_cpu_write(ipcache,desc);// 写入Per-CPU缓存return1;}return0;}

往Radix树加IP规则时,先查IP是否已存在:存在则更新标记,不存在则新建节点插入,保证规则不重复。

(2)CIDR规则:哈希链表+RCU

CIDR是网段匹配(比如192.168.1.0/24),先把网段的网络地址哈希到桶,再遍历桶里的链表找匹配的网段:

// 哈希桶数组,大小是2^16#definebucket_num(1<<16)structhlist_head*cidr_hash;// 初始化哈希桶voidfw_cidr_init(void){cidr_hash=kmalloc(bucket_num*sizeof(*cidr_hash),GFP_KERNEL);for(inti=0;i<bucket_num;i++)INIT_HLIST_HEAD(&cidr_hash[i]);}// CIDR匹配核心:先哈希,再遍历链表intip_in_cidr_blacklist(u32 ip){inti;u16 hash;cidr_desc*desc;// 遍历已配置的CIDR掩码(从大到小,保证精准匹配优先)for(i=0;i<32;i++){if(cidr_mask_array[i]==0)continue;// 把IP按掩码截断,得到网段地址u32 cidr_ip=ip&~((1<<cidr_mask_array[i])-1);hash=hashfn(cidr_ip);// 哈希计算rcu_read_lock();// RCU读锁,保证并发安全// 遍历哈希桶里的链表hlist_for_each_entry_rcu(desc,&cidr_hash[hash],node){if(desc->ip==cidr_ip&&(desc->flags&CIDR_BLACKLIST_MASK)){rcu_read_unlock();return1;}}rcu_read_unlock();}return0;}

这里有个小技巧:CIDR掩码按从大到小遍历(比如先/24再/16),保证小网段(更精准)优先匹配。

(3)端口规则:位图+链表

端口规则分“单端口”和“端口范围”,位图负责快速查询,链表负责存储范围规则:

// 位图:2*65536位 = 16KB,占用内存极小staticlongunsignedint*port_bitmap;// 初始化位图voidfw_port_init(void){port_bitmap=bitmap_zalloc(1<<17,GFP_KERNEL);// 2^17位 = 16KB}// 快速判断端口是否在白名单(位运算,O(1))intport_in_whitelist(u16 port){returntest_bit((port<<1),port_bitmap);}// 添加端口范围规则:遍历范围,设置位图intinsert_port(void*p){port_desc*desc=p;intstart=desc->start;intend=desc->end?:start;// 单端口则start=endif(desc->flags&PORT_WHITELIST_MASK){for(inti=start;i<=end;i++){set_bit((i<<1),port_bitmap);// 设置白名单位}}// 同时把范围规则存到链表,方便后续删除// ... 链表插入逻辑 ...return1;}

位图的优势是“极致快”,查一个端口只需一次位运算,比任何链表/树都快;链表则是为了记录“范围”,避免删除时要遍历所有端口。

4. 用户态交互:ProcFS接口

为了让用户能动态配置规则,防火墙在/proc下创建了一套文件节点,比如:

  • /proc/xxx/ip/whitelist/add:往这里写IP,添加IP白名单;
  • /proc/xxx/cidr/blacklist/delete:往这里写CIDR,删除CIDR黑名单;
  • /proc/xxx/port/whitelist/show:读这个文件,查看端口白名单。

核心实现是注册文件操作函数(read/write),用户写数据时,内核解析数据(比如把“192.168.1.0/24”拆成IP和掩码),再调用对应的添加/删除函数;用户读数据时,内核把规则拼接成字符串返回给用户态。

这里要注意并发安全:用mutex锁保护规则的添加/删除,避免多进程同时写规则导致数据错乱。

四、核心知识点总结

这款内核级防火墙的实现,核心是把“Linux内核网络编程”的关键知识点落地,总结几个核心要点:

1. 性能优化的核心思路

  • 数据结构适配场景:精准匹配(单IP)用Radix树,网段匹配(CIDR)用哈希链表,端口匹配用位图——不同场景选最合适的结构,避免“一刀切”;
  • 减少冗余操作:Per-CPU缓存缓存最近匹配的规则,跳过已连接的数据包,减少重复计算;
  • 并发安全且高效:读规则用RCU(无锁读),写规则用Mutex(低冲突锁),兼顾并发安全和性能。

2. Netfilter使用的关键

  • 钩子点选择:PRE_ROUTING适合“路由前过滤”,能在数据包进入本地进程/转发前就处理;
  • 优先级设置:略高于连接跟踪(CONNTRACK),避免对已建立的连接重复过滤,提升性能;
  • 决策返回值:NF_ACCEPT(放行)、NF_DROP(丢弃)是Netfilter的核心返回值,决定数据包的命运。

3. 内核态编程的注意事项

  • 内存管理:内核态内存不能随意分配,要用kmalloc/kfree,且要指定分配标志(如GFP_KERNEL);
  • 并发安全:多CPU核、多进程操作同一数据时,必须用RCU/Mutex等机制保护,避免数据竞争;
  • 用户态-内核态交互:用copy_from_user/copy_to_user传输数据,不能直接访问用户态内存,避免内核崩溃。

五、应用场景与扩展思路

这款内核级防火墙适合轻量级、高性能、低延迟的网络管控场景,比如:

  • 嵌入式设备(路由器、网关)的网络访问控制;
  • 服务器的精准IP/端口黑白名单管控;
  • 高并发场景下的快速数据包过滤。

扩展方向也很明确:

  1. 支持IPv6:只需把IP的存储从32位改成128位,适配Radix树和哈希函数;
  2. 增强日志:记录过滤的数据包信息(源IP、端口、时间),输出到内核日志或用户态;
  3. 动态开关:添加全局开关,通过ProcFS控制防火墙的启用/禁用;
  4. 更细粒度的规则:支持按协议(ICMP/ICMPv6)、按网卡、按时间段过滤。

总结

Linux内核级防火墙的核心,本质是“利用内核原生框架(Netfilter)+ 适配场景的高效数据结构 + 安全的并发控制”,实现对网络数据包的快速管控。相比用户态防火墙,它少了用户态-内核态的切换开销,能在高并发场景下保持极低的延迟;而合理选择数据结构(Radix树、哈希、位图),则是保证过滤性能的关键。

Welcome to follow WeChat official account【程序猿编码

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 18:44:53

百度AI:让电脑和手机也能像人一样操作屏幕的智能助手诞生了

想象一下&#xff0c;如果你的电脑或手机能像真人一样看懂屏幕上的内容&#xff0c;知道哪个按钮该点击&#xff0c;哪个输入框该填写&#xff0c;甚至还能帮你完成复杂的多步操作任务&#xff0c;那会是什么样的体验&#xff1f;这听起来像是科幻电影里的情节&#xff0c;但百…

作者头像 李华
网站建设 2026/4/23 18:51:17

备份了,却救不了你?警惕这三种“伪备份”陷阱

很多企业都以为自己有备份——U盘拷过、网盘同步过、甚至买了专业备份软件……可当勒索病毒真正来袭、硬盘突然崩溃时&#xff0c;却发现&#xff1a;备份根本用不了。这不是危言耸听&#xff0c;而是无数中小企业踩过的坑。今天&#xff0c;我们不谈功能多强大&#xff0c;只聊…

作者头像 李华
网站建设 2026/4/21 1:48:47

【软件测试】9_性能测试实战 _性能测试监控

文章目录一、性能测试监控关键指标1.1 系统指标1.2 硬件服务器资源指标1.2.1 CPU、内存、磁盘1.2.2 CPU使用率1.2.3 CPU占用分类1.2.4 内存和虚拟内存1.2.5 磁盘IO1.2.6 网络1.3 JAVA应用1.3.1 JVM-java虚拟机1.3.2 JAVA虚拟机内存1.3.3 FULL GC机制1.4 数据库监控1.4.1 慢查询…

作者头像 李华
网站建设 2026/4/18 8:04:42

不想用 ElevenLabs?2026 年 7 款 AI 语音、TTS 与语音克隆替代方案评测

随着人工智能语音技术的飞速发展&#xff0c;创作者、开发者和企业越来越多地寻找 ElevenLabs 的替代方案——这些平台能提供更具竞争力的定价、更高的语音克隆准确率、更灵活的 API 以及更强的可扩展性。本篇 2026 年评测将为您深入分析 7 款 ElevenLabs 顶级替代方案、它们的…

作者头像 李华
网站建设 2026/4/25 14:55:20

软件开发公司新蓝海:2026年如何借力AI开发平台,降本增效接大单?

对于软件开发公司而言&#xff0c;2026年既是挑战也是机遇。客户需求日益智能化&#xff0c;但自建AI团队成本高昂、技术风险大。此时&#xff0c;选择一个得力的AI开发平台作为战略合作伙伴&#xff0c;将成为突围的关键。它不仅能提升自身交付能力&#xff0c;更能开辟“AI代…

作者头像 李华