完成内容
我将创建一个简单的防火墙demo,帮助你熟悉网络内核编程。这个demo将基于Linux的Netfilter框架实现。
基础框架

基础概念
Netfilter 框架
- Netfilter 是 Linux 内核中的一个框架,用于对网络数据包进行过滤、修改和重定向。它提供了多个钩子点(hook points),允许用户在不同的网络层插入自定义逻辑。
Netfilter 主要功能
- 数据包过滤(如防火墙)。
- 网络地址转换(NAT,包括 SNAT 和 DNAT)。
- 数据包修改(如修改 TTL、TOS 等字段)。
- 数据包日志记录。
Netfilter 钩子点
-
NF_INET_PRE_ROUTING:数据包进入网络栈后,但在路由决策之前。适合用于 DNAT(目标地址转换)或数据包标记。
1)netfilter的内核挂载点在代码中的位置:内核代码路径 :/net/ipv4/ip_input.c
2)具体函数 :在 ip_rcv()
ip_rcv_finish是路由查找后的回调函数
-
NF_INET_LOCAL_IN:数据包目标是本机(即目标地址是本机的 IP 地址)。适合用于过滤或修改进入本机的数据包。
1)内核代码路径 :/net/ipv4/ip_input.c
2)具体函数 :在 ip_local_deliver()
-
NF_INET_FORWARD:数据包需要被转发到其他主机(即本机作为路由器)。适合用于过滤或修改转发的数据包。
1)内核代码路径 :/net/ipv4/ip_forward.c
2)具体函数 :在 ip_forward()
-
NF_INET_LOCAL_OUT:数据包从本机发出。适合用于过滤或修改从本机发出的数据包。
1)内核代码路径 :/net/ipv4/ip_output.c
2)具体函数 :在 __ip_local_out()
-
NF_INET_POST_ROUTING:数据包在路由决策之后,但在发送到网络接口之前。适合用于 SNAT(源地址转换)或数据包标记
1)内核代码路径 :/net/ipv4/ip_output.c
2)具体函数 :在 ip_output()
在NF_INET_PRE_ROUTING点进行实例实现
c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
staticunsignedintcustom_hook(void*priv,structsk_buff*skb,conststructnf_hook_state*state){
structiphdr*ip_header =ip_hdr(skb);
if(ip_header->protocol == IPPROTO_TCP){// 只处理 TCP 数据包
structtcphdr*tcp_header =tcp_hdr(skb);
printk(KERN_INFO "Custom Netfilter Hook: TCP packet to port %u\n",ntohs(tcp_header->dest));
if(ntohs(tcp_header->dest)==22){ // 只处理目标端口为 22 的数据包
printk(KERN_INFO "Custom Netfilter Hook: TCP packet to port 22\n");
return NF_DROP;// 丢弃数据包
}
}
return NF_ACCEPT;// 允许数据包继续传递
}
staticstructnf_hook_ops nfho ={
.hook = custom_hook,
.pf = NFPROTO_IPV4, //作用在ip层
.hooknum = NF_INET_PRE_ROUTING,// 在 PRE_ROUTING 阶段处理数据包
.priority = NF_IP_PRI_FIRST, // 优先级
};
staticint __init pre_routing_module_init(void){
nf_register_net_hook(&init_net,&nfho);
printk(KERN_INFO "Custom Netfilter Hook registered\n");
return0;
}
staticvoid __exit pre_routing_module_exit(void){
nf_unregister_net_hook(&init_net,&nfho);
printk(KERN_INFO "Custom Netfilter Hook unregistered\n");
}
module_init(pre_routing_module_init);
module_exit(pre_routing_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("pre_routing_module_init");
MODULE_DESCRIPTION("Netfilter hook example");
Makefile:
c
obj-m += hook_NF_INET_PRE_ROUTING.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
编译与验证
终端中运行以下命令来编译内核模块
c
$ make
make -C /lib/modules/5.4.0-150-generic/build M=/home/hook_netfilter modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic'
CC [M] /homehook_netfilter/hook_NF_INET_PRE_ROUTING.o
Building modules, stage 2.
MODPOST 1 modules
CC [M] /home/hook_netfilter/hook_NF_INET_PRE_ROUTING.mod.o
LD [M] /homehook_netfilter/hook_NF_INET_PRE_ROUTING.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic'
c
@ubuntu:hook_netfilter$ ls
hook_NF_INET_PRE_ROUTING.c hook_NF_INET_PRE_ROUTING.mod hook_NF_INET_PRE_ROUTING.mod.o Makefile Module.symvers
hook_NF_INET_PRE_ROUTING.ko hook_NF_INET_PRE_ROUTING.mod.c hook_NF_INET_PRE_ROUTING.o modules.order
加载模块
c
$ sudo insmod hook_NF_INET_PRE_ROUTING.ko
查看模块
c
$ lsmod |grep hook
hook_NF_INET_PRE_ROUTING 16384 0
c
$ dmesg | tail
[716700.997388] Custom Netfilter Hook registered
[709907.950324] Custom Netfilter Hook: TCP packet to port 22
[709907.950325] Custom Netfilter Hook: TCP packet to port 22
验证结果

验证成功(无法连接)

我们可以用一个框图来简单罗列数据方向

意味着 custom_hook 函数被挂载到了 ip_rcv() 函数中的 NF_HOOK 调用点。
这是最早能抓到包的地方,所有进入的数据包(无论是发往本机还是需要转发的)都会经过这里,过滤规则会非常早地生效。