I have a service running on 127.0.0.1 with port 2222. I need to forward all requests to 192.168.2.2:2222 (outside IP) only from subnet 192.168.1.0/24 to 127.0.0.1:2222.

I'm trying to use this, but it's not working.

$ iptables -t nat -I PREROUTING -p tcp -d 192.168.1.0/24 --dport 2222 -j DNAT --to-destination 127.0.0.1:2222

How can I get this to work?

UPD: Edit address scheme.

  • We need some clarification. Where is the traffic coming from? Where is the traffic originally going to? Where should the traffic be going to? As I read it, you want traffic from 192.168.1.0/24 to 127.0.0.1:2222 to be redirected to 12.23.34.45:2222. But Warren's answer assumes you want traffic from 192.168.1.0/24 to 12.23.34.45:2222 to be redirected to 127.0.0.1:222 – Patrick Jan 29 '14 at 13:37
  • 1
    Traffic comes from 192.168.1.0/24 subnet to 192.168.2.2:2222 and should be translated to service on 127.0.0.1:2222. I'm corrected address scheme. – SimWhite Jan 29 '14 at 14:27
  • 1
    You want a rule that allows traffic to port 2222 on the loopback interface from subnet 192.168.1.0/24? That isn't just a single rule type of setup. See here: debuntu.org/… – slm Jan 29 '14 at 14:35
  • Yes. As I understand I need to add masq rule? IP forwarding is already enabled of course. – SimWhite Jan 29 '14 at 14:47
  • Why not run it on a "real" IP, and filter out traffic comming from unwanted sources? That is what firewalls are for, essentially... – vonbrand Feb 3 '14 at 1:04
up vote 41 down vote accepted

The iptables rule you are using will work, but there is one additional change you need to make:

sysctl -w net.ipv4.conf.eth0.route_localnet=1

(replacing eth0 with the nic 192.168.2.2 resides on)

By default this value is 0, which instructs the kernel to not route external traffic destined to 127.0.0.0/8. This is just for security as such traffic is not normal.


Additional note: your current iptables rule is too broad. Your rule specifies -d 192.168.1.0/24 --dport 2222 as the destination match, meaning that if your machine tries to talk to another host on port 2222 (i.e. outbound traffic), it'll get redirected as well. You either need to change the -d match to -d 192.168.2.2, or add -i eth0 (or whatever your nic is).

  • 5
    This info is surprisingly hard to find. – Wren T. May 16 '14 at 4:06
  • Yes, very valuable hard-to-find information! Thank you! But I don't have route_localnet. Have there been other names for it? (in linux 2.6.30) ls /proc/sys/net/ipv4/conf/lan/: accept_redirects arp_accept arp_filter arp_notify disable_policy force_igmp_version log_martians medium_id proxy_arp secure_redirects shared_media accept_source_route arp_announce arp_ignore bootp_relay disable_xfrm forwarding mc_forwarding promote_secondaries rp_filter send_redirects tag – imz -- Ivan Zakharyaschev Mar 31 '15 at 22:39
  • I see, the patch for route_localnet is more recent (June 7, 2012) than my kernel ( 2.6.30-std-def-alt15 #1 SMP Mon Dec 14 08:45:48 UTC 2009). Ok, I'll simply achieve the wanted port-forwarding (from outside to inside) with a forwarding process like netcat (nc), xinetd, or the port-forwarding options of ssh (the latter is ineffective and stupid, of course, but I mention it, because, well, this possibility is there). – imz -- Ivan Zakharyaschev Mar 31 '15 at 22:50
  • My 2 cents: you may enable this parameter for all interfaces with sysctl -w net.ipv4.conf.all.route_localnet=1 – Dmitriusan Nov 10 '17 at 12:39

You can redirect to localhost but not to loopback (127.0.0.0/8). Loopback is a loophole. You have to redirect to one of your real interfaces. Try using REDIRECT.

iptables -t nat -A PREROUTING ..... -j REDIRECT --to-port 222

  • This is probably the best answer without the sysctl voodo... – Lester Cheung Jun 3 '16 at 2:30
  • Still doesn't help if port 222 is only being listened on localhost (the rule won't change destination address) – sanmai Dec 20 '16 at 4:35
  • In that case, use -j DNAT --to-destination w.x.y.z:222 – dresende Dec 21 '16 at 0:58

What if the correct answer with route_localnet doesn't work?..

If your kernel doesn't include the patch for route_localnet, then... upgrade the kernel!

Or there are other ways to forward the traffic coming to one interface to another port on another interface (in particular, to localhost) by running a process that would listen on the external interface and forward the traffic.

netcat (nc), xinetd, and ssh (and perhaps more) are all examples of programs able to do this (although choosing ssh would be strange and ineffective).

I've written a configuration for xinetd for this. Now this service is automatically brought up:

# cat /etc/xinetd.d/z-from-outside 
# default: off
# description: Forward connections to the z port.
service z-from-outside
{
    disable         = no
    socket_type     = stream
    type        = UNLISTED
    wait            = no
    user            = nobody
    bind        = vaio.ob
    port        = 7070
    redirect    = localhost 7070
}
# 

(vaio.ob is the name of this host on the external network interface.)

After a service xinetd reload, let's check that it is listening:

# lsof -i -P | fgrep 7070
xinetd    556      root    6u  IPv4 1797906      0t0  TCP vaio.ob:7070 (LISTEN)
sshd    27438 tun_zzoom    4u  IPv4 1059100      0t0  TCP localhost.localdomain:7070 (LISTEN)
# 

And indeed, connections do go through!

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.