iptables REDIRECT --to-ports range

I have this rule
iptables -t nat -A PREROUTING -p tcp -d --dport 1234 -j REDIRECT --to-ports 12345-12347

There's actually two Qs
- it suppose to redirect connections to 3 different ports 12345 12347 12347, however my serwer seems to ignore the last port 12347 - no connection is redirected there. Any ideas?
- if new connections arrives, how is determined destination port for this new connection? round-robin, least-occupied, firt-that-connects, etc. ?

Linux server 2.6.19-1.2911.fc6PAE #1 SMP Sat Feb 10 15:16:17 EST 2007 i686 athlon i386 GNU/Linux

Some document references please. No IMHO please.
LVL 43
Who is Participating?
NopiusConnect With a Mentor Commented:
ravenpl, hello.

What about pure facts. I've got sources of kernel and iptables 1.3.7 and digged into netfilter sources (not much, but enougth to understand). Iptables only parses your command line, and setup max.tcp.port and min.tcp.port for your rule and it also assigns a flag
IP_NAT_RANGE_PROTO_SPECIFIED. Everything else is done inside kernel (all related files are in net/ipv4/netfilter/ subdirectory of kernel sources)

When packet is checked and satisfies your -j REDIRECT rule, we go there:
1) File ipt_REDIRECT.c,  function redirect_target()

I see that 'max' and 'min' (that we passed to iptables) are preserved here (that's good):

/* Transfer from original range. */
newrange = ((struct ip_nat_range) { mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,  newdst, newdst,  mr->range[0].min, mr->range[0].max });

What is impotent in this function: return ip_nat_setup_info(ct, &newrange, hooknum);

2) File ip_nat_core.c, function ip_nat_setup_info()
What is impotent, call to: get_unique_tuple(&new_tuple, &curr_tuple, range, conntrack, maniptype);

3) File ip_nat_core.c, function get_unique_tuple()
I see here:
/* Only bother mapping if it's not already in range and unique */
        if ((!(range->flags & IP_NAT_RANGE_PROTO_SPECIFIED)
             || proto->in_range(tuple, maniptype, &range->min, &range->max))
            && !ip_nat_used_tuple(tuple, conntrack)) {

We almost found the reason: proto->in_range(tuple, maniptype, &range->min, &range->max)
This function checks if original's packet destination port IS already in range 'min-max' and returns (without changing destination ports if yes). If NOT, we proceed with this code:
/* Last change: get protocol to try to obtain unique tuple. */
        proto->unique_tuple(tuple, range, maniptype, conntrack);

3) File ip_nat_proto_tcp.c function tcp_unique_tuple()
You may look to this function, it's quiet small and self describing.
That's an algorithm of choosing new port:
for (i = 0; i < range_size; i++, port++) {
                *portptr = htons(min + port % range_size);
                if (!ip_nat_used_tuple(tuple, conntrack)) {
                        return 1;

Here 'port' is a local static variable (it increased by one for each new connection and keeps value), so we should have round robin behaviour for each new connection.

What I suggest, why your rule is not working, is a tcp destination port of original packet within the destination range, not as in your very first example, but something like:

iptables -t nat -A PREROUTING -p tcp -d --dport 12345 -j REDIRECT --to-ports 12345-12347
Hi Ravenpl,

Can you please check to see what the filter layout says about why it's not working with:

iptables -L -vn


iptables -L -vn -t nat

And also check the PREROUTE rule to be sure you have the real IP addresses being prepended. That is to say if PREROUTE were'nt enacted, you'ld need a rule that looks like:

-j REDIRECT --to-ports

I think it just hooks on the first available port. I don't know of any mechanism that allows round robin here, although you may be able to build that out.
ravenplAuthor Commented:
> -j REDIRECT --to-ports
this above is invalid.

I found that it's simply bug. And in fact only first port is beeing choosen everytime.
Will You Be GDPR Compliant by 5/28/2018?

GDPR? That's a regulation for the European Union. But, if you collect data from customers or employees within the EU, then you need to know about GDPR and make sure your organization is compliant by May 2018. Check out our preparation checklist to make sure you're on track today!

To accomplish this I typically use a mirror port.
Are you perhaps working through a managed switch?
ravenplAuthor Commented:
No, it's not managed switch. And I don't think mirror port is suitable for me.
My goal is to distribute connections to various local tcp/ports.
I workarounded this, but was wandering why --to-ports is not working.
ravenplAuthor Commented:
Thanx Nopius, I did such investigation as well. Unfortunatelly misunderstood it.
Filled http://bugzilla.kernel.org/show_bug.cgi?id=8325, which should answer everything.
Indeed it alwas chooses first port, unless it can't (maybe other rule conflicts with this one).
Since not really answered, B only.
ravenpl. thank you for points. I don't have Linux locally to patch and test kernel, only sources... If this feature is impotent for you, It would be helpful to insert some printk() statements in netfilter code and see what really happens... From just viewing sources I may miss something impotent...

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.