iptables基本规则
iptables只是用户层用于管理防火墙规则的工具,具体的防火墙是由内核模块netfilter实现
netfilter基本概念
Netfilter是一套融入在Linux内核网络协议栈中的报文处理(过滤或者修改)框架。它在内核中报文的关键流动路径上定义了5个HOOK点(下图蓝色方框),各个协议(如IPv4、IPv6、ARP)可以在这些HOOK点安装钩子函数,报文流经此地,内核会按照优先级调用这些钩子函数,这些钩子函数最终会决定报文是被NF_ACCEPT(放行)还是NF_DROP(丢弃)
如上图,iptables其实是向IP层(绿色的部分)的HOOK点注入一些数据包处理函数,这样当数据包经过相应的hook时,处理函数就被调用,从而实现包过滤功能,系统中还有其他层的HOOK点(ebtables设置Bridge 过滤规则,arptables设置ARP Layer规则)
-
iptables
for IPv4 /NFPROTO_IPV4
-
ip6tables
for IPv6 /NFPROTO_IPV6
-
arptables
for ARP /NFPROTO_ARP
-
ebtables
for Bridging /NFPROTO_BRIDGE
五链(chain)是什么?
是数据包在协议栈下层要经过的5个钩子点,对应netfilter中的五个HOOK
为什么叫链?
因为一HOOK点上可以安装多个钩子(过滤规则),内核用“链条”将这些钩子串起来
四表(table)是什么?
为什么需要表?(表的作用)
上文介绍到防火墙过滤其实就在数据包经过内核关键的节点上设置一些钩子(过滤规则),决定包的去留,为啥还需要表?其实 Netfilter设计多个表的目的是:
1、方便分类管理
- filter表——过滤数据包
- Nat表——用于网络地址转换(IP、端口)
- Mangle表——修改数据包的服务类型、TTL、并且可以配置路由实现QOS
- Raw表——决定数据包是否被状态跟踪机制处理
2、限定各个钩子(或者说过滤规则)执行的顺序
各表的有限顺序:Raw——mangle——nat——filter,也就是当包进过某HOOK点时,优先执行mangle表(如果有)中的过滤规则,再执行其他的。
注:有些HOOK点只有特定的表,就比如网络地址转换的nat表只能存在于:
路由确定前的PREROUTING链中,做目的地址转换
另外存在于路由决定后的,做源地址转换
各概念在内核源码中的实现
钩子点
linux-4.4\include\uapi\linux
钩子函数
linux-4.4\include\linux\netfilter.h
这便是当包进过某钩子点是需要执行的钩子函数(过滤规则),我理解是iptables下发的规则最终解析成这函数,注册到链中。看nf_hookfn函数声明简单可知,应该根据传入sk_buff结构体,做一些判断,返回返回如下结果
着重解释下这两个:
- NF_STOLEN:当钩子函数想要“窃取”一个数据包并避免后续的钩子函数或内核进一步处理时,使用此常量。这意味着钩子函数将拥有该数据包的所有权,需要自行处理或释放该数据包。
- NF_QUEUE:Netfilter会将该数据包放入队列中,并生成一个标识符(即 Queue ID),该标识符用于与用户空间程序进行通信。用户空间程序可以通过libnetfilter_queue库的API从队列中获取数据包,决定是否丢弃该数据包、修改该数据包的内容等,并可以将该数据包发送回内核空间进行后续处理,或者将其丢弃
表(table)
链里有表,还是表里有链?
在网上经常看到这样的图
又有这样的
让人产生到底链里有表,还是表里有链的疑惑,我认为第二张图更容易理解数据如何经过5个链(HOOK),分别匹配各表中规则的流程,而实际源码的数据结构中表是这样表示的
linux-4.4\include\linux\netfilter\x_tables.h
如下面伪代码,我们在定义table结构体时会指定该表支持的链,我想这可能是第一幅图的由来吧
struct xt_table_info my_table = {
// ...
.hook_entry = {[NF_INET_PRE_ROUTING] = my_pre_routing_func,
[NF_INET_FORWARD] = my_forward_func,
// ...
},
// ...
};
上面结构体中hook_entry数组便是到达某节点后该回调的钩子函数
注册钩子函数
支线问题
连接跟踪机制contrack
是什么?
解释下上图中的contrack,这个在之前的图中也出现过,这标识Linux单独的内核模块连接跟踪机制,该机制运行内核“审查”通过此处的包,并能识别出这数据包属于哪个网络连接(比如数据包a属于IP1:8888->IP2:80
这个tcp连接,数据包b属于ip3:9999->IP4:53
这个udp连接),并将这些连接数据(conntrack条目)存在一二维数组结构的哈希表(hash table)中。
可以用cat /proc/net/nf_conntrack
查看当前跟踪的所有conntrack条目
记录这些条目有什么用?
conntrack机制是iptables实现状态匹配(-m state
)以及NAT的基础
如何实现拒绝所有新建立的TCP连接?
iptables -A INPUT -m state --state NEW -j DROP
如何利用conntrack机制实现NAT功能?
先查看下路由器的nat表,如下,所有要出去的包过POSTROUTING时都会执行下面这规则
MASQUERADE是iptables中一种用于实现NAT的动作,主要用于将内部网络(如局域网)的私有IP地址转换为公网IP地址,具体干的操作应该是:
- 当一个包从内网发出去时,根据IP和端口查conntrack表
- 若已被记录在表中,用该连接对应的公网IP和端口来替换数据包的源IP和端口
- 若未记录在表中,为该连接分配新的公网IP和端口号,并将该连接记录在conntrack表中,标记状态为NEW
回到主线
注册钩子函数
学着有什么用,为什么需要注册钩子函数?
netfilter框架在链路层、网络层有很多钩子点,如果想在这抓包调试的话,直接写个netfilter模块,insmod插入到内核(我还没试过)
注册全局的钩子函数
之前我们看到每个iptable规则都指定了特定的表,可不可以不指定表,数据包进过某钩子点时就回调注册的钩子函数?这应该是注册全局的的钩子函数的作用。
linux-4.4\net\netfilter\core.c
上面是注册时调用的源码,其实只需要找到它调用list_add_rcu将钩子函数加入到了一个链表中
如何注册全局钩子函数?
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
static struct nf_hook_ops nfho;
/* Hook function to be called on outgoing packets */
unsigned int hook_func_out(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
/* Manipulate the outgoing packet */
/* ... */
/* Accept the packet */
return NF_ACCEPT;
}
/* Initialization routine */
static int __init init_hook(void)
{
/* Fill in the hook structure */
nfho.hook = hook_func_out; /* Hook function */
nfho.pf = PF_INET; /* IPv4 packets */
nfho.hooknum = NF_INET_POST_ROUTING; /* Post-routing */
nfho.priority = NF_IP_PRI_FIRST; /* Highest priority */
/* Register the hook */
nf_register_net_hook(&init_net, &nfho);
printk(KERN_INFO "Custom netfilter hook registered.\n");
return 0;
}
/* Cleanup routine */
static void __exit exit_hook(void)
{
/* Unregister the hook */
nf_unregister_net_hook(&init_net, &nfho);
printk(KERN_INFO "Custom netfilter hook unregistered.\n");
}
/* Entry point */
module_init(init_hook);
module_exit(exit_hook);
/* Module information */
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Custom netfilter hook example");
MODULE_LICENSE("GPL");
通过注册新的netfilter表的方式注册钩子函数
除了上面注册全局钩子函数,还可以通过注册新的netfilter表的方式注册钩子函数,比如下面代码注册了个名为“my_table”的表,数据经过PREROUTING和FAROWRD节点时会调用此表的钩子函数
也可以iptables向这表中添加新的规则
iptables -t my_table -A OUTPUT -j ACCEPT
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
/* Hook function for the PREROUTING hook */
static unsigned int my_prerouting_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
printk(KERN_INFO "my_prerouting_hook called\n");
return NF_ACCEPT;
}
/* Hook function for the FORWARD hook */
static unsigned int my_forward_hook(void *priv, struct sk_buff *skb,
const struct nf_hook_state *state)
{
printk(KERN_INFO "my_forward_hook called\n");
return NF_ACCEPT;
}
/* Table structure */
static struct xt_table_info my_table = {
.name = "my_table",
.size = sizeof(struct xt_entry),
.number = 10,
.initial_entries = 5,
.hook_entry = {//钩子函数在这!!
[NF_INET_PRE_ROUTING] = my_prerouting_hook,
[NF_INET_FORWARD] = my_forward_hook,
},
.underflow = {},
.stacksize = 4,
.jumpstack = NULL,
};
/* Module initialization function */
static int __init my_module_init(void)
{
int ret;
/* Register the table with Netfilter */
ret = xt_register_table(&my_table, AF_INET);
if (ret != 0) {
printk(KERN_ERR "Failed to register table\n");
return ret;
}
printk(KERN_INFO "Table registered\n");
return 0;
}
/* Module cleanup function */
static void __exit my_module_exit(void)
{
/* Unregister the table from Netfilter */
xt_unregister_table(&my_table, AF_INET);
printk(KERN_INFO "Table unregistered\n");
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
前面可知这些标准表raw filter nat mangle是有优先级顺序的,那自定义的表和标准表谁先执行?
先注册先执行,标准表也需要注册
一图了解xt_register_table做了什么?
应该是在xt_tables数组中在加一个xt_table结构体
- 绿色的箭头 表示指针 hook_entry[NF_INET_PRE_ROUTING]
- 紫色的箭头 表示指针 hook_entry[NF_INET_LOCAL_OUT]
- 绿色的箭头 表示指针 hook_entry[NF_INET_POUST_ROUTING
hook 在内核网络栈中对应特定的触发点位置?
以 IPv4 协议栈为例
以PRE_ROUTING为例,其在ip_rcv函数中触发
看到这rcu是不是有点眼熟,我们在前面注册钩子函数时遇到过
iptables为什么自定义链无法主动调用只能从其它链跳转过来?
自定义链的触发依赖jump target,用户定义的 chain 只能通过从另一个规则跳转(jump)到它,因为它们没有注册到 netfilter hook,因为netfilter hook中没有自定义hook点,只有固定的5个内置点。 用户定义的 chain 可以看作是对调用它的 chain 的扩展
参考文章
浅入浅出 iptables 原理:在内核里骚一把 netfilter~
Netfilter 是如何工作的(二):表(table)与规则(rule)
Linux协议栈-netfilter(5)-iptables
基于Netfilter从零开始写一个Linux防火墙,为我们的迷你防火墙编写代码
http://iptables详解(10):iptables自定义链
【博客598】从netfilter hook执行原理分析iptables为什么自定义链无法主动调用只能从其它链跳转过来
深入理解 netfilter 和 iptables
Nftables - Packet flow and Netfilter hooks in detail
云计算底层技术-netfilter框架研究
Bridge与netfilter