一.概述                                                   

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内核为例:

 struct nf_hook_ops {
     struct list_head list;

     /* User fills in from here down. */
     nf_hookfn *hook;
     struct module *owner;
     u_int8_t pf;
     unsigned int hooknum;
     /* Hooks are ordered in ascending priority. */
     int priority;
 };

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

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

 typedef unsigned int nf_hookfn(unsigned int hooknum,
                    struct sk_buff *skb,
                    const struct net_device *in,
                    const struct net_device *out,
                    int (*okfn)(struct sk_buff *));

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

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

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

NF_DROP:丢弃数据包

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

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

NF_REPEAT:再次调用该hook函数。

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

 enum nf_inet_hooks {
     NF_INET_PRE_ROUTING,
     NF_INET_LOCAL_IN,
     NF_INET_FORWARD,
     NF_INET_LOCAL_OUT,
     NF_INET_POST_ROUTING,
     NF_INET_NUMHOOKS
 };

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

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

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

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

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

参数都是nf_hook_ops结构指针。

三.简单例子                                           

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

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

 /**
  * @file netfilter_hook.c
  */

 #include <linux/module.h>
 #include <linux/kernel.h>

 #include <linux/ip.h>
 #include <linux/netfilter_ipv4.h>

 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_AUTHOR("yuuyuu");
 MODULE_DESCRIPTION("netfilter");
 MODULE_VERSION("1.0");

 /* 打印点分制ip地址 */
 #define printk_ip(info, be32_addr) \
     printk("%s %d.%d.%d.%d\n", \
     info, \
     ((unsigned ], \
     ((unsigned ], \
     ((unsigned ], \
     ((unsigned ])

 int filter_ip(__be32 addr)
 {
     unsigned ];
     unsigned ];
      || host_num ==  || host_num == )
         ;
     ;
 }

 int filter_src_dst_ip(__be32 s_addr, __be32 d_addr)
 {
     int i = filter_ip(s_addr) && filter_ip(d_addr);
     return i;
 }

 /* NF_INET_PRE_ROUTING */
 unsigned int pre_routing_hook(unsigned int hooknum, struct sk_buff *skb,
                                 const struct net_device *in, const struct net_device *out,
                                 int (*okfn)(struct sk_buff *))
 {
     struct iphdr *ip_header;

     ip_header = ip_hdr(skb);
     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
     {
         printk("pre_routing_hook()==================================\n");
         printk_ip("src ip:", ip_header->saddr);
         printk_ip("dst ip:", ip_header->daddr);
     }

     return NF_ACCEPT;
 }

 struct nf_hook_ops pre_routing_ops =
 {
     .hook = pre_routing_hook,
     .pf = PF_INET,
     .hooknum = NF_INET_PRE_ROUTING,
     .priority = NF_IP_PRI_FIRST
 };

 /* NF_INET_LOCAL_IN */
 unsigned int local_in_hook(unsigned int hooknum, struct sk_buff *skb,
                                 const struct net_device *in, const struct net_device *out,
                                 int (*okfn)(struct sk_buff *))
 {
     struct iphdr *ip_header;

     ip_header = ip_hdr(skb);
     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
     {
         printk("local_in_hook()========================================\n");
         printk_ip("src ip:", ip_header->saddr);
         printk_ip("dst ip:", ip_header->daddr);
     }

     return NF_ACCEPT;
 }

 struct nf_hook_ops local_in_ops =
 {
     .hook = local_in_hook,
     .pf = PF_INET,
     .hooknum = NF_INET_LOCAL_IN,
     .priority = NF_IP_PRI_FIRST
 };

 /* NF_INET_FORWARD */
 unsigned int forward_hook(unsigned int hooknum, struct sk_buff *skb,
                                 const struct net_device *in, const struct net_device *out,
                                 int (*okfn)(struct sk_buff *))
 {
     struct iphdr *ip_header;

     ip_header = ip_hdr(skb);
     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
     {
         printk("forward_hook=========================================\n");
         printk_ip("src ip:", ip_header->saddr);
         printk_ip("dst ip:", ip_header->daddr);
     }

     return NF_ACCEPT;
 }

 struct nf_hook_ops forward_ops =
 {
     .hook = forward_hook,
     .pf = PF_INET,
     .hooknum = NF_INET_FORWARD,
     .priority = NF_IP_PRI_FIRST
 };

 /* NF_INET_LOCAL_OUT */
 unsigned int local_out_hook(unsigned int hooknum, struct sk_buff *skb,
                                 const struct net_device *in, const struct net_device *out,
                                 int (*okfn)(struct sk_buff *))
 {
     struct iphdr *ip_header;

     ip_header = ip_hdr(skb);
     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
     {
         printk("local_out_hook===========================================\n");
         printk_ip("src ip:", ip_header->saddr);
         printk_ip("dst ip:", ip_header->daddr);
     }

     return NF_ACCEPT;
 }

 struct nf_hook_ops local_out_ops =
 {
     .hook = local_out_hook,
     .pf = PF_INET,
     .hooknum = NF_INET_LOCAL_OUT,
     .priority = NF_IP_PRI_FIRST
 };

 /* NF_INET_POST_ROUTING */
 unsigned int post_routing_hook(unsigned int hooknum, struct sk_buff *skb,
                                 const struct net_device *in, const struct net_device *out,
                                 int (*okfn)(struct sk_buff *))
 {
     struct iphdr *ip_header;

     ip_header = ip_hdr(skb);
     if (filter_src_dst_ip(ip_header->saddr, ip_header->daddr))
     {
         printk("post_routing_hook====================================\n");
         printk_ip("src ip:", ip_header->saddr);
         printk_ip("dst ip:", ip_header->daddr);
     }

     return NF_ACCEPT;
 }

 struct nf_hook_ops post_routing_ops =
 {
     .hook = post_routing_hook,
     .pf = PF_INET,
     .hooknum = NF_INET_POST_ROUTING,
     .priority = NF_IP_PRI_FIRST
 };

 /* 注册 */
 static int hook_init(void)
 {
     printk("hook_init()======================\n");
     nf_register_hook(&pre_routing_ops);
     nf_register_hook(&local_in_ops);
     nf_register_hook(&forward_ops);
     nf_register_hook(&local_out_ops);
     nf_register_hook(&post_routing_ops);

     ;
 }

 static void hook_exit(void)
 {
     printk("hook_exit()=====================\n");
     nf_unregister_hook(&pre_routing_ops);
     nf_unregister_hook(&local_in_ops);
     nf_unregister_hook(&forward_ops);
     nf_unregister_hook(&local_out_ops);
     nf_unregister_hook(&post_routing_ops);
 }

 module_init(hook_init);
 module_exit(hook_exit);

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

Makefile文件

 KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
 PWD := $(shell pwd)

 obj-m := netfilter_hook.o

 default:
     $(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

 

然后参看内核输出信息:

 sudo dmesg

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

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

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

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

    与网络数据包的发送不同,网络收包是异步的的.由于你不确定谁会在什么时候突然发一个网络包给你.因此这个网络收包逻辑事实上包括两件事:1.数据包到来后的通知2.收到通知并从数据包中获取数据这两件事发生在协 ...

  2. Linux内核网络数据包处理流程

    Linux内核网络数据包处理流程 from kernel-4.9: 0. Linux内核网络数据包处理流程 - 网络硬件 网卡工作在物理层和数据链路层,主要由PHY/MAC芯片.Tx/Rx FIFO. ...

  3. linux2.6.24内核源代码分析(2)——扒一扒网络数据包在链路层的流向路径之一

    在2.6.24内核中链路层接收网络数据包出现了两种方法,第一种是传统方法,利用中断来接收网络数据包,适用于低速设备:第二种是New Api(简称NAPI)方法,利用了中断+轮询的方法来接收网络数据包, ...

  4. Linux 中的网络数据包捕获

    Linux 中的网络数据包捕获 Ashish Chaurasia, 工程师 简介: 本教程介绍了捕获和操纵数据包的不同机制.安全应用程序,如 VPN.防火墙和嗅探器,以及网络应用程序,如路由程序,都依 ...

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

    http://dog250.blog.51cto.com/2466061/1612791 可以说sk_buff结构体是Linux网络协议栈的核心中的核心,几乎所有的操作都是围绕sk_buff这个结构体 ...

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

    转自:http://www.2cto.com/os/201502/376226.html 可以说sk_buff结构体是Linux网络协议栈的核心中的核心,几乎所有的操作都是围绕sk_buff这个结构体 ...

  7. 用C++实现网络编程---抓取网络数据包的实现方法

    一般都熟悉sniffer这个工具,它可以捕捉流经本地网卡的所有数据包.抓取网络数据包进行分析有很多用处,如分析网络是否有网络病毒等异常数据,通信协议的分析(数据链路层协议.IP.UDP.TCP.甚至各 ...

  8. Android利用tcpdump和wireshark抓取网络数据包

    Android利用tcpdump和wireshark抓取网络数据包 主要介绍如何利用tcpdump抓取andorid手机上网络数据请求,利用Wireshark可以清晰的查看到网络请求的各个过程包括三次 ...

  9. 网络数据包信息收集工具ferret-sidejack

    网络数据包信息收集工具ferret-sidejack   网络数据包传递用户的各种操作和对应的信息.但是由于各种数据混在一起,不利于渗透测试人员分析.Kali Linux提供了一款信息搜集工具ferr ...

随机推荐

  1. 学习hadoop遇到的问题

    1.运行hadoop的帐号,需要多大的权限? 之前实验用的帐号是root,后来新建了个普通帐号,发觉不能直接套用到已经建立好的hadoop系统,因为无权限,即使将普通帐号加入到root群组仍然存在异常 ...

  2. Nodejs与ES6系列2:Promise对象

    2.promise对象 js单线程异步执行的特性,因此在代码中充斥着回调函数.随着回调函数的增加,代码的可读性会愈来愈差,因此引入promise对象是不错的一种选择,可以避免层层回调函数.在ECMA6 ...

  3. MixItUp:超炫!基于 CSS3 & jQuery 的过滤和排序插件

    MixItUp 是一款轻量,但功能强大的 jQuery 插件,提供了对分类和有序内容的美丽的动画过滤和排序功能.特别适合用于作品集网站,画廊,图片博客以及任何的分类或有序内容. 它是如何工作的? Mi ...

  4. 13个风格独特的关于页面(About Pages)设计

    如何向其他人呈现你自己和你的作品呢?关于页面的设计是非常重要的,以让你的观众更多地了解你,你的工作和你的想法.这是一个很好的方式来获得更加个性化的展示效果. 设计一个漂亮的关于页面是具有挑战性的.出于 ...

  5. Java2_java入门时的一些基本概念的理解(j2ee,j2se,j2me,jdk,sdk,jre,jvm,跨平台)

    什么是SDK呢? Software Develop Kit的简称,顾名思义就是软件开发包.软件开发商实现底层模块,并对其进行类库封装,配置成高级别的开发环境,为程序员上层的程序开发提供支持.譬如Goo ...

  6. 异常之Tomcat7.0服务器无法发布项目

    今天突然就不能发布tomcat 7.0服务器了,并弹出对话框,报出如下错误: Cannot acquire J2EEFlexProjDeployable object for module test ...

  7. [原][CSS3]会动的盒子机器人

    [PC与移动端皆可]会动的盒子机器人 浏览器必须可以解析perspective属性. 在线:http://wangxinsheng.herokuapp.com/boxMan 代码: <!DOCT ...

  8. SQLServer表内自关联级联删除

    今天处理SQLServer级联删除遇到了很蛋疼的事. SQLServer 不支持表内自关联级联删除,而MySql和Oracle却支持. 貌似原因是SQLServer 会产生循环级联,就不给这样弄.所以 ...

  9. FeatureLayer,FeatureDataset,FeatureClass,Feature的概念

    刚学AE,其中很多概念都模糊不清.经过一段时间的摸索总结,对FeatureLayer,FeatureDataset,FeatureClass,Feature几个概念有了一点认识.拿出来分享一下,有错误 ...

  10. ArcPy之Python介绍

    1.Python简介 Python是一种面向对象.解释型计算机程序设计语言;Python是一种简单易学,功能强大的编程语言.它有高效率的高层数据结构,简单而有效地实现面向对象编程.Python简洁的语 ...