iptables之snat

看内核snat部分,有如下几个标志位,有点混淆,所以看下下发规则命令行iptables是如何指定的。

#define NF_NAT_RANGE_MAP_IPS            (1 << 0)
#define NF_NAT_RANGE_PROTO_SPECIFIED        (1 << 1)
#define NF_NAT_RANGE_PROTO_RANDOM       (1 << 2)
#define NF_NAT_RANGE_PERSISTENT         (1 << 3)
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY     (1 << 4)

这里先说下结论,后面再编译ipbales源码,gdb查看一下流程。
可以使用如下命令查看snat的参数列表

root@master:~/iptables-1.8.5# iptables -j SNAT -h
...
SNAT target options:
 --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]
                                Address to map source to.
[--random] [--random-fully] [--persistent]

--to-source 指定了ip/ip范围和port/port范围,
    只要指定了ip或者ip范围就会设置 NF_NAT_RANGE_MAP_IPS,
    只要指定了port或者port范围就会设置 NF_NAT_RANGE_PROTO_SPECIFIED。
--random 指定了这个参数就会设置NF_NAT_RANGE_PROTO_RANDOM,
--random-fully 指定了这个参数就会设置 NF_NAT_RANGE_PROTO_RANDOM_FULLY,
--persistent 指定了这个参数就会设置 NF_NAT_RANGE_PERSISTENT

参数--to-source比较简单,就是指定了ip和port,后面三个参数的作用得结合内核代码看。

--persistent 参数只在下面代码用到,通过 jhash2 计算j值时的最后一个参数 jhash2(const u32 *k, u32 length, u32 initval),如果指定了此参数,则直接写0,如果不指定,需要根据dst ip计算出一个值。

nf_nat_setup_info->get_unique_tuple->find_best_ips_proto
{
    j = jhash2((u32 *)&tuple->src.u3, sizeof(tuple->src.u3) / sizeof(u32),
           range->flags & NF_NAT_RANGE_PERSISTENT ?
            0 : (__force u32)tuple->dst.u3.all[max] ^ zone);

    full_range = false;
    for (i = 0; i <= max; i++) {
        /* If first bytes of the address are at the maximum, use the
         * distance. Otherwise use the full range.
         */
        if (!full_range) {
            minip = ntohl((__force __be32)range->min_addr.all[i]);
            maxip = ntohl((__force __be32)range->max_addr.all[i]);
            dist  = maxip - minip + 1;
        } else {
            minip = 0;
            dist  = ~0;
        }

        var_ipp->all[i] = (__force __u32)
            htonl(minip + reciprocal_scale(j, dist));
        if (var_ipp->all[i] != range->max_addr.all[i])
            full_range = true;

        if (!(range->flags & NF_NAT_RANGE_PERSISTENT))
            j ^= (__force u32)tuple->dst.u3.all[i];
    }
}

如果不指定--random和--random-fully,则优先判断报文自带的ip或者port是否在规则指定的范围内。
如果指定了这两个标志,就会直接调用find_best_ips_proto选择合适的ip,调用四层协议提供的 unique_tuple 函数,选择合适的port。下面代码是内核中选择端口时流程

nf_nat_setup_info->get_unique_tuple
{
    ...
    if (maniptype == NF_NAT_MANIP_SRC &&
        !(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
        /* try the original tuple first */
        if (in_range(l3proto, l4proto, orig_tuple, range)) {
            if (!nf_nat_used_tuple(orig_tuple, ct)) {
                *tuple = *orig_tuple;
                goto out;
            }
        } else if (find_appropriate_src(net, zone, l3proto, l4proto,
                        orig_tuple, tuple, range)) {
            pr_debug("get_unique_tuple: Found current src map\n");
            if (!nf_nat_used_tuple(tuple, ct))
                goto out;
        }
    }
    /* 2) Select the least-used IP/proto combination in the given range */
    *tuple = *orig_tuple;
    find_best_ips_proto(zone, tuple, range, ct, maniptype);

    /* Only bother mapping if it's not already in range and unique */
    if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
        //规则指定了port,则判断报文自带的port是否在指定的port range内。
        //如果在range内,并且只指定了一个port,或者没被使用,则返回,否则还得调用unique_tuple选择合适的port
        if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
            if (l4proto->in_range(tuple, maniptype,
                          &range->min_proto,
                          &range->max_proto) &&
                (range->min_proto.all == range->max_proto.all ||
                 !nf_nat_used_tuple(tuple, ct)))
                goto out;
        //规则没有指定port,则还使用报文中自带的port
        } else if (!nf_nat_used_tuple(tuple, ct)) {
            goto out;
        }
    }
    /* Last change: get protocol to try to obtain unique tuple. */
    //调用四层协议提供的 unique_tuple,对于udp来说,此函数为udp_unique_tuple
    l4proto->unique_tuple(l3proto, tuple, range, maniptype, ct);
