Netfilter之IPVS匹配扩展

人盡茶涼 提交于 2019-12-02 06:43:00

以下iptables命令查看ipvs匹配扩展的帮助信息。可匹配的字段分别为:虚拟服务的协议号、地址、端口、数据流的方向、转发模式以及控制连接的端口号。对于类似FTP的服务,其控制连接的端口为21,数据端口为20。

$ iptables -m ipvs --help    
iptables v1.6.0


IPVS match options:
[!] --ipvs                      packet belongs to an IPVS connection

Any 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!