本文主要内容:Netfilter的原理和实现浅析,以及示例模块。

内核版本:2.6.37

Author:zhangskd @ csdn blog

概述

Netfilter为多种网络协议(IPv4、IPv6、ARP等)各提供了一套钩子函数。

在IPv4中定义了5个钩子函数,这些钩子函数在数据包流经协议栈的5个关键点被调用。

这就像有5个钓鱼台,在每个钓鱼台放了一个鱼钩(钩子函数),把经过的数据包钓上来,

然后根据自定义的规则,来决定数据包的命运:

可以原封不动的放回IPv4协议,继续向上层递交;可以进行修改,再放回IPv4协议;也可以直接丢弃。

Netfilter主要采用连接跟踪(Connection Tracking)、包过滤(Packet Filtering)、地址转换(NAT)、包处理

(Packet Mangling)四种技术。

(1) IP层的5个钓鱼台

[java] view plaincopy
  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. };

支持的协议类型:

[java] view plaincopy
  1. enum {
  2. NFPROTO_UNSPEC = 0,
  3. NFPROTO_IPV4 = 2,
  4. NFPROTO_ARP = 3,
  5. NFPROTO_BRIDGE = 7,
  6. NFPROTO_IPV6 = 10,
  7. NFPROTO_DECNET = 12,
  8. NFPROTO_NUMPROTO,
  9. };

(2) 钩子函数

[java] view plaincopy
  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 *));
  6. /* 处理函数返回值 */
  7. #define NF_DROP 0 /* drop the packet, don't continue traversal */
  8. #define NF_ACCEPT 1 /* continue traversal as normal */
  9. #define NF_STOLEN 2 /* I've taken over the packet, don't continue traversal */
  10. #define NF_QUEUE 3 /* queue the packet (usually for userspace handling) */
  11. #define NF_REPEAT 4 /* call this hook again */
  12. #define NF_STOP 5
  13. #define NF_MAX_VERDICT NF_STOP

(3) Netfilter实体

在使用Netfilter时,需要定义一个nf_hook_ops实例。

[java] view plaincopy
  1. struct nf_hook_ops {
  2. struct list_head list;
  3. /* User fills in from here down. */
  4. nf_hookfn *hook; /* 要注册的钩子函数 */
  5. struct module *owner;
  6. u_int8_t pf; /* 协议类型 */
  7. unsigned int hooknum; /* 哪个钓鱼台 */
  8. /* Hooks are ordered in asending priority. */
  9. int priority; /* 数值越小,优先级越高 */
  10. };
  11. typedef __u8 u_int8_t;

(4) 注册与注销

[java] view plaincopy
  1. /* Functions 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);

实现

Netfilter定义了一个全局链表:

[java] view plaincopy
  1. struct list_head nf_hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS];
  2. EXPORT_SYMBOL(nf_hooks);
  3. static DEFINE_MUTEX(nf_hook_mutex);

(1) 注册函数

注册函数会把nf_hook_ops放入nf_hooks相应的位置中。

[java] view plaincopy
  1. int nf_register_hook(struct nf_hook_ops *reg)
  2. {
  3. struct nf_hook_ops *elem;
  4. int err;
  5. err = mutex_lock_interruptible(&nf_hook_mutex);
  6. if (err < 0)
  7. return err;
  8. list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
  9. if (reg->priority < elem->priority)
  10. break;
  11. }
  12. list_add_rcu(&reg->list, elem->list.prev); /* 把netfilter实例添加到队列中 */
  13. mutex_unlock(&nf_hook_mutex);
  14. return 0;
  15. }

(2) 注销函数

[java] view plaincopy
  1. void nf_unregister_hook(struct nf_hook_ops *reg)
  2. {
  3. mutex_lock(&nf_hook_mutex);
  4. list_del_rcu(&reg->list); /* 把netfilter实例从队列中删除 */
  5. mutex_unlock(&nf_hook_mutex);
  6. synchronize_net();
  7. }