out:
    rcu_read_unlock();
}

static void
udp_unique_tuple(const struct nf_nat_l3proto *l3proto,
         struct nf_conntrack_tuple *tuple,
         const struct nf_nat_range *range,
         enum nf_nat_manip_type maniptype,
         const struct nf_conn *ct)
{
    nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct,
                    &udp_port_rover);
}
void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
                 struct nf_conntrack_tuple *tuple,
                 const struct nf_nat_range *range,
                 enum nf_nat_manip_type maniptype,
                 const struct nf_conn *ct,
                 u16 *rover)
{
    unsigned int range_size, min, i;
    __be16 *portptr;
    u_int16_t off;

    if (maniptype == NF_NAT_MANIP_SRC)
        portptr = &tuple->src.u.all;
    else
        portptr = &tuple->dst.u.all;
    //规则没有指定port range,如果是dnat,则不修改port。如果是
    //snat,根据报文自带的源port选择合适的范围
    /* If no range specified... */
    if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) {
        /* If it's dst rewrite, can't change port */
        if (maniptype == NF_NAT_MANIP_DST)
            return;

        if (ntohs(*portptr) < 1024) {
            /* Loose convention: >> 512 is credential passing */
            if (ntohs(*portptr) < 512) {
                min = 1;
                range_size = 511 - min + 1;
            } else {
                min = 600;
                range_size = 1023 - min + 1;
            }
        } else {
            min = 1024;
            range_size = 65535 - 1024 + 1;
        }
    } else {
        min = ntohs(range->min_proto.all);
        range_size = ntohs(range->max_proto.all) - min + 1;
    }

    if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
        off = l3proto->secure_port(tuple, maniptype == NF_NAT_MANIP_SRC
                          ? tuple->dst.u.all
                          : tuple->src.u.all);
    } else if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
        off = prandom_u32();
    } else {
        off = *rover;
    }

    for (i = 0; ; ++off) {
        *portptr = htons(min + off % range_size);
        if (++i != range_size && nf_nat_used_tuple(tuple, ct))
            continue;
        if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL))
            *rover = off;
        return;
    }
}

iptables

  1. 使用源码安装iptables,可以加gdb断点,方便调式

操作系统版本

root@ubuntu:~/iptables-1.8.5# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.3 LTS
Release:        18.04
Codename:       bionic
root@ubuntu:~/iptables-1.8.5# uname -a
Linux ubuntu 4.15.0-109-generic #110-Ubuntu SMP Tue Jun 23 02:39:32 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

下载安装依赖包

wget https://launchpad.net/ubuntu/+archive/primary/+files/libnftnl-dev_1.1.7-1_amd64.deb
wget https://launchpad.net/ubuntu/+archive/primary/+files/libnftnl11_1.1.7-1_amd64.deb

dpkg -i ./libnftnl11_1.1.7-1_amd64.deb
dpkg -i ./libnftnl-dev_1.1.7-1_amd64.deb

解压编译iptables

tar -jxf iptables-1.8.5.tar.bz2
cd iptables-1.8.5/
./configure
//指定 -g -o0,方便gdb设置断点
export EXTRA_CFLAGS="-g -o0"
//编译
make
//将iptables命令安装到系统中
make install

编译成功后,会在iptables目录下生成两个脚本: xtables-nft-multi和xtables-legacy-multi。它们都会分别调用 iptables/.libs/下的xtables-nft-multi和xtables-legacy-multi。其中nft是新命令,legacy是iptables命令。

root@ubuntu:~/iptables-1.8.5# ls iptables/xtables-nft-multi
iptables/xtables-nft-multi
root@ubuntu:~/iptables-1.8.5# ls iptables/xtables-legacy-multi
iptables/xtables-legacy-multi

