LVS源码分析三---netfilter hook

版权:自由转载,说明出处,保留原作者名,保留文章的完整性,需要商业用途,请联系作者me.
韩波 yahoo邮箱 GameProgramHack 2010.01.02 深圳市龙岗区坂田镇XXX小山村。

内核版本linux-2.6.32.2
ipvs是基于netfilter框架的,在这里我们先了解一下ipvs的hook在netfilter系统中的位置。
[inline:1=test]
上图中红色标注的hook是ip_vs注册的。
长期工作在linux2.4.35下,对于图中nf_defrag_ipv4.o, iptable_raw.o,iptable_security.o,selinux.o很陌生。

static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
/* After packet filtering, forward packet through VS/DR, VS/TUN,
* or VS/NAT(change destination), so that filtering rules can be
* applied to IPVS. */
/××
× 由上图可见ip_vs的hook是挂在iptable_filter.o模块的钩子后面。
× 因此应用层iptables对应的-t filter表的规则,可以和ip_vs.o一起工作。
× 经过LOCAL_IN的连接,也就是需要转发到后端真实服务器的链接。
× 因此这里ip_vs.o不会把数据包按协议栈的流程发送到传输层,
× 而是根据当前的模式VS/DR或VS/TUN或VS/NAT,经过对应的处理,把包转发给后端真实的服务器。
× 由下面ip_vs_out的钩子可以猜到这个函数在VS/NAT模式下需要实现dnat功能。
×/
{
.hook = ip_vs_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_LOCAL_IN,
.priority = 100,
},
/* After packet filtering, change source only for VS/NAT */
/××
× 由上图可见ip_vs的hook是挂在iptable_filter.o模块的钩子后面。
× 因此应用层iptables对应的-t filter表的规则,可以和ip_vs.o一起工作。
× 根据“change source only for VS/NAT”猜想,这个函数的功能是在VS/NAT
× 模式下,处理真实服务器——>客户端方向的回包,把回包中的源IP,从真实
× 服务器的IP修改为本机(LB)的IP。
× 这里可见判断,NAT实现ip_vs并没有使用netfilter的nat,而是自己实现
× 了一套。
×/
{
.hook = ip_vs_out,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = 100,
},
/* After packet filtering (but before ip_vs_out_icmp), catch icmp
* destined for 0.0.0.0/0, which is for incoming IPVS connections */
/**
* 猜想做client和真实服务器的ICMP中转。
*/
{
.hook = ip_vs_forward_icmp,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_FORWARD,
.priority = 99,
},
/* Before the netfilter connection tracking, exit from POST_ROUTING */
{
.hook = ip_vs_post_routing,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_NAT_SRC-1,
},
};

/*
* It is hooked before NF_IP_PRI_NAT_SRC at the NF_INET_POST_ROUTING
* chain, and is used for VS/NAT.
* It detects packets for VS/NAT connections and sends the packets
* immediately. This can avoid that iptable_nat mangles the packets
* for VS/NAT.
*/
/××
× 由上图可以看到这个钩子是在iptable_nat,selinux.o,ip_conntrack.o模块的钩子之前。
× 设置了skb->ipvs_property这个标志的包,将返回NF_STOP,不会继续跑后面的钩子。
× ip_vs是不能与selinux.o协同工作,启动ip_vs.o后,属于ip_vs的包将不会
× 经过selinux hook处理,导致功能失效。
× 至于iptables_nat, 不经过ip_vs处理的连接,还是可以正常使用iptables -t nat下发的规则。
× 经过ip_vs的包,如果iptables的nat表有对应的规则,那末这个规则将失效。对于经过ip_vs的
× 自己实现的nat。也就是说当一个连接同时符合iptables -nat规则和ip_vs规则的时候,iptables
* nat规则失效,ip_vs正常工作。
×/
static unsigned int ip_vs_post_routing(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
if (!skb->ipvs_property)
return NF_ACCEPT;
/* The packet was sent from IPVS, exit this chain */
return NF_STOP;
}

这里有一个问题:
在LOCAL_IN,优先级为100的钩子钩子有两个ip_vs_in,nf_nat_fn,到底谁在前谁在后?
这种情况,取决于注册钩子的顺序,也是内核模块加载顺序。

既然这样,为什么上图把ip_vs_in画在前,nf_nat_fn画在后?
由于我猜想ip_vs_in要部分接管nat功能,只有ip_vs_in位于nf_nat_fn前面才能实现这个功能。

只有ip_vs_in位于nf_nat_fn前面才能实现这个功能,那就意味这必须先加载ip_vs.o,后加载
iptable_nat.o模块?
对,由于netfilter实现先注册的在前面:
int nf_register_hook(struct nf_hook_ops *reg)
{
struct nf_hook_ops *elem;
int err;

err = mutex_lock_interruptible(&nf_hook_mutex);
if (err < 0)
return err;
list_for_each_entry(elem, &nf_hooks[reg->pf][reg->hooknum], list) {
if (reg->priority < elem->priority)
break;
}
list_add_rcu(&reg->list, elem->list.prev);
mutex_unlock(&nf_hook_mutex);
return 0;
}

这里个人的看法是设序设计当中应该尽量避免二义性,减少依赖性。
建议ip_vs_in的优先改为(100 > x > 50)范围,最好为75。
最佳的方案最好是能和netfilter项目组联系,在下面添加一个属于自己的优先级。不过可能2个不同的开源项目操作起来不方便。
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_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX,
};

广告过后,精彩等着你... ...

附件大小
Image icon linux2.6.32.2netfilter.JPG219.21 KB