netfilter-在内核态操作网络数据包

一.概述                                                   

netfilter是自2.4内核的一个数据包过滤框架。可以过滤数据包,网络地址和端口转换(nat和napt技术),以及其他操作数据包的功能。主要工作原理是在内核模块注册回调函数(hook函数)到内核,内核执行到相关点时会触发这个回调函数,然后根据回调函数里的逻辑,对包含网络协议栈的sk_buff结构进行操作!!!iptables的内核模块就是使用的netfilter。netfilter官方网站:http://www.netfilter.org/

二.基本函数接口                                    

1.把hook函数注册到内核需要使用结构体nf_hook_ops来关联,该结构体定义在内核头文件目录的linux/netfilter.h里面,这里以2.6.39内核为例:

 1 struct nf_hook_ops {
 2     struct list_head list;
 3
 4     /* User fills in from here down. */
 5     nf_hookfn *hook;
 6     struct module *owner;
 7     u_int8_t pf;
 8     unsigned int hooknum;
 9     /* Hooks are ordered in ascending priority. */
10     int priority;
11 };

nf_hookfn就是要填充的hook函数。pf时8位协议族,如ipv4就是PF_INET。hooknum是指定hook函数注册的数据包的路径地点。priority是该hook的优先级,当有多个hook在同一个点注册的时候,会按照优先级来执行hook。

2.nf_hookfn函数声明也是在linux/netfilter.h里面:

1 typedef unsigned int nf_hookfn(unsigned int hooknum,
2                    struct sk_buff *skb,
3                    const struct net_device *in,
4                    const struct net_device *out,
5                    int (*okfn)(struct sk_buff *));

sk_buff是内核维护的网络协议栈结构,里面可以取出各协议栈信息。该函数的返回值:

1 /* Responses from hook functions. */
2 #define NF_DROP 0
3 #define NF_ACCEPT 1
4 #define NF_STOLEN 2
5 #define NF_QUEUE 3
6 #define NF_REPEAT 4
7 #define NF_STOP 5

NF_ACCEPT:接收数据包,由内核继续正常的报文传送

NF_DROP:丢弃数据包

NF_STOLEN:数据包的操作全部由hook函数处理

NF_QUEUE:将报文入队,通常交由用户程序处理

NF_REPEAT:再次调用该hook函数。

3.hooknum的5个通用注册点也是定义在linux/netfilter.h

1 enum nf_inet_hooks {
2     NF_INET_PRE_ROUTING,
3     NF_INET_LOCAL_IN,
4     NF_INET_FORWARD,
5     NF_INET_LOCAL_OUT,
6     NF_INET_POST_ROUTING,
7     NF_INET_NUMHOOKS
8 };

NF_INET_PRE_ROUTING:系统收到数据后,且没有经过路由
NF_INET_LOCAL_IN:系统收到数据,经过路由后,如果数据的目标地址是本机就经过该点
NF_INET_FORWARD:系统收到数据,经过路由后,如果数据的目标地址是其他地方就经过该点
NF_INET_LOCAL_OUT:系统发送数据时,未经过路由
NF_INET_POST_ROUTING:系统发送数据时,经过了路由阶段,马上就发出去了

对于ipv4的注册点值跟上面一样,只是用的宏定义:

 1 /* IP Hooks */
 2 /* After promisc drops, checksum checks. */
 3 #define NF_IP_PRE_ROUTING    0
 4 /* If the packet is destined for this box. */
 5 #define NF_IP_LOCAL_IN        1
 6 /* If the packet is destined for another interface. */
 7 #define NF_IP_FORWARD        2
 8 /* Packets coming from a local process. */
 9 #define NF_IP_LOCAL_OUT        3
10 /* Packets about to hit the wire. */
11 #define NF_IP_POST_ROUTING    4
12 #define NF_IP_NUMHOOKS        5
13 #endif /* ! __KERNEL__ */

4.注册和撤销注册函数:

1 /* Function to register/unregister hook points. */
2 int nf_register_hook(struct nf_hook_ops *reg);
3 void nf_unregister_hook(struct nf_hook_ops *reg);

参数都是nf_hook_ops结构指针。

三.简单例子                                           

我们用一个简单例子观察vxlan的数据包在各hook点的ip情况,vxlan环境可以参考Docker+OpenvSwitch搭建VxLAN实验环境