(3) 内核接口

内核的Netfilter钩子函数调用:

NF_HOOK

|--> NF_HOOK_THRESH

|--> nf_hook_thresh

|--> nf_hook_slow

|--> nf_iterate

[java] view plaincopy
  1. static inline int NF_HOOK(uint8_t pf, unsigned int hook, struct sk_buff *skb,
  2. struct net_device *in, struct net_device *out, int (*okfn)(struct sk_buff *))
  3. {
  4. /* INT_MIN表示要调用钓鱼台的所有钩子函数 */
  5. return NF_HOOK_THRESH(pf, hook, skb, in, out, okfn, INT_MIN);
  6. }
  7. static inline int NF_HOOK_THRESH(uint8_t pf, unsigned int hook, struct sk_buff *skb,
  8. struct net_device *in, struct net_device *out, int (*okfn)(struct sk_buff *), int thresh)
  9. {
  10. int ret = nf_hook_thresh(pf, hook, skb, in, out, okfn, thresh);
  11. if (ret == 1)
  12. ret = okfn(skb); /* 如果skb没被处理掉,调用此函数 */
  13. return ret;
  14. }
[java] view plaincopy
  1. /**
  2. * nf_hook_thresh - call a netfilter hook
  3. * Returns 1 if the hook has allowed the packet to pass.
  4. * The function okfn must be invoked by the caller in this case.
  5. * Any other return value indicates the packet has been consumed by the hook.
  6. */
  7. static inline int nf_hook_thresh(u_int8_t pf, unsigned int hook, struct sk_buff *skb,
  8. struct net_device *indev, struct net_device *outdev, int (*okfn)(struct sk_buff *), int thresh)
  9. {
  10. #ifndef CONFIG_NETFILTER_DEBUG
  11. /* 如果协议pf的hook点上没有已注册的nf_hook_ops实例,直接返回1 */
  12. if (list_empty(&nf_hooks[pf][hook]))
  13. return 1;
  14. #endif
  15. return nf_hook_slow(pf, hook, skb, indev, outdev, okfn, thresh);
  16. }
  17. /* Returns 1 if okfn() needs to be executed by the caller, -EPERM for NF_DROP, 0 otherwise. */
  18. int nf_hook_slow(u_int8_t pf, unsigned int hook, struct sk_buff *skb, struct net_device *indev,
  19. struct net_device *outdev, int (*okfn)(struct sk_buff *), int hook_thresh)
  20. {
  21. struct list_head *elem;
  22. unsigned int verdict;
  23. int ret = 0;
  24. /* We may already have this, but read-locks nest anyway */
  25. rcu_read_lock();
  26. elem = &nf_hooks[pf][hook];
  27. next_hook:
  28. verdict = nf_iterate(&nf_hooks[pf][hook], skb, hook, indev, outdev, &elem, okfn, hook_thresh);
  29. if (verdict == NF_ACCEPT || verdict == NF_STOP) {
  30. ret = 1;
  31. } else if (verdict == NF_DROP) {
  32. kfree_skb(skb);
  33. ret = -EPERM;
  34. } else if ((verdict & NF_VERDICT_MASK) == NF_QUEUE) {
  35. if (! nf_queue(skb, elem, ph, hook, indev, outdev, okfn, verdict >> NF_VERDICT_BITS))
  36. goto next_hook;
  37. }
  38. rcu_read_unlock();
  39. return ret;
  40. }
