DNS, The Masquerade

Scenario: a RaspberryPI primarily acting as access point, assigning IPs by DHCP and catching all DNS requests to redirect all HTTP traffic to himself, which may eventually switch to connect as a client to an existing wireless network (and delegating DHCP and DNS to another access point).

90% of this stuff may be easily achieved with nmcli, the powerful and convenient command line interface for Network Manager.

nmcli c add type wifi ifname wifi-device con-name myconnection \
    autoconnect yes ssid myconnection
nmcli connection modify myconnection \
    802-11-wireless.mode ap 802-11-wireless.band bg ipv4.method shared
nmcli connection up myconnection

But I got stuck in a details. Using shared method, nmcli automatically provides a dnsmasq instance properly configured as DHCP server on the same IP class and as DNS forwarding server, assuming that an upstream network connection is available to reach actual DNS. I've found no way to alter the behaviour of this ad-hoc dnsmasq instance, and instrument it as a catch-all.

So I had to hack a little...

First of all, I've created a /etc/NetworkManager/dispatcher.d/90-update-dnsmasq.conf script - to be executed at each change occurring at the networking status operated by NM - containing

#!/bin/sh

case "$2" in
 "up")
  ip=`ifconfig wlan0 | sed -En "s/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p"`
  echo "address=/#/$ip" > /etc/dnsmasq.d/my-ip
  service dnsmasq restart
esac

So, I configure dnsmasq with a classic catch-all configuration.

But the dnsmasq instance executed by NM ignores the configurations in /etc/dnsmasq.d/, and cannot be replaced by killing it and starting again forcing the configurations due NM itself provides to automatically respawn it, causing a collision in the opening of TCP port 53.

Port TCP 53 is the solution to the dilemma.

I've modified the dnsmasq.conf file to make it listen at port 5353. This is ignored by the NM instance, but permits me to run another instance - through systemd, and restarted by the above mentioned NM script - to manage the catch-all behaviour. At this point, it is obvious to setup a pair of iptables rules to handle port redirection:

iptables -t nat -I PREROUTING -p tcp --dport 53 -j REDIRECT --to-port 5353
iptables -t nat -I PREROUTING -p udp --dport 53 -j REDIRECT --to-port 5353

In the end, I have:

  • a Bash script that setups the network via nmcli, accordly to a file of mines
  • eventually, the instance of dnsmasq enabled by NM acting as DHCP server
  • another instance of dnsmasq, dynamically configured, acting as DNS server

Overkilling, but it works.