本例子是内核模块编程,可以参考初探linux内核编程,参数传递以及模块间函数调用

  1 /**
  2  * @file netfilter_hook.c
  3  */
  4
  5 #include <linux/module.h>
  6 #include <linux/kernel.h>
  7
  8 #include <linux/ip.h>
  9 #include <linux/netfilter_ipv4.h>
 10
 11 MODULE_LICENSE("Dual BSD/GPL");
 12 MODULE_AUTHOR("yuuyuu");
 13 MODULE_DESCRIPTION("netfilter");
 14 MODULE_VERSION("1.0");
 15
 16 /* 打印点分制ip地址 */
 17 #define printk_ip(info, be32_addr)  18     printk("%s %d.%d.%d.%d\n",  19     info,  20     ((unsigned char *)&(be32_addr))[0],  21     ((unsigned char *)&(be32_addr))[1],  22     ((unsigned char *)&(be32_addr))[2],  23     ((unsigned char *)&(be32_addr))[3])
 24
 25 int filter_ip(__be32 addr)
 26 {
 27     unsigned char net_num = ((unsigned char *)&addr)[0];
 28     unsigned char host_num = ((unsigned char *)&addr)[3];
 29     if (net_num == 10 || host_num == 1 || host_num == 2)
 30         return 1;
 31     return 0;
 32 }
 33
 34 int filter_src_dst_ip(__be32 s_addr, __be32 d_addr)
 35 {
 36     int i = filter_ip(s_addr) && filter_ip(d_addr);
 37     return i;
 38 }
 39
 40 /* NF_INET_PRE_ROUTING */
 41 unsigned int pre_routing_hook(unsigned int hooknum, struct sk_buff *skb,
 42                                 const struct net_device *in, const struct net_device *out,
 43                                 int (*okfn)(struct sk_buff *))
 44 {
 45     struct iphdr *ip_header;
 46
 47     ip_header = ip_hdr(skb);
 48     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
 49     {
 50         printk("pre_routing_hook()==================================\n");
 51         printk_ip("src ip:", ip_header->saddr);
 52         printk_ip("dst ip:", ip_header->daddr);
 53     }
 54
 55     return NF_ACCEPT;
 56 }
 57
 58 struct nf_hook_ops pre_routing_ops =
 59 {
 60     .hook = pre_routing_hook,
 61     .pf = PF_INET,
 62     .hooknum = NF_INET_PRE_ROUTING,
 63     .priority = NF_IP_PRI_FIRST
 64 };
 65
 66 /* NF_INET_LOCAL_IN */
 67 unsigned int local_in_hook(unsigned int hooknum, struct sk_buff *skb,
 68                                 const struct net_device *in, const struct net_device *out,
 69                                 int (*okfn)(struct sk_buff *))
 70 {
 71     struct iphdr *ip_header;
 72
 73     ip_header = ip_hdr(skb);
 74     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
 75     {
 76         printk("local_in_hook()========================================\n");
 77         printk_ip("src ip:", ip_header->saddr);
 78         printk_ip("dst ip:", ip_header->daddr);
 79     }
 80
 81     return NF_ACCEPT;
 82 }
 83
 84 struct nf_hook_ops local_in_ops =
 85 {
 86     .hook = local_in_hook,
 87     .pf = PF_INET,
 88     .hooknum = NF_INET_LOCAL_IN,
 89     .priority = NF_IP_PRI_FIRST
 90 };
 91
 92 /* NF_INET_FORWARD */
 93 unsigned int forward_hook(unsigned int hooknum, struct sk_buff *skb,
 94                                 const struct net_device *in, const struct net_device *out,
 95                                 int (*okfn)(struct sk_buff *))
 96 {
 97     struct iphdr *ip_header;
 98
 99     ip_header = ip_hdr(skb);
100     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
101     {
102         printk("forward_hook=========================================\n");
103         printk_ip("src ip:", ip_header->saddr);
104         printk_ip("dst ip:", ip_header->daddr);
105     }
106
107     return NF_ACCEPT;
108 }
109
110 struct nf_hook_ops forward_ops =
111 {
112     .hook = forward_hook,
113     .pf = PF_INET,
114     .hooknum = NF_INET_FORWARD,
115     .priority = NF_IP_PRI_FIRST
116 };
117
118 /* NF_INET_LOCAL_OUT */
119 unsigned int local_out_hook(unsigned int hooknum, struct sk_buff *skb,
120                                 const struct net_device *in, const struct net_device *out,
121                                 int (*okfn)(struct sk_buff *))
122 {
123     struct iphdr *ip_header;
124
125     ip_header = ip_hdr(skb);
126     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
127     {
128         printk("local_out_hook===========================================\n");
129         printk_ip("src ip:", ip_header->saddr);
130         printk_ip("dst ip:", ip_header->daddr);
131     }
132
133     return NF_ACCEPT;
134 }
135
136 struct nf_hook_ops local_out_ops =
137 {
138     .hook = local_out_hook,
139     .pf = PF_INET,
140     .hooknum = NF_INET_LOCAL_OUT,
141     .priority = NF_IP_PRI_FIRST
142 };
143
144 /* NF_INET_POST_ROUTING */
145 unsigned int post_routing_hook(unsigned int hooknum, struct sk_buff *skb,
146                                 const struct net_device *in, const struct net_device *out,
147                                 int (*okfn)(struct sk_buff *))
148 {
149     struct iphdr *ip_header;
150
151     ip_header = ip_hdr(skb);
152     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
153     {
154         printk("post_routing_hook====================================\n");
155         printk_ip("src ip:", ip_header->saddr);
156         printk_ip("dst ip:", ip_header->daddr);
157     }
158
159     return NF_ACCEPT;
160 }
161
162 struct nf_hook_ops post_routing_ops =
163 {
164     .hook = post_routing_hook,
165     .pf = PF_INET,
166     .hooknum = NF_INET_POST_ROUTING,
167     .priority = NF_IP_PRI_FIRST
168 };
169
170 /* 注册 */
171 static int hook_init(void)
172 {
173     printk("hook_init()======================\n");
174     nf_register_hook(&pre_routing_ops);
175     nf_register_hook(&local_in_ops);
176     nf_register_hook(&forward_ops);
177     nf_register_hook(&local_out_ops);
178     nf_register_hook(&post_routing_ops);
179
180     return 0;
181 }
182
183 static void hook_exit(void)
184 {
185     printk("hook_exit()=====================\n");
186     nf_unregister_hook(&pre_routing_ops);
187     nf_unregister_hook(&local_in_ops);
188     nf_unregister_hook(&forward_ops);
189     nf_unregister_hook(&local_out_ops);
190     nf_unregister_hook(&post_routing_ops);
191 }
192
193 module_init(hook_init);
194 module_exit(hook_exit);