root@ubuntu:~/iptables-1.8.5# ls iptables/.libs/
xtables-legacy-multi  xtables-nft-multi

root@ubuntu:~/iptables-1.8.5# file  iptables/xtables-nft-multi
iptables/xtables-nft-multi: Bourne-Again shell script, ASCII text executable
root@ubuntu:~/iptables-1.8.5# file  iptables/.libs/xtables-legacy-multi
iptables/.libs/xtables-legacy-multi: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=875ac2a4a56de055f109563e8cce1e61f7f2b69d, with debug_info, not stripped
  1. snat和dnat的用法
    ip和port可以指定单个,也可以指定一个范围

static void SNAT_help(void)
{
    printf(
"SNAT target options:\n"
" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
"               Address to map source to.\n"
"[--random] [--random-fully] [--persistent]\n");
}

./extensions/libipt_SNAT.t
:POSTROUTING
*nat
-j SNAT --to-source 1.1.1.1;=;OK
-j SNAT --to-source 1.1.1.1-1.1.1.10;=;OK
-j SNAT --to-source 1.1.1.1:1025-65535;;FAIL
-j SNAT --to-source 1.1.1.1 --to-source 2.2.2.2;;FAIL
-p tcp -j SNAT --to-source 1.1.1.1:1025-65535;=;OK
-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65535;=;OK
-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65536;;FAIL
-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65535 --to-source 2.2.2.2-2.2.2.20:1025-65535;;FAIL
-j SNAT;;FAIL

static void DNAT_help(void)
{
    printf(
"DNAT target options:\n"
" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
"               Address to map destination to.\n"
"[--random] [--persistent]\n");
}

static void DNAT_help_v2(void)
{
    printf(
"DNAT target options:\n"
" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port[/port]]]\n"
"               Address to map destination to.\n"
"[--random] [--persistent]\n");
}

./extensions/libipt_DNAT.t
:PREROUTING
*nat
-j DNAT --to-destination 1.1.1.1;=;OK
-j DNAT --to-destination 1.1.1.1-1.1.1.10;=;OK
-j DNAT --to-destination 1.1.1.1:1025-65535;;FAIL
-j DNAT --to-destination 1.1.1.1 --to-destination 2.2.2.2;;FAIL
-p tcp -j DNAT --to-destination 1.1.1.1:1025-65535;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1-1.1.1.10:1025-65535;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1-1.1.1.10:1025-65536;;FAIL
-p tcp -j DNAT --to-destination 1.1.1.1-1.1.1.10:1025-65535 --to-destination 2.2.2.2-2.2.2.20:1025-65535;;FAIL
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/1000;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/3000;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/65535;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/0;;FAIL
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/65536;;FAIL
-j DNAT;;FAIL
  1. 注册target
    注册 snat target

static struct xtables_target snat_tg_reg = {
    .name       = "SNAT",
    .version    = XTABLES_VERSION,
    .family     = NFPROTO_IPV4,
    .size       = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
    .userspacesize  = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
    .help       = SNAT_help,
    .x6_parse   = SNAT_parse,
    .x6_fcheck  = SNAT_fcheck,
    .print      = SNAT_print,
    .save       = SNAT_save,
    .x6_options = SNAT_opts,
    .xlate      = SNAT_xlate,
};
xtables_register_target(&snat_tg_reg);

注册 dnat target

static struct xtables_target dnat_tg_reg[] = {
    {
        .name       = "DNAT",
        .version    = XTABLES_VERSION,
        .family     = NFPROTO_IPV4,
        .revision   = 0,
        .size       = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
        .userspacesize  = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
        .help       = DNAT_help,
        .print      = DNAT_print,
        .save       = DNAT_save,
        .x6_parse   = DNAT_parse,
        .x6_fcheck  = DNAT_fcheck,
        .x6_options = DNAT_opts,
        .xlate      = DNAT_xlate,
    },
    {
        .name       = "DNAT",
        .version    = XTABLES_VERSION,
        .family     = NFPROTO_IPV4,
        .revision   = 2,
        .size       = XT_ALIGN(sizeof(struct nf_nat_range2)),
        .userspacesize  = XT_ALIGN(sizeof(struct nf_nat_range2)),
        .help       = DNAT_help_v2,
        .print      = DNAT_print_v2,
        .save       = DNAT_save_v2,
        .x6_parse   = DNAT_parse_v2,
        .x6_fcheck  = DNAT_fcheck_v2,
        .x6_options = DNAT_opts,
        .xlate      = DNAT_xlate_v2,
    },
};
xtables_register_targets(dnat_tg_reg, ARRAY_SIZE(dnat_tg_reg));
  1. 命令行流程
    legacy命令实现

