netfilter之match和target

匹配规则

iptables的rule中,匹配规则包含标准匹配和扩展匹配。

  1. 标准匹配
    标准匹配使用如下结构体表示,包含源目的ip,出入接口,协议号等。

struct ipt_ip {
    /* Source and destination IP addr */
    struct in_addr src, dst;
    /* Mask for src and dest IP addr */
    struct in_addr smsk, dmsk;
    char iniface[IFNAMSIZ], outiface[IFNAMSIZ];
    unsigned char iniface_mask[IFNAMSIZ], outiface_mask[IFNAMSIZ];

    /* Protocol, 0 = ANY */
    __u16 proto;

    /* Flags word */
    __u8 flags;
    /* Inverse flags */
    __u8 invflags;
};

通过下面几个选项指定标准匹配规则

-p, --protocol [!] protocol
       The specified protocol can be one of tcp, udp, icmp, or all, or it can be a         
       numeric value, representing one of these protocols or a different one. A 
       protocol name from /etc/protocols is also allowed
-s, --source [!] address[/mask]
-d, --destination [!] address[/mask]
-i, --in-interface [!] name
-o, --out-interface [!] name
  1. 扩展匹配
    有两种方法指定扩展匹配规则:
    a. 通过 -p/--protocol 隐式的指定。即-p既指定了标准匹配的协议,也顺便指定了扩展匹配,但不是所有协议都支持扩展匹配,只有注册了扩展匹配的协议才可以,比如tcp,udp在函数tcpudp_mt_init中注册成为了扩展匹配。
    b. 通过 -m/--match 显示的指定。

可以使用 -h/--help查看指定扩展匹配的帮助信息。如下查看state扩展匹配的帮助信息:

# iptables -m state -h
...
state match options:
 [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]
                                State(s) to match

下面这条规则中,-i eth0指定了标准匹配的入接口,-p既指定了标准匹配的协议号,又指定了扩展规则为udp,--dport为扩展规则udp的参数。

iptables –A INPUT –i eth0 –p udp –dport 137:138 –j ACCEPT 

目标target

报文如果匹配到规则,就要执行此规则指定的target。
target也分为标准target和扩展target。
标准target:用户自定义链和内建target ACCEPT, DROP, QUEUE or RETURN。
扩展target:extention模块。已经支持很多扩展target,包括DNAT,MARK,LOG等。可以使用 -h/--help查看指定扩展target的帮助信息,如下查看SNAT模块的帮助信息:

# iptables -j SNAT  -h
...
SNAT target options:
 --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]
                                Address to map source to.
[--random] [--random-fully] [--persistent]

下面这条规则,指定的target为扩展target SNAT。

iptables –t nat –A POSTROUTING –s 192.168.10.10 –o eth1 –j SNAT --to-source 111.196.221.212

可通过 -j 指定所有target,但是 -g 只能指定自定义链。

扩展匹配规则和扩展目标是以kernel module形式存在的,所以需要提前加载到内核中,否则添加规则时会报错。

注册扩展匹配规则和扩展目标

首先看一个全局变量xt,其为数组链表,类型为struct xt_af,match成员用来保存注册的扩展匹配规则,target成员用来保存注册的扩展目标。xt在xt_init初始化时分配内存,大小为NFPROTO_NUMPROTO乘sizeof(struct xt_af),这说明每种协议使用一个链表。

struct xt_af {
    struct mutex mutex;
    struct list_head match;
    struct list_head target;
};

static struct xt_af *xt;

xt_init
    xt = kmalloc(sizeof(struct xt_af) * NFPROTO_NUMPROTO, GFP_KERNEL);
    if (!xt)
        return -ENOMEM;

    for (i = 0; i < NFPROTO_NUMPROTO; i++) {
        mutex_init(&xt[i].mutex);
        INIT_LIST_HEAD(&xt[i].target);
        INIT_LIST_HEAD(&xt[i].match);
    }

