【博客587】ipvs hook点在netfilter中的位置以及优先级

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

ipvs hook点在netfilter中的位置以及优先级

1、netfilter栈全景图

在这里插入图片描述

2、Netfilter hooks

五个hook点
在这里插入图片描述

每个 hook 在内核网络栈中对应特定的触发点位置以 IPv4 协议栈为例有以下 netfilter hooks 定义
在这里插入图片描述

  • NF_INET_PRE_ROUTING: 这个 hook 在 IPv4 协议栈的 ip_rcv 函数或 IPv6 协议栈的 ipv6_rcv 函数中执行。所有接收数据包到达的第一个 hook 触发点实际上新版本 Linux 增加了 INGRESS hook 作为最早触发点在进行路由判断之前执行。
  • NF_INET_LOCAL_IN: 这个 hook 在 IPv4 协议栈的 ip_local_deliver() 函数或 IPv6 协议栈的 ip6_input() 函数中执行。经过路由判断后所有目标地址是本机的接收数据包到达此 hook 触发点。
  • NF_INET_FORWARD: 这个 hook 在 IPv4 协议栈的 ip_forward() 函数或 IPv6 协议栈的 ip6_forward() 函数中执行。经过路由判断后所有目标地址不是本机的接收数据包到达此 hook 触发点。
  • NF_INET_LOCAL_OUT: 这个 hook 在 IPv4 协议栈的 __ip_local_out() 函数或 IPv6 协议栈的 __ip6_local_out() 函数中执行。所有本机产生的准备发出的数据包在进入网络栈后首先到达此 hook 触发点。
  • NF_INET_POST_ROUTING: 这个 hook 在 IPv4 协议栈的 ip_output() 函数或 IPv6 协议栈的 ip6_finish_output2() 函数中执行。本机产生的准备发出的数据包或者转发的数据包在经过路由判断之后 将到达此 hook 触发点。

3、iptables四表五链

通过iptables下发的规则最终都会与上图中标注的5个hook点位关联。iptables将这些规则按照不同的作用划分到不同的表中按照划分常用的有raw、mangle、filter、nat四张表即为四表。而关联在5个hook点位的有优先级顺序的规则链即为五链。这种配置管理逻辑也就是使用iptables的人都最为熟知的“四表五链”。

在这里插入图片描述
在这里插入图片描述

每个链上的每个表都可以挂载多条规则
在这里插入图片描述
表之间也是有优先级的

raw > mangle > nat > filter

3、netfilter hook点

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,
}

hook点优先级数值越低优先级越高

enum nf_ip_hook_priorities {
 	NF_IP_PRI_FIRST = INT_MIN,
 	NF_IP_PRI_CONNTRACK_DEFRAG = -400,
 	NF_IP_PRI_RAW = -300,
	NF_IP_PRI_SELINUX_FIRST = -225,
	NF_IP_PRI_CONNTRACK = -200,
	NF_IP_PRI_MANGLE = -150,
	NF_IP_PRI_NAT_DST = -100,
	NF_IP_PRI_FILTER = 0,
	NF_IP_PRI_SECURITY = 50,
	NF_IP_PRI_NAT_SRC = 100,
	NF_IP_PRI_SELINUX_LAST = 225,
	NF_IP_PRI_CONNTRACK_HELPER = 300,
	NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
	NF_IP_PRI_LAST = INT_MAX,
};

Hook点和Hook函数的优先级整合

在这里插入图片描述

hook函数执行过程

同一个HPHookPoint可以注册多个hook函数并且这些hook函数的优先级并不相同会按照优先级先后顺序来依次执行同时系统要保证每一个hook函数都会被执行那么就要求每一个hook函数在处理完数据包后都必须向Netfilter报告处理结果。如果数据包在前面某个hook函数处理完后被永久性的借走了该hook函数又没有向Netfilter报告那么后面的hook函数将不知道该如何处理。所有hook函数的返回值将只能是以下几个之一这些值定义

#define NF_DROP 0      //丢弃
#define NF_ACCEPT 1    //保留
#define NF_STOLEN 2    //忘掉报文不再往上传递区别于NF_DROP它没有调用kfree_skb()来释放skb
#define NF_QUEUE 3     //插入用户空间队列中
#define NF_REPEAT 4    //再次调用本hook函数
#define NF_STOP 5      //停止后面的hook函数的执行直接返回
#define NF_MAX_VERDICT NF_STOP

4、netfilter与Iptables关系

