一.概述                                                   

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. linux 查找目录或文件详解

    查找目录:find /(查找范围) -name '查找关键字' -type d查找文件:find /(查找范围) -name 查找关键字 -print 如果需要更进一步的了解,可以参看Linux的命令 ...

  2. ASP.NET MVC Model绑定的简单应用

    Model绑定是 MVC 框架根据 HTTP 请求数据创建 .NET 对象的一个过程. 一.简单类型 1.单一值

  3. [AngularJS] 使用AngularAMD动态加载Service

    [AngularJS] 使用AngularAMD动态加载Service 前言 「使用AngularAMD动态加载Controller」:这篇文章里介绍如何使用AngularAMD来动态加载Contro ...

  4. The template engine

    Play has an efficient templating system which allows to dynamically generate HTML, XML, JSON or any ...

  5. [deviceone开发]-大家比较关注的应用内部升级

    一.简介 这个示例详细介绍了应用内升级的步骤,应用内升级是开发App必须的功能.推荐初学者学习. 二.效果图 三.相关下载 https://github.com/do-project/code4do/ ...

  6. [读码]HTML5像素文字爆炸重组

    [边读码,边学习,技术也好,思路也罢] [一款基于HTML5技术的文字像素爆炸重组动画特效,我们可以在输入框中指定任意文字,点击确定按钮后,就会将原先的文字爆炸散去,新的文字以像素点的形式组合起来,看 ...

  7. Linux 常见命令说明

    目前由于自己接触到的是 windows 的操作系统,所以会经常使用 git bash 来提交代码到 github上. git bash 是 windows 下模拟 Linux 的命令行工具. 在此总结 ...

  8. 送给我的朋友——Cry on my shoulder

    If the hero never comes to you如果你的真命天子仍未来到 If you need someone you"re feeling blue如果你情绪低落需要有人陪伴 ...

  9. GRID方式ALV导出数据到本地丢掉最后一位的问题

    这是SAP的一个Bug,FM方式ALV Grid和Class ALV Grid都会出现,但是ALV List好像没有这个BUG.   在以下几个条件满足的时候就会出现这个问题: 1.字段对应的域Con ...

  10. iOS/Android网络消息推送的实现两种方法

    移动时代,用户为王,而每个APP拥有的活跃用户量(Active Users),决定了其价值. 消息推送成为了不可或缺的活跃唤起工具. 目前消息推送有如下两种途径: 1.iOS传统方式: 通过Apple ...