扩展匹配注册
扩展匹配规则的结构体为xt_match,其定义如下

struct xt_match {
    //链表节点
    struct list_head list;

    //添加规则时,首先根据name和revision到xt[af].match中查找
    const char name[XT_EXTENSION_MAXNAMELEN];

    u_int8_t revision;

    /* Return true or false: return FALSE and set *hotdrop = 1 to
           force immediate packet drop. */
    /* Arguments changed since 2.6.9, as this must now handle
       non-linear skb, using skb_header_pointer and
       skb_ip_make_writable. */
    //数据包匹配扩展规则时,调用match进行匹配
    bool (*match)(const struct sk_buff *skb,
              struct xt_action_param *);

    //添加规则时,调用checkentry做校验
    /* Called when user tries to insert an entry of this type. */
    int (*checkentry)(const struct xt_mtchk_param *);

    /* Called when entry of this type deleted. */
    void (*destroy)(const struct xt_mtdtor_param *);

    /* Set this to THIS_MODULE if you are a module, otherwise NULL */
    struct module *me;
    //只对指定的table生效
    const char *table;
    unsigned int matchsize;
#ifdef CONFIG_COMPAT
    unsigned int compatsize;
#endif
    //只在指定的hook点上生效
    unsigned int hooks;
    //只对指定协议生效
    unsigned short proto;
   //此扩展规则所属地址族
    unsigned short family;
};

扩展匹配规则通过xt_register_matches或者xt_register_match注册到全局变量xt[af].match中,可以在kernel代码中搜索这俩函数查看当前注册了哪些扩展匹配规则。

//xt_register_matchs可以一次性注册多个扩展match
int
xt_register_matches(struct xt_match *match, unsigned int n)
{
    unsigned int i;
    int err = 0;

    for (i = 0; i < n; i++) {
        err = xt_register_match(&match[i]);
        if (err)
            goto err;
    }
    return err;

err:
    if (i > 0)
        xt_unregister_matches(match, i);
    return err;
}

int xt_register_match(struct xt_match *match)
{
    u_int8_t af = match->family;

    mutex_lock(&xt[af].mutex);
    list_add(&match->list, &xt[af].match);
    mutex_unlock(&xt[af].mutex);
    return 0;
}

扩展匹配规则在kernel中注册后,用户态iptables命令就可以在下发命令时指定match了。
iptables添加规则指定match时需要另一个结构体struct xt_entry_match ,它是union类型。用户态需要设置 u.user 里的字段u.user.name和u.user.revision,指定使用哪个match,在内核态用u.user.name和u.user.revision在链表xt[af].match上进行查找,如果找到match了就设置到u.kernel.match = match。

struct xt_entry_match {
    union {
        struct {
            __u16 match_size;

            /* Used by userspace */
            char name[XT_EXTENSION_MAXNAMELEN];
            __u8 revision;
        } user;
        struct {
            __u16 match_size;

            /* Used inside the kernel */
            struct xt_match *match;
        } kernel;

        /* Total length */
        __u16 match_size;
    } u;
    //扩展匹配规则的参数
    unsigned char data[0];
};

下发规则时会调用函数find_check_entry遍历ipt_entry(代表一条rule)里的扩展规则,在链表xt[af].match上进行查找,如果查找到了则调用扩展规则的函数checkentry进行检查。接着获取扩展target,进行检查。