在这里插入图片描述

5、每个链中的表执行关系

当一个包触发 netfilter hook 时处理过程将沿着列从上向下执行在这里插入图片描述

注意

特定事件会导致 table 的 chain 被跳过。例如只有每个连接的第一个包会去匹配 NAT 规则对这个包的动作会应用于此连接后面的所有包。到这个连接的应答包会被自动应用反 方向的 NAT 规则因为内核的conntrack机制。

6、ipvs hook点与iptables hook点的先后关系

netfilter中hook点优先级数值越低优先级越高

enum nf_ip_hook_priorities {
	NF_IP_PRI_FIRST = INT_MIN,
	NF_IP_PRI_RAW_BEFORE_DEFRAG = -450,
	NF_IP_PRI_CONNTRACK_DEFRAG = -400,
	NF_IP_PRI_RAW = -300,
	NF_IP_PRI_SELINUX_FIRST = -225,
	NF_IP_PRI_CONNTRACK = -200,
	NF_IP_PRI_MANGLE = -150,
	NF_IP_PRI_NAT_DST = -100,
	NF_IP_PRI_FILTER = 0,
	NF_IP_PRI_SECURITY = 50,
	NF_IP_PRI_NAT_SRC = 100,
	NF_IP_PRI_SELINUX_LAST = 225,
	NF_IP_PRI_CONNTRACK_HELPER = 300,
	NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
	NF_IP_PRI_LAST = INT_MAX,
};

绘图
在这里插入图片描述
在这里插入图片描述

为什么默认情况下ipvs会跳过conntrack
在这里插入图片描述
看看iptables hook点的优先级具体数值查询上面netfilter优先级表

iptables_filter表优先级

...
/** filter表挂载在LOCAL_IN、FORWORD、LOCAL_OUT hook点上 **/
#define FILTER_VALID_HOOKS ((1 << NF_INET_LOCAL_IN) | \
			    (1 << NF_INET_FORWARD) | \
			    (1 << NF_INET_LOCAL_OUT))

...

static const struct xt_table packet_filter = {
	.name		= "filter",
	.valid_hooks	= FILTER_VALID_HOOKS,
	.me		= THIS_MODULE,
	.af		= NFPROTO_IPV4,
	.priority	= NF_IP_PRI_FILTER,
	.table_init	= iptable_filter_table_init,
};
...

mangle表优先级

...
/** mangle表挂载在所有的hook点上 **/
#define MANGLE_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \
			    (1 << NF_INET_LOCAL_IN) | \
			    (1 << NF_INET_FORWARD) | \
			    (1 << NF_INET_LOCAL_OUT) | \
			    (1 << NF_INET_POST_ROUTING))

...

static const struct xt_table packet_mangler = {
	.name		= "mangle",
	.valid_hooks	= MANGLE_VALID_HOOKS,
	.me		= THIS_MODULE,
	.af		= NFPROTO_IPV4,
	.priority	= NF_IP_PRI_MANGLE,
	.table_init	= iptable_mangle_table_init,
};
...

nat表优先级

...
/** nat表挂载在PRE_ROUTING、POST_ROUTING、LOCAL_OUT和LOCAL_IN hook点上**/
static const struct xt_table nf_nat_ipv4_table = {
	.name		= "nat",
	.valid_hooks	= (1 << NF_INET_PRE_ROUTING) |
			  (1 << NF_INET_POST_ROUTING) |
			  (1 << NF_INET_LOCAL_OUT) |
			  (1 << NF_INET_LOCAL_IN),
	.me		= THIS_MODULE,
	.af		= NFPROTO_IPV4,
	.table_init	= iptable_nat_table_init,
};

...

/** 
nat表又细分为dnat和snat具备不同的优先级
dnat挂载在PRE_ROUTING和OUTPUT hook点上
snat挂载在INPUT和POSTROUTING hook点上
**/
static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
	{
		.hook		= iptable_nat_do_chain,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_PRE_ROUTING,
		.priority	= NF_IP_PRI_NAT_DST,
	},
	{
		.hook		= iptable_nat_do_chain,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_POST_ROUTING,
		.priority	= NF_IP_PRI_NAT_SRC,
	},
	{
		.hook		= iptable_nat_do_chain,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_OUT,
		.priority	= NF_IP_PRI_NAT_DST,
	},
	{
		.hook		= iptable_nat_do_chain,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_NAT_SRC,
	},
};

raw表优先级