static const struct subcommand multi_subcommands[] = {
#ifdef ENABLE_IPV4
    {"iptables",            iptables_main},
    {"main4",               iptables_main},
    {"iptables-save",       iptables_save_main},
    {"save4",               iptables_save_main},
    {"iptables-restore",    iptables_restore_main},
    {"restore4",            iptables_restore_main},
    {"iptables-legacy",     iptables_main},
    {"iptables-legacy-save",iptables_save_main},
    {"iptables-legacy-restore",iptables_restore_main},
#endif
    {"iptables-xml",        iptables_xml_main},
    {"xml",                 iptables_xml_main},
#ifdef ENABLE_IPV6
    {"ip6tables",           ip6tables_main},
    {"main6",               ip6tables_main},
    {"ip6tables-save",      ip6tables_save_main},
    {"save6",               ip6tables_save_main},
    {"ip6tables-restore",   ip6tables_restore_main},
    {"restore6",            ip6tables_restore_main},
    {"ip6tables-legacy",    ip6tables_main},
    {"ip6tables-legacy-save",ip6tables_save_main},
    {"ip6tables-legacy-restore",ip6tables_restore_main},
#endif
    {NULL},
};

int main(int argc, char **argv)
{
    return subcmd_main(argc, argv, multi_subcommands);
}

新命令实现

static const struct subcommand multi_subcommands[] = {
    {"iptables-xml",        iptables_xml_main},
    {"xml",             iptables_xml_main},
    {"iptables",            xtables_ip4_main},
    {"iptables-nft",        xtables_ip4_main},
    {"main4",           xtables_ip4_main},
    {"save4",           xtables_ip4_save_main},
    {"restore4",            xtables_ip4_restore_main},
    {"iptables-save",       xtables_ip4_save_main},
    {"iptables-restore",        xtables_ip4_restore_main},
    {"iptables-nft-save",   xtables_ip4_save_main},
    {"iptables-nft-restore",    xtables_ip4_restore_main},
    {"ip6tables",           xtables_ip6_main},
    {"ip6tables-nft",       xtables_ip6_main},
    {"main6",           xtables_ip6_main},
    {"save6",           xtables_ip6_save_main},
    {"restore6",            xtables_ip6_restore_main},
    {"ip6tables-save",      xtables_ip6_save_main},
    {"ip6tables-restore",       xtables_ip6_restore_main},
    {"ip6tables-nft-save",  xtables_ip6_save_main},
    {"ip6tables-nft-restore",   xtables_ip6_restore_main},
    {"iptables-translate",      xtables_ip4_xlate_main},
    {"ip6tables-translate",     xtables_ip6_xlate_main},
    {"iptables-restore-translate",  xtables_ip4_xlate_restore_main},
    {"ip6tables-restore-translate", xtables_ip6_xlate_restore_main},
    {"arptables",           xtables_arp_main},
    {"arptables-nft",       xtables_arp_main},
    {"arptables-restore",       xtables_arp_restore_main},
    {"arptables-nft-restore",   xtables_arp_restore_main},
    {"arptables-save",      xtables_arp_save_main},
    {"arptables-nft-save",      xtables_arp_save_main},
    {"ebtables-translate",      xtables_eb_xlate_main},
    {"ebtables",            xtables_eb_main},
    {"ebtables-restore",        xtables_eb_restore_main},
    {"ebtables-save",       xtables_eb_save_main},
    {"ebtables-nft",        xtables_eb_main},
    {"ebtables-nft-restore",    xtables_eb_restore_main},
    {"ebtables-nft-save",       xtables_eb_save_main},
    {"xtables-monitor",     xtables_monitor_main},
    {NULL},
};
//main函数
int main(int argc, char **argv)
{
    return subcmd_main(argc, argv, multi_subcommands);
}

每个命令对应一个函数,比如执行 iptables 命令时就会调用 xtables_ip4_main。

下面是main函数流程和参数解析

