OpenWrt Forum Archive

Topic: Interim fix for Android L DNS issue?

The content of this topic has been archived between 31 Mar 2018 and 7 May 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

So according to this: https://code.google.com/p/android/issue … l?id=79504 the Android L has a known issue where it ignores the DNS domain coming from DHCP or static configuration and always uses the Google DNS servers. On top of that, Google Chrome seems to be using hard-coded Google DNS servers via IPv6.

The following is credited to rhester72, who provided great instructions on how to make sure that both IPv4 and IPv6 DNS requests from the devices connected to the router are intercepted (in my case to make sure that script I run to block ads by redirecting certain domains to a transparent gif works for these devices).

First install the required packages:

opkg update
opkg install ip ip6tables-mod-nat kmod-ipt-nat6

Then run the following:

echo "iptables -t nat -A prerouting_rule -i br-lan -p udp --dport 53 -j DNAT --to `/usr/sbin/ip addr show dev br-lan | grep global | grep -m 1 inet | awk '{print $2}' | cut -d/ -f1`" >> /etc/firewall.user
echo "iptables -t nat -A prerouting_rule -i br-lan -p tcp --dport 53 -j DNAT --to `/usr/sbin/ip addr show dev br-lan | grep global | grep -m 1 inet | awk '{print $2}' | cut -d/ -f1`" >> /etc/firewall.user
echo "ip6tables -t nat -A PREROUTING -i br-lan -p udp --dport 53 -j DNAT --to-destination [`/usr/sbin/ip addr show dev br-lan | grep global | grep inet6 | awk '{print $2}' | cut -d/ -f1`]" >> /etc/firewall.user
echo "ip6tables -t nat -A PREROUTING -i br-lan -p tcp --dport 53 -j DNAT --to-destination [`/usr/sbin/ip addr show dev br-lan | grep global | grep inet6 | awk '{print $2}' | cut -d/ -f1`]" >> /etc/firewall.user

And reboot the router.

Now your Android L devices (well, all the devices on your network really) will be using your router's DNS instead of whatever is hardcoded.

PS. If you want to intercept the DNS requests only from specific devices, you can add  "-m mac --mac-source <MAC_of_Android_L_device>" after the "br-lan" to the iptables rules above.

Again -- huge thanks to Rodney for coming up with this solution.

(Last edited by stangri on 9 Mar 2015, 22:08)

Anyone? I'm pretty sure it's possible, but don't have the technical expertise for this.

At least in the olden days of Tomato, something like this would force all DNS requests to a specific target:

iptables -t nat -A PREROUTING -i br0 -p udp --dport 53 -j DNAT --to <internal_LAN_DNS_IP>
iptables -t nat -A PREROUTING -i br0 -p tcp --dport 53 -j DNAT --to <internal_LAN_DNS_IP>

If you wanted to do it only for a specific MAC:

iptables -t nat -A PREROUTING -i br0 -m mac --mac-source <MAC_of_Android_L_device> -p udp --dport 53 -j DNAT --to <internal_LAN_DNS_IP>
iptables -t nat -A PREROUTING -i br0 -m mac --mac-source <MAC_of_Android_L_device> -p tcp --dport 53 -j DNAT --to <internal_LAN_DNS_IP>

(You need both UDP and TCP, as DNS can use both depending on factors like the length of the request/reply.)

Please also note that the examples above concern themselves only with IPv4.

Rodney

Hey Rodney, thank you so much for your prompt reply.

Some follow-up questions:
1. The interface (br0) should be set to the interface where the requests are coming from? Or the bridge?
2. That would not prevent the router itself from reaching the google DNS servers, right?
3. I'm a bit confused about iptables chain/rules difference, so if I wanted to remove those two things from iptables -- what should I do?

