Linux netfilter Hacking HOWTO

Posted rtoax

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux netfilter Hacking HOWTO相关的知识,希望对你有一定的参考价值。

Linux netfilter Hacking HOWTO

Rusty Russell and Harald Welte, mailing list netfilter@lists.samba.org

$Revision: 521 $ $Date: 2002-07-02 06:07:19 +0200 (mar, 02 jul 2002) $


This document describes the netfilter architecture for Linux, how to hack it, and some of the major systems which sit on top of it, such as packet filtering, connection tracking and Network Address Translation.


1. Introduction

2. Where Can I Get The Latest?

3. Netfilter Architecture

4. Information for Programmers

5. Translating 2.0 and 2.2 Packet Filter Modules

6. Netfilter Hooks for Tunnel Writers

7. The Test Suite

8. Motivation

9. Thanks

目录

一、 简介

1.1 什么是网络过滤器?

1.2 我们在 2.0 和 2.2 中有什么问题?

1.3 你是谁?

1.4 为什么会崩溃?

2. 我在哪里可以得到最新的?

3. Netfilter 架构

3.1 网络 过滤器基础

3.2 数据包选择:IP 表

包过滤

网络地址转换

伪装、端口转发、透明代理

数据包处理

3.3 连接追踪

3.4 其他补充

4. 程序员须知

4.1 理解ip_tables

ip_tables 数据结构

来自用户空间的 ip_tables

ip_tables 使用和遍历

4.2 扩展iptables

内核

新的匹配功能

新目标

新表

用户空间工具

新的匹配功能

新目标

使用‘libiptc’

4.3 理解 NAT 网络地址转换

连接跟踪

4.4 扩展连接跟踪/NAT

标准 NAT 目标

新协议

内核内部

新的 NAT 目标

协议助手

连接跟踪助手模块

描述

可用的结构和功能

conntrack 辅助模块的示例骨架

NAT 辅助模块

描述

可用的结构和功能

示例 NAT 助手模块

4.5 理解 netfilter 网络过滤器

4.6 编写新的 Netfilter 模块

插入 Netfilter Hooks

处理排队的数据包

从用户空间接收命令

4.7 用户空间中的数据包处理

5. 翻译 2.0 和 2.2 包过滤模块

6. 隧道编写者的 Netfilter Hooks

7. 测试套件

7.1 编写测试

7.2 变量和环境

7.3 有用的工具

gen_ip

rcv_ip

错误

本地IP

7.4 随机建议

8. 动机

9. 谢谢


一、 简介

嗨,大家好。

这份文件是一段旅程;有些地区的旅行很方便,而在其他地区,您会发现自己几乎是孤身一人。我能给你的最好建议是拿一大杯舒适的咖啡或热巧克力,坐在舒适的椅子上,在冒险进入有时危险的网络黑客世界之前吸收内容。

为了更多地了解在 netfilter 框架之上使用基础设施,我建议阅读 Packet Filtering HOWTO NAT HOWTO。有关内核编程的信息,我建议阅读 Rusty 的内核黑客不可靠指南和 Rusty 的内核锁定不可靠指南。

(C) 2000 年保罗“生锈”罗素。根据 GNU GPL 许可。

1.1 什么是网络过滤器?

netfilter 是一个用于数据包处理的框架,位于普通的 Berkeley 套接字接口之外。它有四个部分。

  1. 首先,每个协议都定义了“钩子”(IPv4 定义了 5 个),它们是数据包遍历该协议栈的明确定义的点。在这些点中的每一个点,协议都会使用数据包和钩子号调用 netfilter 框架。
  2. 其次,内核的某些部分可以注册以侦听每个协议的不同钩子。因此,当一个数据包被传递到 netfilter 框架时,它会检查是否有人注册了该协议和钩子;如果是这样,他们每个人都有机会按顺序检查(并可能改变)数据包,然后丢弃数据包 ( NF_DROP),允许它通过 ( NF_ACCEPT),告诉 netfilter 忘记数据包 ( NF_STOLEN),或者让 netfilter将数据包排队用户空间的数据包 ( NF_QUEUE)。
  3. 第三部分是已经排队的数据包被收集(由 ip_queue 驱动程序)发送到用户空间;这些数据包是异步处理的。
  4. 最后一部分由代码和文档中的很酷的注释组成。这对于任何实验项目都是有用的。netfilter 的座右铭是(无耻地从 Cort Dougan 那里偷来的):
        ``So... how is this better than KDE?''

(这个座右铭勉强排除了‘鞭打我,打败我,让我使用 ipchains’)。

