AFWall+ (Android Firewall+) is a firewall application for Android OS and the only one which works with iptables – so far the best solution since it doesn’t create a VPN tunnel to filter/block ads because such a tunnel causes more battery drain. The benefits of using iptables are almost endless and AFWall+ is really powerful in the right hands.
The normal way how to change the DNS on a WiFi connection, this might not work on newer Android versions and behind a mobile connection.
What’s the problem with Android and the (mobile) DNS?
Google restricted the function to change the mobile DNS-Server, it works on WiFi via the shown screenshot but if you’re on mobile the DHCP-DNS settings are been used or the one directly provided by the provider.
Android and a custom script can fix this behavior.
Android and mobile DNS-Server
Android has several interfaces for each kind of connection, mobile (rmnet0), wlan (wlan0) or VPN (tun0).
Until Android 4
Until Android 4 the setprop command could be used to set the DNS-server.
setprop net.rmnet0.dns1 <DNS-Address 1>
setprop net.rmnet0.dns2 <DNS-Address 2>
setprop net.dns1 <DNS-Address 1>
setprop net.dns1 <DNS-Address 2>
The problem is once the connection got interrupt/lost you need to execute these commands again. AFWall+ or Tasker could help here to detect the network status and re-set the command and this worked more or less well.
Android 4 bis including 6
Android 4.3 – Android 6 allowed you to set the DNS on rmnet0 via the ndc command.
ndc resolver setnetdns rmnet0 "" <DNS-Address 1> <DNS-Address 2>
ndc resolver clearnetdns rmnet0
ndc resolver flushnet rmnet0
The problem here is that once the connectivity got changed from e.g. WiFi to mobile the settings got overridden, similar like under Android 4 you had to re-apply these commands each time again.
Google removed the ndc / setprop function for rmnet0 and it wasn’t anymore possible to set it like the traditional old-fashion way.
Custom-scripts helped here, to bypass this issue:
# Force a specific dns server for all interfaces and delete the old DNS
$IPTABLES -t nat -D OUTPUT -p tcp --dport 53 -j DNAT --to-destination 220.127.116.11:53 || true
$IPTABLES -t nat -D OUTPUT -p udp --dport 53 -j DNAT --to-destination 18.104.22.168:53 || true
Set the new DNS
$IPTABLES -t nat -I OUTPUT -p tcp --dport 53 -j DNAT --to-destination 22.214.171.124:53
$IPTABLES -t nat -I OUTPUT -p udp --dport 53 -j DNAT --to-destination 126.96.36.199.169:53
The workaround worked but it has a fundamental flaw, it set the DNS-server for all interfaces, which means if you work with e.g. a PI-Hole it could’t filter out the ad-trackers and ended up with problems between the Pi-Hole DNS server and your device because the custom script wants to change the DNS address again after each connectivity change.
The real solution
# Set a specific DNS-Server for all networks except e.g your home WiFi (192.168.150.0/20)
$IPTABLES -t nat -D OUTPUT ! -s 192.168.150.0/20 -p tcp --dport 53 -j DNAT --to-destination 188.8.131.52:53 || true
$IPTABLES -t nat -D OUTPUT ! -s 192.168.150.0/20 -p udp --dport 53 -j DNAT --to-destination 184.108.40.206:53 || true
Set new DNS
$IPTABLES -t nat -I OUTPUT ! -s 192.168.150.0/20 -p tcp --dport 53 -j DNAT --to-destination 220.127.116.11:53
$IPTABLES -t nat -I OUTPUT ! -s 192.168.150.0/20 -p udp --dport 53 -j DNAT --to-destination 18.104.22.168:53
What does it do?
Every request gets forwarded to
22.214.171.124:53 or any other address you like. The benefit of this method is that it also works if you trying to login into other networks. This own created DNS-Server for your home network also bypasses the the problem that the DHCP server tries to get the DNS-Address which then would override the script again.
adb shell tcpdump -ns0 -i any 'port 53'
TO verify if all works correct we want to debug the connection, we see what packages are going in and out while we are behind WiFi or the mobile interface.
The mobile output looks similar like this:
12:12:30.537734 IP 08.100.223.230.50355 > 126.96.36.199:53: 38091+ A? mail.google.com. (32)
The Wifi output should look like this:
12:12:30.915985 IP 192.168.150.15.58283 > 192.168.150.1.53: 42633+ A? mail.kuketz.de. (32)
Why is this better than using solutions like NetGuard, AdGuard & Co?
Every app which doesn’t change it on a ‘root’ level creates a VPN tunnel which might causes some troubles, it drains the battery and Android only allows one tunnel at the same time, which means you can’t connect to a VPN while filtering ads due Androids implementation.
For root users this is by far the best solution I found and it should work for all users however, I think that Google needs to change this and it will be changed with newer Android versions again because of the new upcoming DNS features but for now you got a solution.
- Android netd (android.googlesource.com)