...
/** raw表挂载在PRE_ROUTING和LOCAL_OUT hook点上 **/
#define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT))

...
static const struct xt_table packet_raw = {
	.name = "raw",
	.valid_hooks =  RAW_VALID_HOOKS,
	.me = THIS_MODULE,
	.af = NFPROTO_IPV4,
	.priority = NF_IP_PRI_RAW,
	.table_init = iptable_raw_table_init,
};

static const struct xt_table packet_raw_before_defrag = {
	.name = "raw",
	.valid_hooks =  RAW_VALID_HOOKS,
	.me = THIS_MODULE,
	.af = NFPROTO_IPV4,
	.priority = NF_IP_PRI_RAW_BEFORE_DEFRAG,
	.table_init = iptable_raw_table_init,
};

security优先级

...
/** security表挂载在LOCAL_IN、FORWARD和LOCAL_OUT hook点上 **/
#define SECURITY_VALID_HOOKS	(1 << NF_INET_LOCAL_IN) | \
				(1 << NF_INET_FORWARD) | \
				(1 << NF_INET_LOCAL_OUT)

...

static const struct xt_table security_table = {
	.name		= "security",
	.valid_hooks	= SECURITY_VALID_HOOKS,
	.me		= THIS_MODULE,
	.af		= NFPROTO_IPV4,
	.priority	= NF_IP_PRI_SECURITY,
	.table_init	= iptable_security_table_init,
};

再看看ipvs hook点的优先级具体数值查询上面netfilter优先级表

static const struct nf_hook_ops ip_vs_ops4[] = {
	/* After packet filtering, change source only for VS/NAT */
	{
		.hook		= ip_vs_reply4,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_NAT_SRC - 2,
	},
	/* After packet filtering, forward packet through VS/DR, VS/TUN,
	 * or VS/NAT(change destination), so that filtering rules can be
	 * applied to IPVS. */
	{
		.hook		= ip_vs_remote_request4,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_IN,
		.priority	= NF_IP_PRI_NAT_SRC - 1,
	},
	/* Before ip_vs_in, change source only for VS/NAT */
	{
		.hook		= ip_vs_local_reply4,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_OUT,
		.priority	= NF_IP_PRI_NAT_DST + 1,
	},
	/* After mangle, schedule and forward local requests */
	{
		.hook		= ip_vs_local_request4,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_LOCAL_OUT,
		.priority	= NF_IP_PRI_NAT_DST + 2,
	},
	/* After packet filtering (but before ip_vs_out_icmp), catch icmp
	 * destined for 0.0.0.0/0, which is for incoming IPVS connections */
	{
		.hook		= ip_vs_forward_icmp,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_FORWARD,
		.priority	= 99,
	},
	/* After packet filtering, change source only for VS/NAT */
	{
		.hook		= ip_vs_reply4,
		.pf		= NFPROTO_IPV4,
		.hooknum	= NF_INET_FORWARD,
		.priority	= 100,
	},
};

有了ipvs hook优先级关系和iptables hook优先级后就可以比较ipvs hook和iptables rule hook的优先级关系以input链分析为例其它链按照这个方式去比较

input链上ipvs的2个hook的优先级如下查上述表
NF_IP_PRI_NAT_SRC - 2  =  100 - 2  =  98
NF_IP_PRI_NAT_SRC - 1  =  100 - 1  =  99

iptables hook在每个表的优先级如下查上述表

NF_IP_PRI_FILTER  = 0
NF_IP_PRI_MANGLE = -150
NF_IP_PRI_NAT_SRC = 100
NF_IP_PRI_SECURITY = 50

得到input链中执行顺序mangle hook > filter hook > ipvs hook > nat hook

给出iptables表的优先级数值关系以方便查询对比
在这里插入图片描述

总结优先级关系

根据定义ipvs挂载在LOCAL_IN、FORWORD和LOCAL_OUT的hook点上了根据优先级我们得到以下结论
1、INPUTipvs的hook会在Filter TABLE 后执行然后转到NAT TABLE
2、OUTPUTipvs的hook会在NAT TABLE 然后转到Filter TABLE
3、FORWARDipvs的hookFilter TABLE后执行然后结束整个FORWARD包往POSTROUTING走

我们在iptables流程图上新增了ipvs的位置图中的流转顺序就是执行顺序比如FORWARD表中manglefilteripvs三个是按顺序排好的从mangle到filter再到ipvs
在这里插入图片描述
在这里插入图片描述

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6