除了这个原始框架之外,还编写了各种模块,提供类似于以前(pre-netfilter)内核的功能,特别是可扩展的 NAT 系统和可扩展的数据包过滤系统 (iptables)。

1.2 我们在 2.0 和 2.2 中有什么问题?


  1. 没有建立用于将数据包传递到用户空间的基础设施:
    • 内核编码很难
    • 内核编码必须在 C/C++ 中完成
    • 动态过滤策略不属于内核
    • 2.2 引入了通过 netlink 将数据包复制到用户空间,但重新注入数据包很慢,并且需要进行“健全性”检查。例如,重新注入声称来自现有接口的数据包是不可能的。
  2. 透明代理是一个缸:
    • 我们查找每个数据包以查看是否有绑定到该地址的套接字
    • 允许 root 绑定到外部地址
    • 无法重定向本地生成的数据包
    • REDIRECT 不处理 UDP 回复:将 UDP 命名数据包重定向到 1153 不起作用,因为某些客户端不喜欢来自端口 53 以外的任何回复。
    • REDIRECT 不与 tcp/udp 端口​​分配协调:用户可能会得到一个被 REDIRECT 规则遮蔽的端口。
    • 在 2.1 系列中至少被破坏过两次。
    • 代码非常具有侵入性。考虑 2.2.1 中 #ifdef CONFIG_IP_TRANSPARENT_PROXY 数量的统计数据:11 个文件中出现 34 次。将此与 CONFIG_IP_FIREWALL 进行比较,后者在 5 个文件中出现了 10 次。
  3. 创建独立于接口地址的数据包过滤规则是不可能的:
    • 必须知道本地接口地址以区分本地生成或本地终止的数据包和直通数据包。
    • 在重定向或伪装的情况下,这还不够。
    • 前向链只有关于传出接口的信息,这意味着您必须使用网络拓扑知识来确定数据包的来源。
  4. 伪装被添加到数据包过滤上:

    包过滤和伪装之间的相互作用使防火墙变得复杂:

    • 在输入过滤时,回复数据包似乎是发往 box 本身
    • 在前向过滤时,根本看不到伪装的数据包
    • 在输出过滤时,数据包似乎来自本地框
  5. TOS 操作、重定向、ICMP 不可达和标记(可以影响端口转发、路由和 QoS)也被添加到数据包过滤器代码中。https://rtoax.blog.csdn.net/article/details/108748689
  6. ipchains 代码既不是模块化的,也不是可扩展的(例如 MAC 地址过滤、选项过滤等)。
  7. 缺乏足够的基础设施导致了大量不同的技术:
    • 伪装,加上每个协议模块
    • 通过路由代码的快速静态 NAT(没有每个协议处理)
    • 端口转发、重定向、自动转发
    • Linux NAT 和虚拟服务器项目。
  8. CONFIG_NET_FASTROUTE 与包过滤不兼容:
    • 转发的数据包无论如何都要遍历三个链
    • 无法判断这些链是否可以绕过
  9. 无法检查由于路由保护(例如源地址验证)而丢弃的数据包。
  10. 无法自动读取数据包过滤规则上的计数器。
  11. CONFIG_IP_ALWAYS_DEFRAG 是一个编译时选项,对于想要一个通用内核的发行版来说很困难。

1.3 你是谁?

我是唯一一个愚蠢到这样做的人。作为 ipchains 的合著者和当前的 Linux 内核 IP 防火墙维护者,我看到人们在当前系统中遇到的许多问题,以及他们正在尝试做的事情。

1.4 为什么会崩溃?

哇!你应该已经看到了过去的一周!

因为我不是我们所有人都希望的那么出色的程序员,而且由于缺乏时间、设备和/或灵感,我当然没有测试过所有场景。我确实有一个测试套件,我鼓励你为它做出贡献。

2. 我在哪里可以得到最新的?

netfilter.org 上有一个 CVS 服务器,其中包含最新的 HOWTO、用户空间工具和测试套件。对于随意浏览,您可以使用 Web 界面

要获取最新资源,您可以执行以下操作:


  1. 匿名登录netfilter CVS服务器:
    cvs -d :pserver:cvs@pserver.netfilter.org:/cvspublic login
    
  2. 当它要求你输入密码时,输入“cvs”。
  3. 使用以下代码查看代码:
    # cvs -d :pserver:cvs@pserver.netfilter.org:/cvspublic co netfilter/userspace
    
  4. 要更新到最新版本,请使用
    cvs update -d -P