static int
find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
         unsigned int size)
{
    struct xt_entry_target *t;
    struct xt_target *target;
    int ret;
    unsigned int j;
    struct xt_mtchk_param mtpar;
    struct xt_entry_match *ematch;

    j = 0;
    mtpar.net   = net;
    mtpar.table     = name;
    mtpar.entryinfo = &e->ip;
    mtpar.hook_mask = e->comefrom;
    mtpar.family    = NFPROTO_IPV4;
    //遍历e指定的扩展匹配规则,每个扩展匹配规则定义为struct xt_entry_match
    xt_ematch_foreach(ematch, e) {
        //在链表xt[af].match上查找扩展匹配规则
        find_check_match(ematch, &mtpar);
        ++j;
    }

    //获取target,查找target,并做检查
    t = ipt_get_target(e);
    target = xt_request_find_target(NFPROTO_IPV4, t->u.user.name,
                    t->u.user.revision);

    t->u.kernel.target = target;

    ret = check_target(e, net, name);

    return 0;
}

find_check_match在链表xt[af].match上查找扩展匹配规则,如果查找到了则调用函数check_match进行一系列的检查。

static int
find_check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
{
    struct xt_match *match;
    int ret;
    //根据name和revison查找match
    match = xt_request_find_match(NFPROTO_IPV4, m->u.user.name,
                      m->u.user.revision);
    //查找失败,返回error
    if (IS_ERR(match)) {
        duprintf("find_check_match: `%s' not found\n", m->u.user.name);
        return PTR_ERR(match);
    }

    //查找成功,将查找结果match赋到u.kernel.match
    m->u.kernel.match = match;

    //根据match本身的要求检查下发的扩展匹配规则是否被允许
    ret = check_match(m, par);
    if (ret)
        goto err;

    return 0;
err:
    module_put(m->u.kernel.match->me);
    return ret;
}

check_match再调用xt_check_match对下发的扩展匹配规则进行如下检查:
a. 检查添加的match规则的size是不是扩展规则模块指定的matchsize
b. 检查添加的match规则的table是不是扩展规则模块指定的
c. 检查添加的match规则的hook是不是扩展规则模块指定的
d. 检查添加的match规则中proto是不是扩展规则模块指定的
e. 调用扩展匹配规则的checkentry检查参数特定的参数

static int
check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
{
    const struct ipt_ip *ip = par->entryinfo;
    int ret;

    par->match     = m->u.kernel.match;
    par->matchinfo = m->data;

    ret = xt_check_match(par, m->u.match_size - sizeof(*m),
          ip->proto, ip->invflags & IPT_INV_PROTO);
    if (ret < 0) {
        duprintf("check failed for `%s'.\n", par->match->name);
        return ret;
    }
    return 0;
}

int xt_check_match(struct xt_mtchk_param *par,
           unsigned int size, u_int8_t proto, bool inv_proto)
{
    int ret;

    //检查添加的match规则的size是不是扩展规则模块指定的matchsize
    if (XT_ALIGN(par->match->matchsize) != size &&
        par->match->matchsize != -1) {
        /*
         * ebt_among is exempt from centralized matchsize checking
         * because it uses a dynamic-size data set.
         */
        pr_err("%s_tables: %s.%u match: invalid size "
               "%u (kernel) != (user) %u\n",
               xt_prefix[par->family], par->match->name,
               par->match->revision,
               XT_ALIGN(par->match->matchsize), size);
        return -EINVAL;
    }

    //检查添加的match规则的table是不是扩展规则模块指定的
    if (par->match->table != NULL &&
        strcmp(par->match->table, par->table) != 0) {
        pr_err("%s_tables: %s match: only valid in %s table, not %s\n",
               xt_prefix[par->family], par->match->name,
               par->match->table, par->table);
        return -EINVAL;
    }

    //检查添加的match规则的hook是不是扩展规则模块指定的
    if (par->match->hooks && (par->hook_mask & ~par->match->hooks) != 0) {
        char used[64], allow[64];

        pr_err("%s_tables: %s match: used from hooks %s, but only "
               "valid from %s\n",
               xt_prefix[par->family], par->match->name,
               textify_hooks(used, sizeof(used), par->hook_mask,
                             par->family),
               textify_hooks(allow, sizeof(allow), par->match->hooks,
                             par->family));
        return -EINVAL;
    }

    //检查添加的match规则中proto是不是扩展规则模块指定的
    if (par->match->proto && (par->match->proto != proto || inv_proto)) {
        pr_err("%s_tables: %s match: only valid for protocol %u\n",
               xt_prefix[par->family], par->match->name,
               par->match->proto);
        return -EINVAL;
    }

    //最后调用扩展匹配规则的checkentry检查参数特定的参数
    if (par->match->checkentry != NULL) {
        ret = par->match->checkentry(par);
        if (ret < 0)
            return ret;
        else if (ret > 0)
            /* Flag up potential errors. */
            return -EIO;
    }
    return 0;
}