1. The bridge, unless you want to do something different for wireless vs. wired.
2. No, those requests originate from your WAN interface.
3. The rules should be added (complete, just as you'd enter them from the CLI, including the iptables command itself) to /etc/firewall.user.  If you wish to disable them for some reason, either remove them from that file or comment them out.

Rodney

Tried running these:
iptables -t nat -A PREROUTING -i br0 -p udp --dport 53 -j DNAT --to 192.168.1.1
iptables -t nat -A PREROUTING -i br0 -p tcp --dport 53 -j DNAT --to 192.168.1.1
in the shell and nothing has been added to the /etc/firewall.user sad

No errors after the iptables commands either, did I do something wrong?

When you execute them in the shell, they are executed 'live' but will not persist during a firewall restart or reboot.

Assuming they work as intended (does DNS, in fact, work as you wish now? smile, you'll need to use vi or some other editor to add the lines to /etc/firewall.user.  Or, you can:

echo "iptables -t nat -A PREROUTING -i br0 -p udp --dport 53 -j DNAT --to 192.168.1.1" >> /etc/firewall.user
echo "iptables -t nat -A PREROUTING -i br0 -p tcp --dport 53 -j DNAT --to 192.168.1.1" >> /etc/firewall.user

from the shell and restart the firewall via "/etc/init.d/firewall restart".

Rodney

Hey Rodney, thanks again for helping me get it working.

But actually, no, it doesn't seem to be working as desired -- the Android L devices seem to still be using the Google's DNS (as no ads are being blocked on Android which are being blocked on other devices).

Also I've done iptables --list and I'm not seeing anything to do with port 53 there, even after adding commands listed above to /etc/firewall.user and restarting firewall. sad

Any clue as to what's going on?

Can you paste the output of "iptables -t nat -L -nv"?

Rodney

Rodney,

to preserve the formatting I've put it at http://pastebin.com/455LqddA

So I do see it there, but still Android L devices seem to go around that.

(Last edited by stangri on 6 Mar 2015, 06:13)

EDIT: Shoot, I forgot that OpenWRT is br-lan, not br0.  Fixed below.

EDIT 2: Finally got a chance to actually test this locally.  Rewritten.

Use the following rules for IPv4:

iptables -t nat -A prerouting_rule -i br-lan -p udp --dport 53 -j DNAT --to 192.168.1.1
iptables -t nat -A prerouting_rule -i br-lan -p tcp --dport 53 -j DNAT --to 192.168.1.1

Put those two entries in /etc/firewall.user (removing any old entries you tested with) and restart the firewall via "/etc/init.d/firewall restart".  Verify by local testing and see that the rules are triggered by seeing the packet and byte counters increase via "iptables -t nat -L prerouting_rule -nv".

If it doesn't work, you're probably trying to use IPv6 for resolution, which gets a little more...interesting to intercept.

Rodney

(Last edited by rhester72 on 6 Mar 2015, 18:00)

Hey Rodney,
I'm afraid that despite your detailed walk-thru it still doesn't work.

I've changed br0 to br-lan, restarted firewall, verified rules got changed to br-lan:
root@ArcherC7:~# iptables -t nat -L -nv
Chain PREROUTING (policy ACCEPT 299 packets, 43809 bytes)
pkts bytes target     prot opt in     out     source               destination
  451 54259 delegate_prerouting  all  --  *      *       0.0.0.0/0            0.0.0.0/0
  143  9906 DNAT       udp  --  br-lan *       0.0.0.0/0            0.0.0.0/0            udp dpt:53 to:192.168.1.1
    0     0 DNAT       tcp  --  br-lan *       0.0.0.0/0            0.0.0.0/0            tcp dpt:53 to:192.168.1.1

And I'm still seeing ads on my L-devices, which means they're still reaching out to Google DNS servers directly.

Also, I'm not seeing byte counters increase with:
root@ArcherC7:~# iptables -t nat -L prerouting_rule -nv
Chain prerouting_rule (1 references)
pkts bytes target     prot opt in     out     source               destination

I've disabled the IPv6 on the router (to the best of my abilities) but when I go into Advanced WiFi on my Android device, I'm still seeing what looks like IPv6 addresses below my local IPv4 address.

I'm thinking it's Google Chrome for Android which is resolving everything some weird way. I've connected to shell via adb and pinged one of the blacklisted domains and it correctly points to my router pixelserv ip.

So I'm puzzled as to how to fix the Google Chrome now. I've cleared cache and data for the Chrome and it's still show the ads it's not supposed to. sad

Two problems that I see:

- You changed br0 to br-lan, but didn't change PREROUTING to prerouting_rule - however, this is largely a 'decorative' problem with your current ruleset (the rules are still being triggered, as the above shows the rule was hit over IPv4 143 times, so v4 lookups are definitely being 'captured')
- The above suggests that Chrome is almost certainly using IPv6 for lookups (basically, what seems to be happening is the request is arriving at your router over v6, but is then being forwarded to the Internet over v4, if I were to guess)

Let's test that theory, if you don't mind.

Enter the following at the command prompt:

ip6tables -A input_rule -i br-lan -p udp --dport 53 -j LOG
ip6tables -A input_rule -i br-lan -p tcp --dport 53 -j LOG

Do some browsing with Chrome on Android, then:

ip6tables -L input_rule -nv

and paste the output (if one or both are being hit, you'll see the counters be non-zero).

If that's the case (and based on reading the complaints to Google about hard-coding of DNS servers, it likely is), I'll see if we can craft a RFC-violating rule for v6 to capture similarly to v4 (and would suggest you keep both, once we get this working properly I'll bake a final 'recipe' for this that should work for anyone).

Rodney

Rodney, that was brilliant idea!
root@ArcherC7:~# ip6tables -L input_rule -nv
Chain input_rule (1 references)
pkts bytes target     prot opt in     out     source               destination
   71  5964 LOG        udp      br-lan *       ::/0                 ::/0                 udp dpt:53 LOG flags 0 level 4
    0     0 LOG        tcp      br-lan *       ::/0                 ::/0                 tcp dpt:53 LOG flags 0 level 4

So yeah, looks like they're using IPv6 (which is totally beyond my understanding of how it works as I've disabled IPv6 on my router).

PS. Changed to prerouting_rule in my IPv4 rules too, thanks.

Tricky little sucker.  smile

OK, last request, and I think we can get you in business - please supply the output from:

ip addr show dev br-lan

(If you aren't comfortable publishing IPv6 globals on a forum, feel free to PM me the information.)

Rodney

Rodney,
pastebin again so that it'd expire (in 24 hours): http://pastebin.com/Q5LHZwU5
Unfortunately there's no PM feature on this forum.

Can I maybe completely disable IPv6 somehow? Wouldn't that be easier? I'm on CC nightly. I mean if you can help me out and provide both options (ip6table rules and the ipv6 disabling) that'd be awesome.

(Last edited by stangri on 7 Mar 2015, 07:50)

EDIT: Corrected the chain (DNAT only allowed in PREROUTING) and added a reboot step to load the netfilter kernel module for IPv6 DNAT

Try the following:

opkg update
opkg install ip6tables-mod-nat kmod-ipt-nat6

Edit /etc/firewall.user and add these lines (leave the final v4 lines in place that we determined above):

ip6tables -t nat -A PREROUTING -i br-lan -p udp --dport 53 -j DNAT --to-destination [fd7a:edf1:483f::1]
ip6tables -t nat -A PREROUTING -i br-lan -p tcp --dport 53 -j DNAT --to-destination [fd7a:edf1:483f::1]

Reboot the router (to pick up the required modules)

Test Android L and validate that DNS works as expected

re: fully disabling IPv6, to be honest, I am not certain how that is (correctly) done - I've seen others say that they have had similar difficulty, but I might suggest spinning that as a different topic if that is a goal.

Let me know how the DNS thing works out after this.  smile

Rodney

(Last edited by rhester72 on 7 Mar 2015, 16:55)

Hey Rodney,

WOOOHOOOOO, it worked!

Kudos for this write-up. opkg update threw me off initially -- I'm a few builds behind on the trunk, so of course ip6tables was now requiring a newer kernel. Since I cached all the packages the day I installed, I just rebooted to drop the updated opkg and installed the individual ipk files from my cache.

I'm completely in the dark about ipv6 -- will that address in the rules ever change? What if I reflash openwrt?

If you're ever in Vancouver, BC -- I owe you a beer man.

The address in the rules shouldn't change - it's called a ULA address, and is determined semi-randomly by OpenWRT on first install.  It's contained in /etc/config/network as option ula_prefix, and is used as a fallback if no other IPv6 subnet is defined but IPv6 is enabled.  It is *not* a global and *not* routable (i.e. it cannot reach the Internet), but will be used throughout your LAN on any IPv6-enabled devices (which is a great many of them, these days).  If you were to do a full-up reflash, yes, it would be recalculated during firstboot.

Theoretically, you could "future-proof" (untested) in the following manner:

ip6tables -t nat -A PREROUTING -i br-lan -p udp --dport 53 -j DNAT --to-destination [`/usr/sbin/ip addr show dev br-lan | grep global | grep inet6 | awk '{print $2}' | cut -d/ -f1`]
ip6tables -t nat -A PREROUTING -i br-lan -p tcp --dport 53 -j DNAT --to-destination [`/usr/sbin/ip addr show dev br-lan | grep global | grep inet6 | awk '{print $2}' | cut -d/ -f1`]

That _should_ recalculate the global IPv6 bind to br-lan on each invocation of /etc/config/firewall, assuming it will allow proper subshell execution.  Feel free to test.  smile

Rodney

Rodney, thanks again -- I'm using a setup script to put all my settings back, so I just added these to the script. I tested your suggestion and it works just fine as part of the script. wink

Thanks again man, your help has been invaluable!

I know there are a lot of people suffering from this Lollipop bug, maybe it should be made a sticky?

Glad to hear it - you can do the same substitution for the IPv4 global, just changing this:

`/usr/sbin/ip addr show dev br-lan | grep global | grep inet6 | awk '{print $2}' | cut -d/ -f1`

to read like this instead:

`/usr/sbin/ip addr show dev br-lan | grep global | grep "inet " | awk '{print $2}' | cut -d/ -f1`

(note the space after inet).  That should pretty much bullet-proof it even if you re-subnet your IPv4 LAN.

This solution probably has more value than just Android Lollipop - for those who use dnsmasq to block certain sites by name, it's pretty easy to bypass by just manually setting DNS servers on the client unless you do something like this to force "captive DNS".  If someone (other than me) would like to properly summarize the approach (modules, the firewall.user rules with substitution, etc.) as a sticky, they are more than welcome to - everything needed is contained within this post, but it's a bit "spread out" and not necessarily as clear and concise as it probably should be for a sticky recipe.

Maybe someday Google will actually fix the underlying problem!  *laughs*

Rodney

Well, supposedly the 5.1 release is just around the corner and it's supposed to be fixed there.

I've tried the "inet " thing, but:
1. I'm not that great with shell and had problems escaping double quotes when part of the echo " ` " " ` " line
2. Pixelserv's ip is also reported by the ip command

so in the end I've used the -m 1 parameter of grep to filter it out.

But now that I've typed all that I'm guessing you didn't notice I've updated my OP yesterday. wink

You can avoid the double-quoting issue by using vi instead of tacking on with echo, but that might be harder to automate - I'll leave that as an exercise for the reader.  smile  Yes, having an additional IP for pixelserv would be problematic, though.  <G>

I did not notice the update to the OP - good work!  You may want to slightly expand it to mention that device-specific filtering can be accomplished via specifying a source MAC in the iptables rules, if for some reason someone should not want all devices forced through local DNS (though it's hard for me to even envision a use case like that).

Rodney

Rodney, thanks again, will expand momentarily!

The original Google bug is still open even with the 5.1 rollout. Can anyone confirm?