[TriLUG] why is it slow?

Aaron S. Joyner aaron at joyner.ws
Fri Sep 15 02:22:40 EDT 2006


Rick DeNatale wrote:

> On 9/13/06, Aaron S. Joyner <aaron at joyner.ws> wrote:
>
>> So this post isn't entirely useless - my gut instinct is that the
>> problem is related to the "u-turn" problem as described, but I'm at a
>> loss to explain precisely the internals of why.  Assuming the NAT
>> implementation is anything close to *sane* on the embedded router, this
>> really shouldn't be a problem.  Then again, don't trust the Chinese or
>> Korean guy who wrote the firmware to have done a sensible job on his
>> first programming project.
>
>
> I've had appliance routers (SMS I think) which refused to recognize
> "U-turn" addressing.
>
> Unlike Brian, there are routers that do this, the Netgear I'm using
> now has no problem with it.
>
> Just out of curiosity how would you set up NAT routing in Linux to do
> this, with port forwarding?  For example say:
>
> wan-interface gets it's ip address from isp via dhcp
> lan-interface 192.168.1.1
> lan devices use 192.168.1.1 as gateway.
>
> for requests coming from either outside or inside:
>     http connections to wan ip address get forwarded to 192.168.1.2
>     wan ssh connections to wan ip address get forwarded to 192.168.1.3
>
Okay, so I started writing this like 30 mins ago, and discovered along 
the way that this doesn't work for this case easily at all.  In fact, I 
tried to cook up a way with just iptables, and failed miserably.  Allow 
me to summarize what I was originally thinking, and then explain why 
this case is messy, and provide a completely wacky solution or two.

In my mind, when you have a simple home network, and a Linux box, you 
run the services on the gateway.  I don't typically have additional 
servers behind my gateway which I push ports through to, via some 
mechanism ala DNAT.  If you do run services on the inside, and DNAT into 
them, you don't loop through the router for numerous reasons, including 
a) inefficient use of network links, b) weird problems like we're about 
to discuss, c) it just feels wrong.  :)  The typical way to handle this 
problem is at the name space level.  If you're addressing service Foo as 
foo.joyner.ws, then the external DNS view will return the external IP 
address, and the internal DNS view will return the internal IP address.  
In the days before views, you either ran two DNS servers with different 
zone files, or you used two different domain names (foo.int.joyner.ws, 
for example - where int is short for internal).  This way, you use the 
names, life is happy, and traffic flows logically.  If you need to use 
IPs to address the service, you better understand what IP to use from 
where.  :)

Anyway, on with why this is messy in Linux.  If you originate traffic 
internally from 192.168.1.5, and address your traffic to the external IP 
address, and have the appropriate DNAT rule, your packet gets passed 
back inside to 192.168.1.2 just fine.  The problem is that you can only 
change the destination address, or the source address.  You can do 
either one with the DNAT or SNAT targets in iptables, respectively.  
Unfortunately, both of these targets terminate rule processing and 
immediately deliver your packet on it's merry way, out the interface.  
Choosing the DNAT rule is the logical choice, as that will at least get 
your packet to where it needs to go.  The problem is, the source address 
is still 192.168.1.5, so when 192.168.1.2 writes the response packet, it 
delivers it directly to 192.168.1.5.  It does not send it back through 
the gateway (as you would sort of hope it would).  Consequentially, when 
that packet arrives, 192.168.1.5 doesn't recognize it as part of the 
stream it originated to the external IP address (naturally), so it drops 
it on the floor, and you get no where.  I googled, I looked through 
Usenet archives, I searched for quite some time for a way to change both 
the source and destination address of a packet with iptables.  Some 
people seem to be suggesting that you can just use an additional SNAT to 
fix the problem (and believe me, it seemed logical before reading the 
iptables man page, and I did try - oh did I try), but my testing proves 
out that this simply does not work.  Once the packet matches the DNAT 
rule, you get no more opportunity to match any appropriate SNAT rules, 
and vice versa.  If someone else out there knows of a way to make this 
work via iptables alone, I'd be mighty interested in it, because I can't 
come up with a way, and it sure feels like there should be one.

On to the truly wacky ways to actually make this work.  So, a few things 
came to mind immediately, all of which are horribly ugly, and if you 
implement them... well... I'm not to blame.  First off, make sure your 
DNAT rule is sufficiently specific that it doesn't capture traffic from 
the internal network (a simple -s ! 192.168.1.0/24 will suffice).  Then, 
start tacking on rules like -j REDIRECT -port 8080, to capture that port 
80 traffic and redirect it to a local port 8080.  Then, run a 
port-redirector on port 8080 which will capture the traffic, and 
originate new traffic on to the destination box.  This will get the 
right source IP address into the packet, and cause 192.168.1.2 to 
respond to the gateway, which can then pass the packets back through the 
port redirector, back to the REDIRECT session / rule, and then 
eventually back to 192.168.1.5.  Don't blame me, I said it was ugly.  I 
also promised to provide more than one ugly solution.

The other way to skin this cat, is to do weird things on the backend 
servers.  You could do something akin to port based policy routing, such 
that you add an iproute2 rule to match traffic originating from port 80 
on the web server, traveling to any host on the local network, and shunt 
it to a different route table, which only has an explicit nexthop set 
for all hosts on the network to the default gateway.  The amusing thing 
here is that you can't match packets with just iproute2 based on port, 
you only get src and dst ip address.  You can match on fwmark, from 
iptables, though.  So you mark the packets with iptables via the -j MARK 
target, then apply the route to them with the `ip rule` command.  This 
solution may or may not actually work, depending on how the gateway 
handles that incoming traffic destined for another host on the same 
network.  It's late, and I don't want to setup the above absurd solution 
to see it if will work or not.  :)  If anyone's feeling bored, let me 
know -- I think it'll work, but I'm not quite sure how you'd have to 
specify the route(s) in the alt routing table, ie. can you do a simple 
/24 with the nexthop set to the gateway, or do you need individual /32s 
for everything but the machine itself and the gateway, or perhaps some 
odd mix to make it easier to specify, with the same effect?

Let it be said that I'm a died in the wool Linux fan, and I don't think 
many people on the list will question that.  But, I was thinking while 
writing that last paragraph, that this is dramatically easier to do on 
an honest-to-god router.  Not just part of it, but all of it.  Granted, 
you have to think like a network guy, and the terminology is almost all 
different, conceptually half of it is different, and the configuration 
of things is 100% different, but once you beat the learning curve, man 
is it easier.  On the flip side, all the things that seem easy and you 
take for granted in the Linux world, become their own challenge in that 
world.  *chuckle*  I'll stick with the things I know inside and out, and 
jump through hurdles like above as required every once in a while.  In 
this world, at least I know how to overcome the bizarre edge cases 
better.  :)  Yes Ryan, this paragraph was for you.  I hope you made it 
this far in.

It's late, I'm tired, enough typing.
Aaron S. Joyner




More information about the TriLUG mailing list