第27,34行简单过滤下本环境的IP,方便查看结果。

Makefile文件

1 KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
2 PWD := $(shell pwd)
3
4 obj-m := netfilter_hook.o
5
6 default:
7     $(MAKE) -C $(KERNEL_DIR) M=$(PWD) modules

四.验证                                                  

vxlan环境:

host1:eth0:192.168.2.1,虚拟网桥10.1.2.10

host2:eth0:192.168.2.2,虚拟网桥10.1.2.11

编译后插入模块,在host1里面ping一次host2的虚拟网桥10.1.2.11

1 ping 10.2.1.11 -c 1

然后参看内核输出信息:

1 sudo dmesg

可以看到vxlan的icmp请求有2个IP在工作,分别经过local_out_hook和post_routing_hook。

同理host2的icmp应答进入本机也是2个IP都经过了pre_routing_hook和local_in_hook。

时间: 2024-08-25 04:43:57

netfilter-在内核态操作网络数据包的相关文章

Linux内核中网络数据包的接收-第一部分 概念和框架

与网络数据包的发送不同,网络收包是异步的的,因为你不确定谁会在什么时候突然发一个网络包给你,因此这个网络收包逻辑其实包含两件事:1.数据包到来后的通知2.收到通知并从数据包中获取数据这两件事发生在协议栈的两端,即网卡/协议栈边界以及协议栈/应用边界:网卡/协议栈边界:网卡通知数据包到来,中断协议栈收包:协议栈栈/应用边界:协议栈将数据包填充socket队列,通知应用程序有数据可读,应用程序负责接收数据.本文就来介绍一下关于这两个边界的这两件事是怎么一个细节,关乎网卡中断,NAPI,网卡poll,

sk_buff封装和解封装网络数据包的过程详解

可以说sk_buff结构体是Linux网络协议栈的核心中的核心,几乎所有的操作都是围绕sk_buff这个结构体进行的,它的重要性和BSD的mbuf类似(看过<TCP/IP详解 卷2>的都知道),那么sk_buff是什么呢?       sk_buff就是网络数据包本身以及针对它的操作元数据.       想要理解sk_buff,最简单的方式就是凭着自己对网络协议栈的理解封装一个直到以太层的数据帧并且成功发送出去,个人认为这比看代码/看文档或者在网上搜资料强多了.当然,网上已经有了大量的这方面的