xtables_ip4_main -->xtables_main --> do_commandx -->do_parse 
重点看下解析参数部分
do_parse 
{
    while ((cs->c = getopt_long(argc, argv,
       "-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::W::nt:m:xc:g:46",
                       opts, NULL)) != -1) {
         switch (cs->c) {
            case 'j':
                    //获取-j指定的target
                    command_jump(cs, optarg);
                        break;
            default:
               //获取match或者target的参数,参见下面的分析a
                   if (command_default(cs, &xtables_globals) == 1)
                       continue;
                   break;
         }
    }
 //调用target的final_check函数,参见下面的分析b。tfcall全称为target final call
    if (cs->target != NULL)
        xtables_option_tfcall(cs->target);
}

a.
int command_default(struct iptables_command_state *cs,
            struct xtables_globals *gl)
{
    struct xtables_rule_match *matchp;
    struct xtables_match *m;

    if (cs->target != NULL &&
        (cs->target->parse != NULL || cs->target->x6_parse != NULL) &&
        cs->c >= cs->target->option_offset &&
        cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) {
        xtables_option_tpcall(cs->c, cs->argv, cs->invert,
                      cs->target, &cs->fw);
        return 0;
    }
}
/**
 * Dispatch arguments to the appropriate parse function, based upon the
 * extension's choice of API.
 */
//tpcall 全称 target parse call
void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
               struct xtables_target *t, void *fw)
{
    struct xt_option_call cb;

    if (t->x6_parse == NULL) {
        if (t->parse != NULL)
            t->parse(c - t->option_offset, argv, invert,
                 &t->tflags, fw, &t->t);
        return;
    }

    c -= t->option_offset;
    cb.entry = xtables_option_lookup(t->x6_options, c);
    if (cb.entry == NULL)
        xtables_error(OTHER_PROBLEM,
            "Extension does not know id %u\n", c);
    cb.arg      = optarg;
    cb.invert   = invert;
    cb.ext_name = t->name;
    cb.data     = t->t->data;
    cb.xflags   = t->tflags;
    cb.target   = &t->t;
    cb.xt_entry = fw;
    cb.udata    = t->udata;
    t->x6_parse(&cb);  //SNAT_parse
    t->tflags = cb.xflags;
}

static void SNAT_parse(struct xt_option_call *cb)
{
    const struct ipt_entry *entry = cb->xt_entry;
    struct ipt_natinfo *info = (void *)(*cb->target);
    int portok;

    if (entry->ip.proto == IPPROTO_TCP
        || entry->ip.proto == IPPROTO_UDP
        || entry->ip.proto == IPPROTO_SCTP
        || entry->ip.proto == IPPROTO_DCCP
        || entry->ip.proto == IPPROTO_ICMP)
        portok = 1;
    else
        portok = 0;

    xtables_option_parse(cb);
    switch (cb->entry->id) {
    case O_TO_SRC:
        if (cb->xflags & F_X_TO_SRC) {
            if (!kernel_version)
                get_kernel_version();
            //如果内核版本大于2.6.10,则不能指定多个 --to-source
            if (kernel_version > LINUX_VERSION(2, 6, 10))
                xtables_error(PARAMETER_PROBLEM,
                       "SNAT: Multiple --to-source not supported");
        }
        //解析ip和port
        //只要指定了port(单个port或者port范围),就会设置 range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
        //只要指定了ip(单个ip或者ip范围),就会设置 range.flags |= NF_NAT_RANGE_MAP_IPS;
        *cb->target = parse_to(cb->arg, portok, info);
        cb->xflags |= F_X_TO_SRC;
        break;
    //如果命令行指定了--persistent,则设置NF_NAT_RANGE_PERSISTENT
    case O_PERSISTENT:
        info->mr.range[0].flags |= NF_NAT_RANGE_PERSISTENT;
        break;
    }
}

