Kernel之Tcpdump和Netfilter
tcpdump 的工作原理
- tcpdump 捕获通过网络接口的数据包,并以可读的格式显示出来。它可以访问 OSI 模型中的数据链路层(第 2层)及以上的层级,从而捕获并分析数据包的头部和有效载荷。
- tcpdump 本身并不直接使用钩子函数,因为它是一个用户空间的工具,依赖于操作系统提供的底层机制来捕获网络数据包。tcpdump 是通过 libpcap 库来实现数据包捕获的,而 libpcap 则是通过操作系统的网络栈接口来获取数据包。
tcpdump 的工作流程
tcpdump 的工作流程可以简化为以下几个步骤:
- 初始化:
tcpdump 调用 libpcap 库来初始化数据包捕获。
libpcap通过操作系统提供的接口来设置数据包捕获。 - 数据包捕获:
操作系统网络栈在接收到数据包时,会将数据包传递给 libpcap。
libpcap 将数据包传递给 tcpdump。 - 数据包处理:
tcpdump 根据用户指定的过滤规则和显示选项,对数据包进行处理和输出。
Netfilter 框架
Netfilter 是 Linux 内核中的一个框架,用于对网络数据包进行过滤、修改和重定向。它提供了多个钩子点(hook points),允许用户在不同的网络层插入自定义逻辑。
Netfilter 主要功能
- 数据包过滤(如防火墙)。
- 网络地址转换(NAT,包括 SNAT 和 DNAT)。
- 数据包修改(如修改 TTL、TOS 等字段)。
- 数据包日志记录。
Netfilter 钩子点
- NF_INET_PRE_ROUTING:
数据包进入网络栈后,但在路由决策之前。
适合用于 DNAT(目标地址转换)或数据包标记。 - NF_INET_LOCAL_IN:
数据包目标是本机(即目标地址是本机的 IP 地址)。
适合用于过滤或修改进入本机的数据包。 - NF_INET_FORWARD:
数据包需要被转发到其他主机(即本机作为路由器)。
适合用于过滤或修改转发的数据包。 - NF_INET_LOCAL_OUT:
数据包从本机发出。
适合用于过滤或修改从本机发出的数据包。 - NF_INET_POST_ROUTING:
数据包在路由决策之后,但在发送到网络接口之前。
适合用于 SNAT(源地址转换)或数据包标记
iptable 和 ebtable是否都属于 netfilter?
iptables 和 ebtables 都是基于 Netfilter 框架的工具,但它们的作用范围和使用场景有所不同
iptables 和 ebtables 区别
iptables 和 ebtables 关系
- 共同点:
两者都基于 Netfilter 框架。
都使用类似的表(Tables)和链(Chains)结构。
都支持过滤、NAT 和修改功能。 - 不同点:
iptables 主要用于 IPv4 数据包,而 ebtables 主要用于以太网帧。
iptables 工作在网络层,而 ebtables 工作在数据链路层。
tcpdump是抓哪一层iso层的log
它可以用于捕获不同 ISO模型中的多个层次的数据包,具体取决于过滤条件和抓包配置。
物理层(网卡接收数据包)
↓
数据链路层(Layer 2,以太网帧)
↓
ebtables(修改/过滤以太网帧)→ 数据包被处理并继续传递
↓
tcpdump(抓取链路层数据包的副本,需指定参数如-e
)
↓
网络层(Layer 3,IP 数据包)
↓
iptables(修改/过滤 IP 数据包)→ 数据包被处理并继续传递
↓
tcpdump(默认抓取网络层及以上的数据副本)
↓
传输层(TCP/UDP)→ 应用层
说明
- 数据链路层(Layer 2):
ebtables 在这里操作以太网帧(如修改 MAC 地址、VLAN 标签)。
数据包经过 ebtables 处理后,继续传递到网络层。
tcpdump 可以通过 -e 参数抓取链路层帧头(需在此时抓取副本)。 - 网络层(Layer 3):
iptables 在这里操作 IP 数据包(如修改 IP 地址、端口号、NAT)。
数据包经过 iptables 处理后,继续传递到传输层和应用层。
tcpdump 默认抓取网络层及以上的数据(如 IP、TCP/UDP 头和应用层负载)。 - tcpdump 的抓包时机:
链路层抓包:tcpdump -e 会在数据链路层处理后(即 ebtables 操作后)抓取帧头。
网络层抓包:tcpdump 默认在网络层处理后(即 iptables 操作后)抓取 IP 层数据。
注意:tcpdump 抓取的是数据包的副本,不会影响实际传输的数据包。
按照ISO层级分
编写内核模块或使用工具(如 iptables )来利用 Netfilter 的钩子点
我这里选择使用内核模块实现 iptables工具实现的功能(当然也可实现ebtables的功能)
编写内核模块
源文件:
#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>
static unsigned int custom_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
struct iphdr *ip_header = ip_hdr(skb);
if (ip_header->protocol == IPPROTO_TCP) { // 只处理 TCP 数据包
struct tcphdr *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; // 允许数据包继续传递
}
static struct nf_hook_ops nfho = {
.hook = custom_hook,
.pf = NFPROTO_IPV4, //作用在ip层
.hooknum = NF_INET_PRE_ROUTING, // 在 PRE_ROUTING 阶段处理数据包
.priority = NF_IP_PRI_FIRST, // 优先级
};
static int __init pre_routing_module_init(void) {
nf_register_net_hook(&init_net, &nfho);
printk(KERN_INFO "Custom Netfilter Hook registered\n");
return 0;
}
static void __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:
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
编译与验证
终端中运行以下命令来编译内核模块
make
结果:
$ 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'
@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
加载模块
$ sudo insmod hook_NF_INET_PRE_ROUTING.ko
查看模块
$ lsmod |grep hook
hook_NF_INET_PRE_ROUTING 16384 0
$ 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
验证结果
验证成功(无法连接)