sk_buff封装和解封装网络数据包的过程详解(转载)

http://dog250.blog.51cto.com/2466061/1612791 可以说sk_buff结构体是Linux网络协议栈的核心中的核心,几乎所有的操作都是围绕sk_buff这个结构体进行的,它的重要性和BSD的mbuf类似(看过<TCP/IP详解 卷2>的都知道),那么sk_buff是什么呢?       sk_buff就是网络数据包本身以及针对它的操作元数据.       想要理解sk_buff,最简单的方式就是凭着自己对网络协议栈的理解封装一个直到以太层的数据帧并且成功发

CORE网络数据包接收传递过程分析

能够接收实际网络流量是CORE的一个显著优点,这使得已有的系统能方便地接入虚拟网络进行模拟.CORE对网络设备的虚拟是通过LXC技术来实现的,而对网络的虚拟则是通过虚拟网卡(veth).网桥(Bridge).Quagga来实现的.本文档主要通过分析CORE中网络数据传递过程,来理解CORE网络模拟. 拓扑结构 为了方便描述,以如图1所示拓扑结构为例子,分析数据流从网卡eth0到虚拟节点n2的过程. 图1 示例拓扑 虚拟网络创建由CORE后台根据前台的拓扑结构和配置,执行相应的命令进行实现,如下:

网络数据包分析 网卡Offload

http://blog.nsfocus.net/network-packets-analysis-nic-offload/ 对于网络安全来说,网络传输数据包的捕获和分析是个基础工作,绿盟科技研究员在日常工作中,经常会捕获到一些大小远大于MTU值的数据包,经过分析这些大包的特性,发现和网卡的offload特性有关,本文对网卡Offload技术做简要描述. 文章目录 网络分片技术 网卡offload机制 发送模式 接收模式 网卡offload模式的设置 Linux windows 网卡Offload

用C++实现网络编程,抓取网络数据包的实现方法和介绍

做过网管或协议分析的人一般都熟悉sniffer这个工具,它可以捕捉流经本地网卡的所有数据包.抓取网络数据包进行分析有很多用处,如分析网络是否有网络病毒等异常数据,通信协议的分析(数据链路层协议.IP.UDP.TCP.甚至各种应用层协议),敏感数据的捕捉等.下面我们就来看看在windows下如何实现数据包的捕获. 下面先对网络嗅探器的原理做简单介绍. 嗅探器设计原理 嗅探器作为一种网络通讯程序,也是通过对网卡的编程来实现网络通讯的,对网卡的编程也是使用通常的套接字(socket)方式来进行.但是,

捕获网络数据包并进行分析的开源库-WinPcap

什么是WinPcap WinPcap是一个基于Win32平台的,用于捕获网络数据包并进行分析的开源库. 大多数网络应用程序通过被广泛使用的操作系统元件来访问网络,比如sockets.  这是一种简单的实现方式,因为操作系统 已经妥善处理了底层具体实现细节(比如协议处理,封装数据包等等),并且提供了一个与读写文件类似的,令人熟悉的接口. 然而,有些时候,这种“简单的方式”并不能满足任务的需求,因为有些应用程序需要直接访问网 络中的数据包.也就是说,那些应用程序需要访问原始数据包,即没有被操作系统利

Linux网络数据包的揭秘以及常见的调优方式总结

https://mp.weixin.qq.com/s/boRWlx1R7TX0NLuI2sZBfQ 作为业务 SRE,我们所运维的业务,常常以 Linux+TCP/UDP daemon 的形式对外提供服务.SRE 需要对服务器数据包的接收和发送路径有全面的了解,以方便在服务异常时能快速定位问题.以 tcp 协议为例,本文将对 Linux 内核网络数据包接收的路径进行整理和说明,希望对大家所有帮助. Linux 数据包接收路径的整体说明 接收数据包是一个复杂的过程,涉及很多底层的技术细节 , 这里

一个最简单的通过WireShark破解SSL加密网络数据包的方法

原文地址: http://article.yeeyan.org/view/530101/444688 一般来说,我们用WireShark来抓取包进行分析是没有多大问题的.但这里有个问题是,如果你碰到的是用SSL/TLS等加密手段加密过的网络数据的时候,往往我们只能束手无策.在过去的话,如果我们拥有的该传输会话的私钥的话我们还是可以将它提供给WireShark来让其对这些加密数据包进行解密的 1. 简介 相信能访问到这篇文章的同行基本上都会用过流行的网络抓包工具WireShark,用它来抓取相应的