3. Netfilter 架构

Netfilter 只是协议栈(现阶段,IPv4、IPv6 和 DECnet)中各个点的一系列钩子。(理想化的)IPv4 遍历图如下所示:

A Packet Traversing the Netfilter System:

   --->[1]--->[ROUTE]--->[3]--->[4]--->
                 |            ^
                 |            |
                 |         [ROUTE]
                 v            |
                [2]          [5]
                 |            ^
                 |            |
                 v            |

左边是数据包进来的地方:

  1. 通过简单的健全性检查(即,没有被截断,IP 校验和正常,没有混杂接收),它们被传递到 netfilter 框架的 NF_IP_PRE_ROUTING [1] 钩子。
  2. 接下来,他们输入路由代码,该代码决定数据包的目的地是另一个接口还是本地进程。路由代码可能会丢弃不可路由的数据包。
  3. 如果它的目的地是盒子本身,则在传递给进程(如果有)之前,将再次为 NF_IP_LOCAL_IN [2] 钩子调用 netfilter 框架。
  4. 如果它注定要传递到另一个接口,则会为 NF_IP_FORWARD [3] 钩子调用 netfilter 框架。
  5. 然后,数据包通过最后的 netfilter 钩子,NF_IP_POST_ROUTING [4] 钩子,然后再次连接到网络上。
  6. NF_IP_LOCAL_OUT [5] 钩子为本地创建的数据包调用。在这里可以看到路由是在调用这个钩子之后发生的:其实是先调用路由代码(为了弄清楚源IP地址和一些IP选项):如果要改变路由,必须改变`skb ->dst' 字段,就像在 NAT 代码中所做的那样。

3.1 网络 过滤器基础

现在我们有一个 IPv4 的 netfilter 示例,您可以看到每个钩子何时被激活。这就是netfilter的精髓。

https://rtoax.blog.csdn.net/article/details/117161170

内核模块可以注册以侦听这些钩子中的任何一个。注册函数的模块必须在钩子内指定函数的优先级;然后当从核心网络代码调用那个 netfilter 钩子时,在那个点注册的每个模块都按照优先级的顺序被调用,并且可以自由地操作数据包。然后,该模块可以告诉 netfilter 执行以下五件事之一:


  1. NF_ACCEPT:继续正常遍历。
  2. NF_DROP:丢弃数据包;不要继续遍历。
  3. NF_STOLEN:我已经接管了数据包;不要继续遍历。
  4. NF_QUEUE:将数据包排队(通常用于用户空间处理)。
  5. NF_REPEAT:再次调用这个钩子。

netfilter 的其他部分(处理排队的数据包,很酷的注释)将在内核部分稍后介绍。

在此基础上,我们可以构建相当复杂的数据包操作,如下两节所示。

3.2 数据包选择:IP 表

一个名为 IP 表的数据包选择系统是基于 netfilter 框架构建的。它是 ipchains(来自 ipfwadm,来自 BSD 的 ipfw IIRC)的直接后代,具有可扩展性。内核模块可以注册一个新表,并请求一个数据包来遍历给定的表。这种数据包选择方法用于数据包过滤(`filter' 表)、网络地址转换(`nat' 表)和一般的路由前数据包处理(`mangle' 表)。

注册到 netfilter 的钩子如下(每个钩子中的函数按照它们实际被调用的顺序):

   --->PRE------>[ROUTE]--->FWD---------->POST------>
       Conntrack    |       Mangle   ^    Mangle
       Mangle       |       Filter   |    NAT (Src)
       NAT (Dst)    |                |    Conntrack
       (QDisc)      |             [ROUTE]
                    v                |
                    IN Filter       OUT Conntrack
                    |  Conntrack     ^  Mangle
                    |  Mangle        |  NAT (Dst)
                    v                |  Filter

包过滤

这个表,‘filter’,不应该改变数据包:只过滤它们。

iptables 过滤器相对于 ipchains 的优点之一是它小而快,它在 NF_IP_LOCAL_IN、NF_IP_FORWARD 和 NF_IP_LOCAL_OUT 点挂接到 netfilter。这意味着对于任何给定的数据包,都有一个(并且只有一个)可能的地方可以过滤它。这使得用户的事情比 ipchains 简单得多。此外,netfilter 框架为 NF_IP_FORWARD 钩子提供输入和输出接口的事实意味着许多类型的过滤要简单得多。

注意:我已将 ipchains 和 ipfwadm 的内核部分作为模块移植到 netfilter 之上,从而无需升级即可使用旧的 ipfwadm 和 ipchains 用户空间工具。

网络地址转换

这是“nat”表的领域,它从两个 netfilter 钩子馈送数据包:对于非本地数据包,NF_IP_PRE_ROUTING 和 NF_IP_POST_ROUTING 钩子分别适合目标和源更改。如果定义了 CONFIG_IP_NF_NAT_LOCAL,则钩子 NF_IP_LOCAL_OUT 和 NF_IP_LOCAL_IN 用于更改本地数据包的目的地。

该表与“过滤器”表略有不同,因为只有新连接的第一个数据包会遍历该表:此遍历的结果然后应用于同一连接中所有未来的数据包。

伪装、端口转发、透明代理

我将 NAT 分为源 NAT(第一个数据包的源已更改)和目标 NAT(第一个数据包的目标已更改)。

伪装是源 NAT 的一种特殊形式:端口转发和透明代理是目标 NAT 的特殊形式。这些现在都是使用 NAT 框架完成的,而不是独立的实体。

数据包处理

包修改表('mangle'表)用于包信息的实际改变。示例应用程序是 TOS 和 TCPMSS 目标。mangle 表挂钩到所有五个 netfilter 挂钩中。(请注意这在内核 2.4.18 中发生了变化。以前的内核没有将 mangle 附加到所有钩子上)

3.3 连接追踪

连接跟踪是 NAT 的基础,但它是作为一个单独的模块实现的;这允许扩展包过滤代码以简单而干净地使用连接跟踪(“状态”模块)。

3.4 其他补充

新的灵活性既提供了做真正时髦的事情的机会,也为人们编写可以混合和匹配的增强或完整替换提供了机会。

4. 程序员须知

我会告诉你一个秘密:我的宠物仓鼠完成了所有的编码。在我的宠物的宏伟计划中,我只是一个通道,一个“前线”。所以,如果有错误,请不要怪我。怪可爱的,毛茸茸的。

4.1 理解ip_tables

iptables 只是在内存中提供一个命名的规则数组(因此命名为“iptables”),以及诸如来自每个钩子的数据包应该从哪里开始遍历的信息。注册表后,用户空间可以使用getsockopt() 和setsockopt() 读取和替换其内容。

https://rtoax.blog.csdn.net/article/details/117161170

iptables 不注册任何 netfilter 钩子:它依赖其他模块来做到这一点,并根据需要向它提供数据包;模块必须分别注册 netfilter 钩子和 ip_tables,并提供在到达钩子时调用 ip_tables 的机制。

ip_tables 数据结构

为方便起见,用户空间和内核内部使用相同的数据结构来表示规则,尽管少数字段仅在内核内部使用。

每个规则由以下部分组成:

  1. 一个 `struct ipt_entry'。
  2. 零个或多个“struct ipt_entry_match”结构,每个结构都附加了可变数量(0 或更多字节)的数据。
  3. “struct ipt_entry_target”结构,附加了可变数量(0 或更多字节)的数据。

正如我们将看到的,规则的可变性质为扩展提供了巨大的灵活性,特别是当每个匹配项或目标可以携带任意数量的数据时。然而,这确实会产生一些陷阱:我们必须注意对齐。我们通过确保“ipt_entry”、“ipt_entry_match”和“ipt_entry_target”结构的大小方便,并且使用 IPT_ALIGN() 宏将所有数据四舍五入到机器的最大对齐来做到这一点。

/* This structure defines each of the firewall rules.  Consists of 3
   parts which are 1) general IP header stuff 2) match specific
   stuff 3) the target to perform if the rule matches */
struct ipt_entry {
	struct ipt_ip ip;

	/* Mark with fields that we care about. */
	unsigned int nfcache;

	/* Size of ipt_entry + matches */
	__u16 target_offset;
	/* Size of ipt_entry + matches + target */
	__u16 next_offset;

	/* Back pointer */
	unsigned int comefrom;

	/* Packet and byte counters. */
	struct xt_counters counters;

	/* The matches (if any), then the target. */
	unsigned char elems[0];
};

`struct ipt_entry' 具有以下字段:

  1. `struct ipt_ip' 部分,包含要匹配的 IP 标头的规范。
  2. 一个“nf_cache”位域,显示该规则检查数据包的哪些部分。
  3. 一个“target_offset”字段,指示从 ipt_entry_target 结构开始的规则开始处的偏移量。这应该始终正确对齐(使用 IPT_ALIGN 宏)。
  4. “next_offset”字段指示此规则的总大小,包括匹配项和目标。这也应该使用 IPT_ALIGN 宏正确对齐。
  5. 内核用于跟踪数据包遍历的“comefrom”字段。
  6. `struct ipt_counters' 字段包含与此规则匹配的数据包的数据包和字节计数器。

#define ipt_entry_match xt_entry_match
struct xt_entry_match {
	union {
		struct {
			__u16 match_size;

			/* Used by userspace */
			char name[XT_EXTENSION_MAXNAMELEN];
			__u8 revision;
		} user;
		struct {
			__u16 match_size;

			/* Used inside the kernel */
			struct xt_match *match;
		} kernel;

		/* Total length */
		__u16 match_size;
	} u;

	unsigned char data[0];
};
struct xt_entry_target {
	union {
		struct {
			__u16 target_size;

			/* Used by userspace */
			char name[XT_EXTENSION_MAXNAMELEN];
			__u8 revision;
		} user;
		struct {
			__u16 target_size;

			/* Used inside the kernel */
			struct xt_target *target;
		} kernel;

		/* Total length */
		__u16 target_size;
	} u;

	unsigned char data[0];
};

`struct ipt_entry_match' 和 `struct ipt_entry_target' 非常相似,因为它们包含一个总(IPT_ALIGN'ed)长度字段(分别是 `match_size' 和 `target_size')和一个保存匹配或目标名称的联合(对于用户空间)和一个指针(用于内核)。

由于规则数据结构的复杂性,提供了一些辅助例程:

ipt_get_target()

/* Helper functions */
static __inline__ struct xt_entry_target *
ipt_get_target(struct ipt_entry *e)
{
	return (void *)e + e->target_offset;
}

这个内联函数返回一个指向规则目标的指针。

IPT_MATCH_ITERATE()

/* fn returns 0 to continue iteration */
#define IPT_MATCH_ITERATE(e, fn, args...) \\
	XT_MATCH_ITERATE(struct ipt_entry, e, fn, ## args)

此宏为给定规则中的每个匹配项调用给定函数。该函数的第一个参数是“struct ipt_match_entry”,其他参数(如果有)是提供给 IPT_MATCH_ITERATE() 宏的参数。该函数必须返回零以继续迭代,或返回非零值以停止。

IPT_ENTRY_ITERATE()

/* fn returns 0 to continue iteration */
#define IPT_ENTRY_ITERATE(entries, size, fn, args...) \\
	XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)

此函数采用指向条目的指针、条目表的总大小和要调用的函数。函数的第一个参数是“struct ipt_entry”,其他参数(如果有)是提供给 IPT_ENTRY_ITERATE() 宏的参数。该函数必须返回零以继续迭代,或返回非零值以停止。

来自用户空间的 ip_tables

用户空间有四种操作:读取当前表、读取信息(挂钩位置和表大小)、替换表(并抓取旧计数器)和添加新计数器。

这允许用户空间模拟任何原子操作:这是由 libiptc 库完成的,它为程序提供了方便的“添加/删除/替换”语义。https://github.com/theojulienne/iptables/tree/master/libiptc

因为这些表被转移到内核空间,对齐成为具有不同用户空间和内核空间类型规则的机器的问题(例如具有 32 位用户空间的 Sparc64)。这些情况是通过覆盖“libiptc.h”中这些平台的 IPT_ALIGN 定义来处理的。

ip_tables 使用和遍历

内核从特定钩子指示的位置开始遍历。检查该规则,如果“struct ipt_ip”元素匹配,则依次检查每个“struct ipt_entry_match”(调用与该匹配项关联的匹配函数)。如果 match 函数返回 0,则迭代在该规则上停止。如果它设置`hotdrop'参数为1,数据包也将被立即丢弃(这用于一些可疑数据包,例如在tcp匹配功能中)。

如果迭代继续到最后,计数器递增,检查 `struct ipt_entry_target':如果它是标准目标,则读取 `verdict' 字段(负表示数据包判定,正表示跳转到的偏移量)。如果答案是肯定的并且偏移量不是下一条规则的偏移量,则设置“back”变量,并且将先前的“back”值放置在该规则的“comefrom”字段中。

对于非标准目标,调用目标函数:它返回一个判断(非标准目标不能跳转,因为这会破坏静态循环检测代码)。判决可以是 IPT_CONTINUE,以继续执行下一条规则。

4.2 扩展iptables

因为我很懒,iptables是相当可扩展的。这基本上是一个将工作交给其他人的骗局,这就是开源的全部意义(参见自由软件,正如 RMS 所说,它是关于自由的,当我写这篇文章时,我正坐在他的一次演讲中)。

扩展iptables可能涉及两个部分:通过编写新模块扩展内核,以及iptables通过编写新共享库可能扩展用户空间程序。

内核

从示例中可以看出,编写内核模块本身相当简单。需要注意的一件事是您的代码必须是可重入的:一个数据包可能来自用户空间,而另一个数据包到达中断。事实上,在 SMP 中,在 2.3.4 及更高版本中,每个 CPU 的中断上可以有一个数据包。

您需要了解的功能是:

init_module()

这是模块的入口点。它返回一个负错误号,如果它成功地向 netfilter 注册自己,则返回 0。

clean_module()

这是模块的出口点;它应该向 netfilter 注销自己。

ipt_register_match()

这用于注册新的匹配类型。你给它一个`struct ipt_match',它通常被声明为一个静态(文件范围)变量。

ipt_register_target()

这用于注册新类型。你给它一个`struct ipt_target',它通常被声明为一个静态(文件范围)变量。

ipt_unregister_target()

用于取消注册您的目标。

ipt_unregister_match()

用于取消注册您的比赛。

关于在新比赛或目标的额外空间中做棘手事情(例如提供计数器)的警告。在 SMP 机器上,使用 memcpy 为每个 CPU 复制整个表:如果你真的想保留中心信息,你应该看到在“限制”匹配中使用的方法。

新的匹配功能

新的匹配函数通常被编写为一个独立的模块。可以依次扩展这些模块,尽管这通常不是必需的。一种方法是使用 netfilter 框架的 `nf_register_sockopt' 函数来允许用户直接与您的模块对话。另一种方法是导出其他模块的符号以注册自己,就像 netfilter 和 ip_tables 所做的那样。

新匹配函数的核心是它传递给“ipt_register_match()”的结构体 ipt_match。此结构具有以下字段:

列表

这个字段被设置为任何垃圾,比如“{NULL, NULL }”。

名称

该字段是匹配函数的名称,由用户空间引用。名称应与模块名称匹配(即,如果名称为“mac”,则模块必须为“ipt_mac.o”)以便自动加载工作。

比赛

这个字段是一个指向匹配函数的指针,它接受 skb、输入和输出设备指针(其中一个可能为 NULL,取决于钩子)、一个指向所处理规则中的匹配数据的指针(在用户空间准备好的结构)、IP 偏移量(非零表示非头片段)、指向协议头的指针(即刚好经过 IP 头)、数据长度(即数据包长度)减去 IP 标头长度),最后是一个指向 `hotdrop' 变量的指针。如果数据包匹配,它应该返回非零值,如果返回 0,可以将 `hotdrop' 设置为 1,以指示必须立即丢弃该数据包。

检查入口

该字段是指向检查规则规范的函数的指针;如果返回 0,则用户不会接受该规则。例如,“tcp”匹配类型将只接受 tcp 数据包,因此如果规则的 `struct ipt_ip' 部分没有指定协议必须是 tcp,则返回零。tablename 参数允许您的匹配项控制它可以在哪些表中使用,并且 `hook_mask' 是可以调用此规则的钩子的位掩码:如果您的匹配项从某些 netfilter 钩子中没有意义,您可以在此处避免这种情况.

破坏

该字段是一个指向函数的指针,当使用该匹配项的条目被删除时调用该函数。这允许您在 checkentry 中动态分配资源并在此处清理它们。

这个字段被设置为“THIS_MODULE”,它给出了一个指向你的模块的指针。它会导致使用计数随着该类型规则的创建和销毁而上升和下降。如果规则引用它,这可以防止用户删除模块(因此调用 cleanup_module() )。

新目标

如果你的目标改变了数据包(即头部或主体),它必须调用 skb_unshare() 来复制数据包以防它被克隆:否则任何具有 skbuff 克隆的原始套接字都会看到更改(即人们会在 tcpdump 中看到奇怪的事情)。

新目标通常也被编写为一个独立的模块。上面关于“新匹配函数”部分的讨论在这里同样适用。

新目标的核心是它传递给 ipt_register_target() 的结构体 ipt_target。此结构具有以下字段:

列表

这个字段被设置为任何垃圾,比如“{NULL, NULL }”。

名称

该字段是目标函数的名称,由用户空间引用。名称应与模块名称匹配(即,如果名称为“REJECT”,则模块必须为“ipt_REJECT.o”)以便自动加载工作。

目标

这是一个指向目标函数的指针,它接受 skbuff、钩子号、输入和输出设备指针(其中一个可能为 NULL)、一个指向目标数据的指针以及规则在表中的位置。如果遍历应该继续,目标函数可能会返回 IPT_CONTINUE (-1) 或网络过滤器判定(NF_DROP、NF_ACCEPT、NF_STOLEN 等)。

检查入口

该字段是指向检查规则规范的函数的指针;如果返回 0,则用户不会接受该规则。

破坏

该字段是一个指向函数的指针,该函数在使用该目标的条目被删除时被调用。这允许您在 checkentry 中动态分配资源并在此处清理它们。

这个字段被设置为“THIS_MODULE”,它给出了一个指向你的模块的指针。随着以 this 为目标的规则被创建和销毁,它会导致使用计数上升和下降。如果规则引用它,这可以防止用户删除模块(因此调用 cleanup_module() )。

新表

如果您愿意,您可以为您的特定目的创建一个新表。为此,您使用具有以下字段的“struct ipt_table”调用“ipt_register_table()”:

列表

这个字段被设置为任何垃圾,比如“{NULL, NULL }”。

名称

该字段是表函数的名称,由用户空间引用。名称应与模块的名称匹配(即,如果名称为“nat”,则模块必须为“iptable_nat.o”)以便自动加载工作。

桌子

这是一个完全填充的“struct ipt_replace”,用户空间使用它来替换表。`counters' 指针应该设置为 NULL。这个数据结构可以声明为`__initdata',所以它在启动后被丢弃。

valid_hooks

这是您将使用的 IPv4 netfilter 挂钩的位掩码:这用于检查这些入口点是否有效,并计算 ipt_match 和 ipt_target `checkentry()' 函数的可能挂钩。

这是整个表的读写自旋锁;将其初始化为 RW_LOCK_UNLOCKED。

私人的

这由 ip_tables 代码在内部使用。

用户空间工具

现在您已经编写了漂亮的内核模块,您可能希望从用户空间控制其上的选项。iptables我没有为每个扩展设置一个分支版本,而是使用最新的 90 年代技术:furbies。抱歉,我的意思是共享库。

新表通常不需要任何扩展 iptables:用户只需使用`-t' 选项使其使用新表。

共享库应该有一个 `_init()' 函数,它会在加载时自动调用:这相当于内核模块的 `init_module()' 函数。这应该调用`register_match()' 或`register_target()',具体取决于您的共享库是提供新匹配还是新目标。

你需要提供一个共享库:这可以用来初始化部分结构,或者提供额外的选项。我现在坚持使用共享库,即使它没有做任何事情,以减少共享库丢失的问题报告。

`iptables.h' 头文件中描述了一些有用的函数,特别是:

check_inverse()

检查一个参数是否实际上是一个“!”,如果是,则设置“反转”标志(如果尚未设置)。如果它返回 true,您应该增加 optind,如示例中所做的那样。

string_to_number()

将字符串转换为给定范围内的数字,如果格式错误或超出范围,则返回 -1。`string_to_number' 依赖于 `strtol'(参见手册页),这意味着前导“0x”将使数字以十六进制为基数,前导“0”将使其以八进制为基数。

退出错误()

如果发现错误,则应调用。通常第一个参数是“PARAMETER_PROBLEM”,这意味着用户没有正确使用命令行。

新的匹配功能

您的共享库的 _init() 函数将 `register_match()' 传递给一个指向静态 `struct iptables_match' 的指针,它具有以下字段:

下一个

该指针用于制作匹配的链表(例如用于列出规则)。最初应将其设置为 NULL。

名称

匹配函数的名称。这应该与库名匹配(例如`libipt_tcp.so' 的“tcp”)。

版本

通常设置为 IPTABLES_VERSION 宏:这用于确保iptables二进制文件不会错误地选择错误的共享库。

尺寸

本次比赛的比赛数据大小;您应该使用 IPT_ALIGN() 宏来确保它正确对齐。

用户空间大小

对于某些匹配,内核会在内部更改某些字段(“限制”目标就是这种情况)。这意味着简单的“memcmp()”不足以比较两个规则(删除匹配规则功能需要)。如果是这种情况,请将所有未更改的字段放在结构的开头,并将不变字段的大小放在此处。然而,这通常与“大小”字段相同。

帮助

打印选项概要的函数。

在里面

这可用于初始化 ipt_entry_match 结构中的额外空间(如果有),并设置任何 nfcache 位;如果您正在检查使用 `linux/include/netfilter_ipv4.h' 的内容无法表达的内容,那么只需在 NFC_UNKNOWN 位中进行 OR 即可。它将在 `parse()' 之前被调用。

解析

当在命令行上看到无法识别的选项时会调用它:如果该选项确实适用于您的库,它应该返回非零值。如果 a '!',则 `invert' 为真 已经看到了。`flags' 指针专供您的匹配库使用,通常用于存储已指定选项的位掩码。确保调整 nfcache 字段。如有必要,您可以通过重新分配来扩展 `ipt_entry_match' 结构的大小,但是您必须确保大小通过 IPT_ALIGN 宏传递。

最终检查

这在命令行被解析后被调用,并被传递给你的库保留的“标志”整数。这让您有机会检查是否已指定任何强制选项,例如:如果是这种情况,请调用`exit_error()'。

打印

链表代码使用它来打印(到标准输出)规则的额外匹配信息(如果有)。如果用户指定了‘-n’标志,则设置数字标志。

保存

这与解析相反:`iptables-save' 使用它来重现创建规则的选项。

extra_opts

这是您的库提供的以 NULL 结尾的额外选项列表。这与当前选项合并并交给 getopt_long;有关详细信息,请参阅手册页。getopt_long 的返回码成为你的 `parse()' 函数的第一个参数 (`c')。

此结构的末尾有额外的元素供 内部使用iptables:您不需要设置它们。

新目标

您的共享库的 _init() 函数将 `register_target()' 传递给一个指向静态 `struct iptables_target' 的指针,它具有与上面详述的 iptables_match 结构相似的字段。

使用‘libiptc’

libiptc是 iptables 控制库,设计用于列出和操作 iptables 内核模块中的规则。虽然它目前用于 iptables 程序,但它使编写其他工具变得相当容易。您需要是 root 用户才能使用这些功能。

内核表本身只是一个规则表和一组代表入口点的数字。链名称(“INPUT”等)由库作为抽象提供。用户定义链通过在用户定义链的头部之前插入一个错误节点来标记,该错误节点包含目标的额外数据部分中的链名称(内置链位置由三个表入口点定义)。

支持以下标准目标:ACCEPT、DROP、QUEUE(分别转换为 NF_ACCEPT、NF_DROP 和 NF_QUEUE)、RETURN(转换为由 ip_tables 处理的特殊 IPT_RETURN 值)和 JUMP(从链名称到表中的实际偏移量)。

当`iptc_init()'被调用时,包括计数器在内的表被读取。该表由`iptc_insert_entry()'、`iptc_replace_entry()'、`iptc_append_entry()'、`iptc_delete_entry()'、`iptc_delete_num_entry()'、`iptc_flush_entries()'、`iptc_flush_entries()'、_iptc_replace_entry()'、_iptc_entry()')操作()' `iptc_delete_chain()' 和 `iptc_set_policy()' 函数。

在调用“iptc_commit()”函数之前,表更改不会被写回。这意味着在同一链上运行的两个图书馆用户可以相互竞争;需要锁定来防止这种情况发生,目前还没有这样做。

然而,没有计数器的竞争。计数器以这样一种方式重新添加到内核中,即表的读取和写入之间的计数器增量仍然显示在新表中。

有各种辅助功能:

iptc_first_chain()

此函数返回表中的第一个链名称。

iptc_next_chain()

此函数返回表中的下一个链名称:NULL 表示没有更多链。

iptc_builtin()

如果给定的链名称是内置链的名称,则返回 true。

iptc_first_rule()

这将返回一个指向给定链名称中第一条规则的指针:NULL 表示空链。

iptc_next_rule()

这将返回一个指向链中下一条规则的指针:NULL 表示链的结尾。

iptc_get_target()

这将获得给定规则的目标。如果是扩展目标,则返回该目标的名称。如果它跳转到另一个链,则返回该链的名称。如果是判决(例如 DROP),则返回该名称。如果它没有目标(会计风格的规则),则返回空字符串。

请注意,应使用此函数而不是直接使用 ipt_entry 结构的“verdict”字段的值,因为它提供了上述对标准判断的进一步解释。

iptc_get_policy()

这将获取内置链的策略,并用该策略的命中统计信息填充“计数器”参数。

iptc_strerror()

此函数返回对 iptc 库中故

以上是关于Linux netfilter Hacking HOWTO的主要内容,如果未能解决你的问题,请参考以下文章

Linux SocketCan client server demo hacking

(服务运维)Linux内核防火墙Netfilter和iptables用户工具

转Netfilter机制

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

Linux Netfilter开发小结

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