HowTo: SNAT/IP Masquerade between 2 wlan cards on Linux

Although this is a quite minimalistic howto you should be able to do IP masquerading between your WLAN cards in less than 2 minutes. As I posted here I'd like to share use my WLAN cards to share one cards Internet connection with the other one. The following steps don't apply only to wifi cards. The same commands should work on Ethernet cards too. Let's start...

The Big picture

The big picture

As you see you have 2 networks:

  • 10.0.0.x (on wlan1)
  • 192.168.178.x (wlan0)

wlan1 will act as a Access Point waiting for incomming connections and wlan0 will be connected to the Internet via FritzBox. Let's get all the pieces together.

Configure wlan0

Well there is not much to say about this section. Make sure you have a working Internet connection via wlan0.

Configure wlan1

Now comes the more interesting part. You want to allow your clients (whoever they are) to connect to your host via wlan1 and use the Internet connection on wlan0. Pretty much ... simple! Let's sum up which steps are required:

  • Allow users to connect to wlan1
  • Make sure clients get an IP address which is different wlan0′s one (just for simplicity sake)
  • Allow your host to do IP Forwarding
  • Forward packets comming from wlan1 to wlan0
  • Forward packets which refer to our 10.0.0.x network from wlan0 to wlan1

Configure hostapd

That sounds quite simple, isn't it? Suppose wlan1 is already listening for incoming connection. You can achieve that by using hostapd  and thereby acting like an Access Point on wlan1. Maybe I'll write an additional tutorial related to this topic. But by now let's say hostapd is running (among with dhcpd, which will assign IP addresses to the clients) and your clients are able to connect to your wifi interface and are successfully in getting an IP address.

# hostapd /etc/hostapd/hostapd.conf 
Configuration file: /etc/hostapd/hostapd.conf
Using interface wlan1 with hwaddr 00:11:22:33:44:55 and ssid 'HackLAN'

wlan1 is now acting as an AP. That's fine for now. Now we must take care every clients will get a valid IP address. Let's have a look at /etc/dhcpd/dhcpd.conf:

subnet netmask {
  option routers;

  # These options HAVE to be changed
  option domain-name-servers;
  option domain-name "";

By this configuration we will assign IP addresses between - with netmask  You might wonder about the last 2 lines. Well these ones have to be set individually. Have a look at /etc/resolv.conf and you'll know which domain-name-servers options you'll be using

# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)

Before you move on to next step we'll have to assign wlan1 an IP within our network range, otherwise the clients won't be able to communicate with our interface.

# ifconfig wlan1 inet netmask

Now start the dhcp server:

# /etc/init.d/isc-dhcp-server start
Starting ISC DHCP server: dhcpd.
# tail -n 100 /var/log/syslog
Nov 25 20:03:16 nat dhcpd: Internet Systems Consortium DHCP Server 4.1.1-P1
Nov 25 20:03:16 nat dhcpd: Copyright 2004-2010 Internet Systems Consortium.
Nov 25 20:03:16 nat dhcpd: All rights reserved.
Nov 25 20:03:16 nat dhcpd: For info, please visit
Nov 25 20:03:16 nat dhcpd: Wrote 11 leases to leases file.

Do some tests

Let's say host BlackTiny is listening for incomming connections and host *Daemon would like to connect to BlackTiny*. Start with the client:

# ifconfig [wifi-interface] up
# iwconfig [wifi-interface] essid "HackLAN" 
# dhclient [wifi-interface]
bound to -- renewal in 251 seconds.

As you see our client Daemon got an IP: Let's have a look at the server/host side:

# tail -f /var/log/syslog
Nov 25 20:09:43 nat dhcpd: DHCPACK on to - (Daemon) via wlan1
Nov 25 20:09:43 nat dhcpd: DHCPREQUEST for ( from - (Daemon) via wlan1
Nov 25 20:09:43 nat dhcpd: DHCPACK on to - (Daemon) via wlan1
Nov 25 20:12:38 nat dhcpd: DHCPREQUEST for from - (Daemon) via wlan1
Nov 25 20:12:38 nat dhcpd: DHCPACK on to - (Daemon) via wlan1

# ping
PING ( 56(84) bytes of data.
64 bytes from icmp_req=1 ttl=64 time=3.59 ms
64 bytes from icmp_req=2 ttl=64 time=2.55 ms
--- ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 2.557/3.076/3.596/0.522 ms

So everything seems to work. How about Internet connectivity?! **Client *Daemon***:

# ping
ping: unknown host

Oh ... I have forgotten about that. We'll have to allow Daemon to access the Internet through BlackTiny. Move on to the next step.

Do the cool stuff

First  of all we'll enable port forwarding on BlackTiny:

# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

Now we'll do some magic iptables stuff:

# # flush all previous entries
# iptables -F
# iptables -t nat -F
# iptables -L
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
# iptables -t nat -L
target     prot opt source               destination         

target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Now we will enable MASQUERADING  on wlan0:

# iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE

That means every time  a packet from Daemon is sent to other network like its own one, this certain packet will first get in its header the IP address of wlan0 and be forwarded to the next station till it reaches its destination (see Big Picture). Any outgoing/forwarded packet will be masqueraded. So all packets will appear to come from the masquerading host (BlackTiny).  There is nothing more to say about it. That's the all stuff behind it.

Alternatively you could use the SNAT target within iptables:

# iptables -t nat -A POSTROUTING -p tcp -o wlan0 -j SNAT --to-source [ip range]

Here is an explanation( found on

"The -to-source option is used to specify which source the packet should use. This option, at its simplest, takes one IP address which we want to use for the source IP address in the IP header. [...] The -to-source IP numbers could then, for instance, be something like: [...] We can also specify a range of ports to be used by SNAT. All the source ports would then be confined to the ports specified. The port bit of the rule would then look like in the example above, :1024-32000. This is only valid if -p tcp or -p udp was specified somewhere in the match of the rule in question. [...]"

Give it a try

Client Daemon:

# ping
PING ( 56(84) bytes of data.
64 bytes from ( icmp_req=1 ttl=50 time=39.6 ms
64 bytes from ( icmp_req=2 ttl=50 time=33.8 ms
64 bytes from ( icmp_req=3 ttl=50 time=60.8 ms
--- ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 33.835/44.782/60.853/11.609 ms

# tracepath
 1:  Daemon                                      0.225ms pmtu 1500
 1:  localhost                                             3.081ms 
 1:  localhost                                             4.630ms 
 2:                                            11.479ms 
 3:                                          9.902ms pmtu 576
 3:                                           26.146ms 
 4:                                          48.251ms 
 5:                                         33.242ms 
 6:                                        26.544ms 
 7:                      73.856ms asymm  9 
 8:                  34.915ms asymm 11 
 9:                        33.810ms asymm 12 
10:                        37.740ms asymm 13 
11:                             39.732ms asymm 13 
12:                                37.717ms asymm 14 
13:                                         39.540ms reached
     Resume: pmtu 576 hops 13 back 50

Have fun and keep it clean!

Prev: Find out your geolocation via MAC address
Next: Simple memory managment using linked lists (implement my_malloc and my_free)

comments powered by Disqus
2010-11-25 00:00