Iptables rules to apply NAT to connections from a LAN client to router

I have the default OpenWrt firewall set up. When a LAN client connects to the WAN IP of the router, the LAN client IP and port aren't translated. What iptables rules do I need to make the traffic go through NAT table? I only want to do this to some LAN clients ideally based on MAC address. The goal is for the router to see the translated port number, I think it needs iptables, but it's fine if it can be done using a different approach.

The ideal approach is to:

  • use the LAN IP; or
  • to setup a hostname on the router that uses the LAN IP

If not, you must redirect to:

config redirect                                                       
	option target 'DNAT'                    
	option src 'wan'                                 
	option proto 'tcp'            
	option src_dport '80'                       
	option dest_port '80'                           
	option name 'HTTP'                     
	option dest 'lan'                   
	option dest_ip '192.168.1.xxx'

The LAN client can create a connection to both the router's LAN IP and WAN IP.

e.g.

  • LAN client IP 10.0.0.2
  • Router LAN IP 10.0.0.1
  • Router WAN IP 1.1.1.1

Currently when 10.0.0.2:2222 connects to 10.0.0.1:1111, the router sees a connection from 10.0.0.2:2222, when 10.0.0.2:2222 connects to 1.1.1.1:1111, the router still sees a connection from 10.0.0.2:2222.

What I want is for the connection to go through NAT, so that the connection becomes 10.0.0.2:2222 (NATed to say 1.1.1.1:5555) -> router (either 1.1.1.1:1111 or 10.0.0.1:1111), so that a router process listening on port 1111 sees an incoming connection with translated source IP:port of 1.1.1.1:5555

Sounds like you’re trying “hairpin NAT” or the like, perhaps so that your LAN clients can connect through the NAT as if they were outside. If so, the return packets also need to be considered and can have significant problems.

Would you describe your goals and topology of the hosts and nets involved?

It is similar to hairpinning with one host being the router itself. The goal is to extract the translated port number so that the process can help the client create connections (NAT traversal), it's a little like running a STUN service serving LAN clients only, so connections must go through NAT for it to work.

The topology is a simple home network, router has one WAN IP, and LAN hosts are behind NAT. This question is about connections between one LAN host and the router, not about connections between LAN hosts or external hosts.

I'm still not sure of your topology or goals, but it strikes me as a "challenging" bit of networking that you're trying to accomplish. Assuming that you SNAT the incoming packet, you'd have:

10.0.0.2:2222    =>    1.1.1.1:1111   Client => Router, setup, in if LAN
1.1.1.1:5555     =>    1.1.1.1:1111   Client => Router, setup, post-NAT, in if LAN
---
1.1.1.1:1111     =>    1.1.1.1:5555   Router => Client, ack; routing table selects loopback as 1.1.1.1 is local

I'm not sure how this benefits you nor how that packet would get back without some serious magic in pre-routing. It feels like it might be functional if you ran all packets destined for the WAN address through the same NAT.

It also doesn't seem to help in determining what the outbound connection would be for either a subsequent client connection to another host, if what you're trying to get at is determining what the source port for a given client-to-host connection would be in the future.

Edit: Still scratching my head on this challenge. Would you back up one step and describe what data you're trying to get at through this scheme? There may be another approach.

determining what the outbound connection would be

That is what I want to achieve. Netfilter NAT implementation is port preserving, it would try to map to the same port if possible. The outbound connection is UDP, so subsequent connections would normally be mapped to the same port used previously.

To keep the question simple. This is very similar to running a STUN service on the router. A normal STUN service processes requests (UDP packets) from WAN and returns the source IP:port in the response, so that clients can see what the mapped external IP:port is. STUN wouldn't work correctly if the request comes from LAN without going through NAT.

Is it theoretically possible to set up iptables to make some LAN traffic go through NAT?

How is NAT hairpinning implemented? Does it do DNAT only? No SNAT/masquerade?

Yes, it should be possible, though getting those return packets to "work" will be dependent on how conntrack manages the connection. You may have to jump through additional hoops if conntrack keeps connection direction and interface in its data structures.

If this is a STUN-like application, what about having the client connect directly to the remote host, then look at conntrack state to determine the port information to communicate to the remote host? It's still not clear to me what the connection to the WAN interface from your LAN host gains you over establishing the connection directly.

Hairpin NAT is something that I avoid entirely as, for most applications, it can be resolved with network-specific DNS. I do perform dual NAT in some applications, with independent NAT instances on each interface using FreeBSD. I don't know if Linux supports multiple NAT instances.

Can you think of some iptables rules I can try? Does the default conntrack setting work with the return packets?

The process needs to manage the connection from the client temporarily, so a direct connection isn't an option, and using conntrack tool would require a lot more work than inspecting the packet.

I use nftables on Linux systems, so perhaps one of the other members with experience with iptables can help out.

Conceptually, you seem to hook things before packets are delivered on "in" and before a routing decision is made on "out". Looking at https://wiki.nftables.org/wiki-nftables/index.php/Netfilter_hooks that would be prerouting or input for "in" and prerouting for "out".

It turns out SNAT in the nat table INPUT chain is all I need.

Below is an example of firewall.user if anyone is interested. I've tested a modified version with SSH traffic (TCP port 22). The script extracts WAN IP and if successful it then applies SNAT to packets coming from br-lan with a specific source MAC address and destination UDP ports.
One could add -d ${src_ip##*src} to allow the flexibility to enable SNAT only if the client connects to router's WAN IP.

#!/bin/sh
src_ip=$(ip route get 192.88.99.1) && \
iptables -t nat -A INPUT -i br-lan -m mac --mac-source 11:22:33:44:55:66 -p udp --match multiport --dports 1111:1121 -j SNAT --to ${src_ip##*src}

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.