[java] view plaincopy
  1. unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, unsigned int hook,
  2. const struct net_device *indev, const struct net_device *outdev, struct list_head **i,
  3. int (*okfn)(struct sk_buff *), int hook_thresh)
  4. {
  5. unsigned int verdict;
  6. /*
  7. * The caller must not block between calls to this function because of risk of
  8. * continuing from deleted element.
  9. */
  10. list_for_each_continue_rcu(*i, head) {
  11. struct nf_hook_ops *elem = (struct nf_hook_ops *) *i;
  12. /* 优先级>=hook_thresh的都会被执行 */
  13. if (hook_thresh > elem_priority)
  14. continue;
  15. verdict = elem->hook(hook, skb, indev, outdev, okfn); /* 已注册的执行函数 */
  16. if (verdict != NF_ACCEPT) {
  17. #ifdef CONFIG_NETFILTER_DEBUG
  18. if (unlikely((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT)) {
  19. NFDEBUG("Evil return from %p(%u).\n", elem->hook, hook);
  20. continue;
  21. }
  22. #endif
  23. if (verdict != NF_REPEAT)
  24. return verdict;
  25. *i = (*i)->prev;
  26. }
  27. }
  28. return NF_ACCEPT;
  29. }

使用

以下是一个简单的模块,加载到一个HTTP服务器上。

通过在PRE_ROUTING处注册my_hookfn,改变接收数据包的源IP为8.8.8.8(Google DNS server)。

当客户端向服务器发送一个请求时,肯定收不到服务器的响应:)

[java] view plaincopy
  1. #include <linux/netfilter.h>
  2. #include <linux/init.h>
  3. #include <linux/module.h>
  4. #include <linux/netfilter_ipv4.h>
  5. #include <linux/ip.h>
  6. #include <linux/inet.h>
  7. /**
  8. * Hook function to be called.
  9. * We modify the packet's src IP.
  10. */
  11. unsigned int my_hookfn(unsigned int hooknum,
  12. struct sk_buff *skb,
  13. const struct net_device *in,
  14. const struct net_device *out,
  15. int (*okfn)(struct sk_buff *))
  16. {
  17. struct iphdr *iph;
  18. iph = ip_hdr(skb);
  19. /* log the original src IP */
  20. printk(KERN_INFO"src IP %pI4\n", &iph->saddr);
  21. /* modify the packet's src IP */
  22. iph->saddr = in_aton("8.8.8.8");
  23. return NF_ACCEPT;
  24. }
  25. /* A netfilter instance to use */
  26. static struct nf_hook_ops nfho = {
  27. .hook = my_hookfn,
  28. .pf = PF_INET,
  29. .hooknum = NF_INET_PRE_ROUTING,
  30. .priority = NF_IP_PRI_FIRST,
  31. .owner = THIS_MODULE,
  32. };
  33. static int __init sknf_init(void)
  34. {
  35. if (nf_register_hook(&nfho)) {
  36. printk(KERN_ERR"nf_register_hook() failed\n");
  37. return -1;
  38. }
  39. return 0;
  40. }
  41. static void __exit sknf_exit(void)
  42. {
  43. nf_unregister_hook(&nfho);
  44. }
  45. module_init(sknf_init);
  46. module_exit(sknf_exit);
  47. MODULE_AUTHOR("zhangsk");
  48. MODULE_LICENSE("GPL");