扩展目标注册
扩展目标的结构体为struct xt_target,其定义如下

/* Registration hooks for targets. */
struct xt_target {
    //链表节点
    struct list_head list;
    //target名字和版本
    const char name[XT_EXTENSION_MAXNAMELEN];
    u_int8_t revision;

    //数据包匹配规则后,执行的target
    /* Returns verdict. Argument order changed since 2.6.9, as this
       must now handle non-linear skbs, using skb_copy_bits and
       skb_ip_make_writable. */
    unsigned int (*target)(struct sk_buff *skb,
                   const struct xt_action_param *);

    /* Called when user tries to insert an entry of this type:
           hook_mask is a bitmask of hooks from which it can be
           called. */
    /* Should return 0 on success or an error code otherwise (-Exxxx). */
    //添加规则时,调用checkentry检查target指定的参数是否正确
    int (*checkentry)(const struct xt_tgchk_param *);

    /* Called when entry of this type deleted. */
    void (*destroy)(const struct xt_tgdtor_param *);

    /* Set this to THIS_MODULE if you are a module, otherwise NULL */
    struct module *me;
    //只对指定的table生效
    const char *table;
    unsigned int targetsize;
    //只对指定的hook点生效
    unsigned int hooks;
    //只对指定的协议生效
    unsigned short proto;
    //只对指定的地址族生效
    unsigned short family;
};

扩展目标通过xt_register_targets或者xt_register_target注册到全局变量xt[af].target中,可以在kernel代码中搜索这俩函数查看当前注册了哪些扩展目标。

//xt_register_targets可以一次性注册多个扩展目标。
int
xt_register_targets(struct xt_target *target, unsigned int n)
{
    unsigned int i;
    int err = 0;

    for (i = 0; i < n; i++) {
        err = xt_register_target(&target[i]);
        if (err)
            goto err;
    }
    return err;

err:
    if (i > 0)
        xt_unregister_targets(target, i);
    return err;
}

/* Registration hooks for targets. */
int xt_register_target(struct xt_target *target)
{
    u_int8_t af = target->family;

    mutex_lock(&xt[af].mutex);
    list_add(&target->list, &xt[af].target);
    mutex_unlock(&xt[af].mutex);
    return 0;
}

扩展目标在kernel中注册后,用户态iptables命令就可以在下发命令时指定扩展目标了。
iptables命令添加扩展目标时还需要另一个结构体xt_entry_target ,它是union类型。用户态需要设置 u.user 里的字段u.user.name和u.user.revision,指定使用哪个扩展目标,在内核态用u.user.name和u.user.revision在链表xt[af].match上进行查找,如果找到了就设置到u.kernel.target= target。

struct xt_entry_target {
    union {
        struct {
            __u16 target_size;

            /* Used by userspace */
            char name[XT_EXTENSION_MAXNAMELEN];
            __u8 revision;
        } user;
        struct {
            __u16 target_size;

            /* Used inside the kernel */
            struct xt_target *target;
        } kernel;

        /* Total length */
        __u16 target_size;
    } u;
    //扩展目标的参数
    unsigned char data[0];
};

下发规则后,在kernel中也会调用find_check_entry查找target,并做一系列参数校验,和match流程类似就不再多说了。

也可参考:netfilter之match和target - 简书 (jianshu.com)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值