本文主要是介绍Netfilter之IPVS匹配扩展,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
以下iptables命令查看ipvs匹配扩展的帮助信息。可匹配的字段分别为:虚拟服务的协议号、地址、端口、数据流的方向、转发模式以及控制连接的端口号。对于类似FTP的服务,其控制连接的端口为21,数据端口为20。
$ iptables -m ipvs --help
iptables v1.6.0IPVS match options:
[!] --ipvs packet belongs to an IPVS connectionAny of the following options implies --ipvs (even negated)
[!] --vproto protocol VIP protocol to match; by number or name, e.g. "tcp"
[!] --vaddr address[/mask] VIP address to match
[!] --vport port VIP port to match; by number or name, e.g. "http"--vdir {ORIGINAL|REPLY} flow direction of packet
[!] --vmethod {GATE|IPIP|MASQ} IPVS forwarding method used
[!] --vportctl port VIP port of the controlling connection to match, e.g. 21 for FTP
IPVS匹配模块初始化
函数ipvs_mt_init先系统注册IPVS匹配扩展定义xt_ipvs_mt_reg。匹配名称为ipvs,这里定义了匹配函数ipvs_mt,以及检查函数ipvs_mt_check。
static struct xt_match xt_ipvs_mt_reg __read_mostly = {.name = "ipvs",.revision = 0,.family = NFPROTO_UNSPEC,.match = ipvs_mt,.checkentry = ipvs_mt_check,.matchsize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
};static int __init ipvs_mt_init(void)
{return xt_register_match(&xt_ipvs_mt_reg);
}
匹配结构中的matchsize字段,给定了数据结构xt_ipvs_mtinfo的长度,其定义中包含所有要匹配的字段变量。其最后一个成员变量bitmask掩码,取值为XT_IPVS_XX中的一个或多个,置位表明指定了相应的字段;否则,不处理相应的字段。如bitmask中置位XT_IPVS_METHOD,表明指定了转发模式字段fwd_method。
字段invert的取值与bitmask相同,但是invert中的相应位指明进行取反操作。例如,invert中设置了XT_IPVS_METHOD位,表明匹配除fwd_method变量中所指定了转发模式之外的模式。
enum {XT_IPVS_IPVS_PROPERTY = 1 << 0, /* all other options imply this one */XT_IPVS_PROTO = 1 << 1,XT_IPVS_VADDR = 1 << 2,XT_IPVS_VPORT = 1 << 3,XT_IPVS_DIR = 1 << 4,XT_IPVS_METHOD = 1 << 5,XT_IPVS_VPORTCTL = 1 << 6,
}struct xt_ipvs_mtinfo {union nf_inet_addr vaddr, vmask;__be16 vport;__u8 l4proto;__u8 fwd_method;__be16 vportctl;__u8 invert;__u8 bitmask;
};
匹配检查
由注册的函数ipvs_mt_check可知,IPVS匹配模块仅支持IPv4和IPv6两种协议。此函数在匹配函数ipvs_mt之前执行。
static int ipvs_mt_check(const struct xt_mtchk_param *par)
{if (par->family != NFPROTO_IPV4
#ifdef CONFIG_IP_VS_IPV6&& par->family != NFPROTO_IPV6
#endif) {pr_info("protocol family %u not supported\n", par->family);return -EINVAL;}return 0;
执行匹配
因为XT_IPVS_IPVS_PROPERTY属性是一个基础属性,并且之后的其它字段都隐含了此字段,如果bitmask掩码完全等于XT_IPVS_IPVS_PROPERTY,表明仅仅设置了此字段,即iptables配置命令行的命令字:(–ipvs)。根据invert的值决定是否匹配,如果此报文仅有IPVS系统处理,skb的成员ipvs_property为1,如果invert为0,不取反,则匹配发生。
其次,如果报文未经过ipvs系统处理,ipvs_property为0,就没有继续处理的必要了。
static bool ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
{const struct xt_ipvs_mtinfo *data = par->matchinfo;struct netns_ipvs *ipvs = net_ipvs(xt_net(par)); const u_int8_t family = xt_family(par); /* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */struct ip_vs_iphdr iph;if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {match = skb->ipvs_property ^ !!(data->invert & XT_IPVS_IPVS_PROPERTY);goto out;}/* other flags than XT_IPVS_IPVS_PROPERTY are set */if (!skb->ipvs_property) {match = false;goto out;}
函数ip_vs_fill_iph_skb获取数据包中的IP头部信息,保存于ip_vs_iphdr结构类型的变量iph中。匹配字段XT_IPVS_PROTO对应于iptables配置命令的命令选项(–vproto)。注意这里的字段对比与以上XT_IPVS_IPVS_PROPERTY字段的操作不同,如果协议字段相等,并且invert字段为1,表明匹配失败。反之,匹配成功,match的初始值就位true。
其后,根据协议值,获取IPVS的协议处理结构和IPVS连接结构。
ip_vs_fill_iph_skb(family, skb, true, &iph);if (data->bitmask & XT_IPVS_PROTO)if ((iph.protocol == data->l4proto) ^ !(data->invert & XT_IPVS_PROTO)) {match = false;goto out;}pp = ip_vs_proto_get(iph.protocol);if (unlikely(!pp)) {match = false;goto out;}/* Check if the packet belongs to an existing entry */cp = pp->conn_out_get(ipvs, family, skb, &iph);if (unlikely(cp == NULL)) {match = false;goto out;}
以下代码根据IPVS连接结构cp中的信息进行匹配比较,其逻辑与以上的XT_IPVS_PROTO字段一致。包括:XT_IPVS_VPORT、XT_IPVS_VPORTCTL、XT_IPVS_DIR、XT_IPVS_METHOD和XT_IPVS_VADDR等字段。
对于XT_IPVS_VPORTCTL字段,需要找到其IPVS控制连接结构,对比其中的虚拟服务端口,因为此为控制端口。
对于方向XT_IPVS_DIR字段的比较,使用conntrack系统的信息值ctinfo进行匹配。
if (data->bitmask & XT_IPVS_VPORT)if ((cp->vport == data->vport) ^ !(data->invert & XT_IPVS_VPORT)) {match = false; goto out_put_cp;}if (data->bitmask & XT_IPVS_VPORTCTL)if ((cp->control != NULL && cp->control->vport == data->vportctl) ^ !(data->invert & XT_IPVS_VPORTCTL)) {match = false; goto out_put_cp;}if (data->bitmask & XT_IPVS_DIR) {enum ip_conntrack_info ctinfo;struct nf_conn *ct = nf_ct_get(skb, &ctinfo);if (ct == NULL) {match = false; goto out_put_cp;}if ((ctinfo >= IP_CT_IS_REPLY) ^ !!(data->invert & XT_IPVS_DIR)) {match = false; goto out_put_cp;}}if (data->bitmask & XT_IPVS_METHOD)if (((cp->flags & IP_VS_CONN_F_FWD_MASK) == data->fwd_method) ^ !(data->invert & XT_IPVS_METHOD)) {match = false; goto out_put_cp;}if (data->bitmask & XT_IPVS_VADDR) {if (ipvs_mt_addrcmp(&cp->vaddr, &data->vaddr, &data->vmask, family) ^ !(data->invert & XT_IPVS_VADDR)) {match = false; goto out_put_cp;}
IPVS匹配应用
在内核邮件列表中,作者提供了一种利用ipvs匹配实现全NAT(SNAT+DNAT)的转发模式。IPVS的NAT/Masq转发模式实现的是DNAT转发,以下iptables命令开启SNAT功能。
# ipvsadm -A -t 192.168.100.30:80 -s rr
# ipvsadm -a -t 192.168.100.30:80 -r 192.168.10.20:80 -m
# ... # Source NAT for VIP 192.168.100.30:80
# iptables -t nat -A POSTROUTING -m ipvs --vaddr 192.168.100.30/32 --vport 80 -j SNAT --to-source 192.168.10.10 or SNAT-ing only a specific real server: # iptables -t nat -A POSTROUTING --dst 192.168.10.20 -m ipvs --vaddr 192.168.100.30/32 -j SNAT --to-source 192.168.10.10
以上命令将到达虚拟服务:192.168.100.30:80的流量,进行SNAT转换,源地址变换为:192.168.10.10。或者仅仅对调度到真实服务器192.168.10.20的流量,进行SNAT转换。
这里有一个问题,如果指定的虚拟服务的端口为FTP端口21,对其进行SNAT操作,那么对于FTP的数据通道,其端口并不是21,就需要以下的命令为其数据连接指定SNAT,这里使用命令选项(–vportctl)。
# SNAT FTP control connection
# iptables -t nat -A POSTROUTING -m ipvs --vaddr 192.168.100.30/32 --vport 21 -j SNAT --to-source 192.168.10.10 # SNAT FTP passive data connection
# iptables -t nat -A POSTROUTING -m ipvs --vaddr 192.168.100.30/32 --vportctl 21 -j SNAT --to-source 192.168.10.10
以上的配置SNAT方式,在开启IPVS同步功能时,在主和被机上将产生不同的行为,
内核版本 5.0
这篇关于Netfilter之IPVS匹配扩展的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!