Netfilter的使用和实现相关推荐

  1. linux 防火墙 -netfilter

    2019独角兽企业重金招聘Python工程师标准>>> 关于iptables 什么是iptables? 常见于linx系统下的应用层防火墙工具 firewalld 和netfilte ...

  2. netfilter的笔记3--那些内置的表

    通过netfilter的笔记2的例子,我们知道了怎么使用netfilter的框架,对于内核的设计原则来说,策略和机制分离,所以提供了iptables来供用户配置防火墙策略. 那么,怎么使用iptabl ...

  3. Linux centos7 Linux网络相关、firewalld和netfilter、netfilter5表5链介绍、iptables语法

    一. Linux网络相关 yum install net-tools ifconfig查看网卡ip ifup ens33开启网卡 ifdown ens33关闭网卡 设定虚拟网卡ens33:0 mii- ...

  4. 基于linux的netfilter处理数据包的过程分析,基于Linux的Netfilter处理数据包的过程分析...

    基于Linux的Netfilter处理数据包的过程分析 防火墙技术在保护网络安全方面的作用越来越明显.相比较window,Linux有更好的网络性能,因此基于Linux的Netfilter技术 (本文 ...

  5. 七周三次课(11月29日) 10.11 Linux网络相关 10.12 firewalld和netfilter 10.13 netfilter5表5链介绍 10.14 iptables语法...

    2019独角兽企业重金招聘Python工程师标准>>> 10.11 Linux网络相关 ifconfig 查看网卡ip (yum install net-tools) 安装 -a   ...

  6. Linux网络相关、firewalld、netfilter及其5表5链、iptables语法

    2019独角兽企业重金招聘Python工程师标准>>> 1.Linux 网络相关 ifconfig 查看网卡IP,见下图, net-tools 包之前安装过了,这边还可以执行这个命令 ...

  7. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架 — conntrack(CT,连接跟踪)

    目录 文章目录 目录 CT CT CT(conntrack,connection tracking,连接跟踪),顾名思义,就是跟踪(并记录)连接的状态,是许多网络应用的基础.例如:iptables.L ...

  8. Linux Kernel TCP/IP Stack — L3 Layer — netfilter 框架

    目录 文章目录 目录 netfilter 框架 netfilter 的组成模块 netfilter 的 Hook 机制实现 netfilter 的工作原理 规则(Rules) 链(Chains) 表( ...

  9. Linux Kernel TCP/IP Stack — L3 Layer — netfilter/iptables 防火墙

    目录 文章目录 目录 iptables/netfilter 框架 iptables-service iptables 指令应用 查看规则 添加规则 删除规则 修改规则 保存和加载规则 常规初始化配置 ...

  10. Linux中netfilter模块编程实践

    上篇我们看了netfilter的实现机制,这篇来实现下netfilter模块实操一把. 为了注册一个钩子,需要填充nf_hook_ops结构体,包括优先级,钩子地点和钩子函数.然后调用nf_regis ...

最新文章

  1. 关于文件保存/关闭时报错:文件正由另一进程使用,因此该进程无法访问此文件。...
  2. Elasticsearch 安装配置 外网访问 及 后台启动
  3. LeetCode MySQL 1527. Patients With a Condition(like)
  4. python opencv显示图片动态_opencv-python计算机视觉图像处理学习笔记2——打开图片,保存图片,显示图片...
  5. 创建数据库和表的SQL语句
  6. upload-labs19记录
  7. xticks函数--Matplotlib
  8. ipad编程软件c语言2020,‎App Store 上的“计算机等级考试C语言版 2020最新”
  9. MyBatis 传入参数之parameterType
  10. 如何自主搭建信息管理系统
  11. Windows Azure AppFabric (一) 平台简介
  12. html如何居中svg,垂直居中SVG标签
  13. 2.4-2.8段地址x16+偏移地址=物理地址
  14. 服务器磁盘配置信息,服务器磁盘阵列配置
  15. 计算机相关经典书籍推荐
  16. Spring的AOP(一):什么是AOP
  17. 一心多用多线程-Thread的api探险
  18. Win11怎么开启任务管理器 Win11任务管理器开启方法
  19. 真正内心强大的人是什么样子???
  20. linux-鸟哥私房菜,基础命令全掌握

热门文章

  1. 要么战胜,要么战死,绝不投降
  2. BZOJ 5261 Rhyme
  3. Oracle数据库编程:PL/SQL编程基础
  4. Net设计模式实例之享元模式( Flyweight Pattern)(1)
  5. redismemcachedSQLNoSQL
  6. Javascript中的Trait与代码重用
  7. 最新SMB僵尸网络利用了7个NSA工具,而WannaCry只用了两个……
  8. Java项目转换成Web项目
  9. Linux和windows动态库
  10. [转] 基于 Apache Mahout 构建社会化推荐引擎