iptables的端口范围映射

来源:互联网 发布:java urlencode解码 编辑:程序博客网 时间:2024/06/10 05:32

iptables的端口范围映射

  使用iptables来进行端口映射我们似乎比较熟悉,指定一个范围的端口映射好像也略懂一二,但实际测试结果却不尽人意。

单个端口映射

  一般而言要在路由器里实现一条端口映射规则,需要两个iptables规则,一条是目的地址转换一条是源地址转换。 如下(192.168.40.200是wan口地址,10.0.0.150是LAN侧主机地址):
  iptables -t nat -I PREROUTING -p tcp -d 192.168.40.200 --dport 23 -j DNAT --to 10.0.0.150:23
  iptables -t nat -I POSTROUTING -p tcp -s 10.0.0.150 --sport 23 -j SNAT --to 192.168.40.200:23

范围端口映射

  查看man iptables可知,范围端口映射规则也挺简单,匹配源时写为--sport port[:port],target时写为[ipaddr][-ipaddr][:port[-port]]。即上面单个的端口映射规则改为这种形式:
  iptables -t nat -I PREROUTING -p tcp -d 192.168.40.200 --dport 23:100 -j DNAT --to 10.0.0.150:23-100

  重点不在于规则怎么写,而是范围端口映射时是怎么映射?因为它可以少对多也可以多对少。例如配置[10-12]映射到[100-102],我怎么知道端口11会映射到具体那个端口,101吗? [100-200]映射到[10-20]时又会怎么样呢?

  如果测试一下第一个问题会发现总是映射到100端口,不是想象的101端口。

代码跟踪

  内核代码一向难度较高,这里也只是浅浅的忽悠一下。
跟踪一下DNAT target的动作,DNAT动作的代码在net/ipv4/netfilter/nf_nat_rule.c里的ipt_dnat_target()函数,然后进入nf_nat_setup_info() ==》 get_unique_tuple()

get_unique_tuple函数

1.static void2.get_unique_tuple(struct nf_conntrack_tuple *tuple,3.         const struct nf_conntrack_tuple *orig_tuple,4.         const struct nf_nat_range *range,5.         struct nf_conn *ct,6.         enum nf_nat_manip_type maniptype)7.{8.    struct net *net = nf_ct_net(ct);9.    const struct nf_nat_protocol *proto;10.    u16 zone = nf_ct_zone(ct);11.12.    /* 1) If this srcip/proto/src-proto-part is currently mapped,13.       and that same mapping gives a unique tuple within the given14.       range, use that.15.16.       This is only required for source (ie. NAT/masq) mappings.17.       So far, we don't do local source mappings, so multiple18.       manips not an issue.  */19.    if (maniptype == IP_NAT_MANIP_SRC &&20.        !(range->flags & IP_NAT_RANGE_PROTO_RANDOM)) {21.        if (find_appropriate_src(net, zone, orig_tuple, tuple, range)) {22.            pr_debug("get_unique_tuple: Found current src map\n");23.            if (!nf_nat_used_tuple(tuple, ct))24.                return;25.        }26.    }27.    printk("%s(%d)\n", __FUNCTION__, __LINE__);28.    /* 2) Select the least-used IP/proto combination in the given29.       range. */30.    *tuple = *orig_tuple;31.    find_best_ips_proto(zone, tuple, range, ct, maniptype);32.33.    /* 3) The per-protocol part of the manip is made to map into34.       the range to make a unique tuple. */35.36.    rcu_read_lock();37.    proto = __nf_nat_proto_find(orig_tuple->dst.protonum);38.39.    /* Only bother mapping if it's not already in range and unique */40.    if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM) &&41.        (!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED) ||42.         proto->in_range(tuple, maniptype, &range->min, &range->max)) &&43.        !nf_nat_used_tuple(tuple, ct))44.        goto out;45.46.    /* Last change: get protocol to try to obtain unique tuple. */47.    proto->unique_tuple(tuple, range, maniptype, ct);48.out:49.    printk("%s(%d)\n", __FUNCTION__, __LINE__);50.    rcu_read_unlock();51.}52.

重点在第40行的判断,里面的proto->in_range(tuple, maniptype, &range->min, &range->max)会判断原tuple的端口是否在映射的端口范围内,如果在那就基本要直接goto out了。否则会进入proto->unique_tuple ==> tcp_unique_tuple ==>nf_nat_proto_unique_tuple

nf_nat_proto_unique_tuple函数,看这一句就好了

1.for (i = 0; ; ++off) {2.        *portptr = htons(min + off % range_size);3.        if (++i != range_size && nf_nat_used_tuple(tuple, ct))4.            continue;5.        if (!(range->flags & IP_NAT_RANGE_PROTO_RANDOM))6.            *rover = off;7.        return;8.    }

portptr为传出的参数,就是映射到这个端口啦。min为映射端口范围的最小值,off为偏移,大多数时候为0,range_size为端口范围大小。所以进到这里一般情况下,就会映射范围端口里面最小的端口。

结论

  当匹配的端口在映射端口的区间内时,那么端口号不会被修改。如果匹配端口不在映射端口的区间内,则大多数情况下映射端口号为最小的端口号,即映射端口号是不可预测的。
  举例: [1000-2000] 映射到[1000-2000]时会一一映射
   [1000-2000] 映射到[3000-4000]结果不可预知,一般会映射到3000端口。

0 0
原创粉丝点击