/* Ranges expected in network order. */
static struct xt_entry_target *
parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
{
    struct nf_nat_ipv4_range range;
    char *arg, *colon, *dash, *error;
    const struct in_addr *ip;
    //-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65535
    //假如设置了上面的规则,则 orig_arg 指向 --to-source 的参数,即"1.1.1.1-1.1.1.10:1025-65535"
    arg = strdup(orig_arg);
    if (arg == NULL)
        xtables_error(RESOURCE_PROBLEM, "strdup");
    memset(&range, 0, sizeof(range));

    //colon指向 ":1025-65535"
    colon = strchr(arg, ':');
    //只要colon不为空,说明指定了port
    if (colon) {
        int port;

        if (!portok)
            xtables_error(PARAMETER_PROBLEM,
                   "Need TCP, UDP, SCTP or DCCP with port specification");

        range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;

        port = atoi(colon+1);
        if (port <= 0 || port > 65535)
            xtables_error(PARAMETER_PROBLEM,
                   "Port `%s' not valid\n", colon+1);
        //不能使用":"分隔port,得使用"-"分隔
        error = strchr(colon+1, ':');
        if (error)
            xtables_error(PARAMETER_PROBLEM,
                   "Invalid port:port syntax - use dash\n");
        //没有"-",说明只指定了一个port
        dash = strchr(colon, '-');
        if (!dash) {
            range.min.tcp.port
                = range.max.tcp.port
                = htons(port);
        } else {
           //指定了最小和最大port
            int maxport;

            maxport = atoi(dash + 1);
            if (maxport <= 0 || maxport > 65535)
                xtables_error(PARAMETER_PROBLEM,
                       "Port `%s' not valid\n", dash+1);
            if (maxport < port)
                /* People are stupid. */
                xtables_error(PARAMETER_PROBLEM,
                       "Port range `%s' funky\n", colon+1);
            range.min.tcp.port = htons(port);
            range.max.tcp.port = htons(maxport);
        }
        /* Starts with a colon? No IP info...*/
        //如果colon和arg地址相同,说明只指定了port,没有指定ip
        if (colon == arg) {
            free(arg);
            return &(append_range(info, &range)->t);
        }
        *colon = '\0';
    }
    //解析指定了ip,可以指定一个ip,或者通过"-"指定ip范围
    range.flags |= NF_NAT_RANGE_MAP_IPS;
    dash = strchr(arg, '-');
    if (colon && dash && dash > colon)
        dash = NULL;

    if (dash)
        *dash = '\0';

    ip = xtables_numeric_to_ipaddr(arg);
    if (!ip)
        xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
               arg);
    range.min_ip = ip->s_addr;
    if (dash) {
        ip = xtables_numeric_to_ipaddr(dash+1);
        if (!ip)
            xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
                   dash+1);
        range.max_ip = ip->s_addr;
    } else
        range.max_ip = range.min_ip;

    free(arg);
    return &(append_range(info, &range)->t);
}

b.
/**
 * Dispatch arguments to the appropriate final_check function, based upon the
 * extension's choice of API.
 */
void xtables_option_tfcall(struct xtables_target *t)
{
    if (t->x6_fcheck != NULL) {
        struct xt_fcheck_call cb;

        cb.ext_name = t->name;
        cb.data     = t->t->data;
        cb.xflags   = t->tflags;
        cb.udata    = t->udata;
        t->x6_fcheck(&cb);   //SNAT_fcheck
    } else if (t->final_check != NULL) {
        t->final_check(t->tflags);
    }
    if (t->x6_options != NULL)
        xtables_options_fcheck(t->name, t->tflags, t->x6_options); //SNAT_opts
}
static void SNAT_fcheck(struct xt_fcheck_call *cb)
{
    static const unsigned int f = F_TO_SRC | F_RANDOM;
    static const unsigned int r = F_TO_SRC | F_RANDOM_FULLY;
    struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
    //如果命令行指定了 --random,则设置NF_NAT_RANGE_PROTO_RANDOM
    if ((cb->xflags & f) == f)
        mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
    //如果命令行指定了--random-fully,则设置NF_NAT_RANGE_PROTO_RANDOM_FULLY
    if ((cb->xflags & r) == r)
        mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
}

snat选项

enum {
    O_TO_SRC = 0,
    O_RANDOM,
    O_RANDOM_FULLY,
    O_PERSISTENT,
    O_X_TO_SRC,
    F_TO_SRC       = 1 << O_TO_SRC,
    F_RANDOM       = 1 << O_RANDOM,
    F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
    F_X_TO_SRC     = 1 << O_X_TO_SRC,
};
static const struct xt_option_entry SNAT_opts[] = {
    {.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
     .flags = XTOPT_MAND | XTOPT_MULTI},
    {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
    {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
    {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
    XTOPT_TABLEEND,
};

也可参考: iptables之snat - 简书 (jianshu.com) 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值