Android betrays tethering data
When I upgraded an Android device the other day, I found that tethering completely stopped working. The updated CyanogenMod had inherited a new bug from Android, informing the carrier that I was tethering. The carrier, Vodafone Italy, had decided to make my life miserable by blocking that traffic. I had a closer look and managed to find a workaround.
There is absolutely no difference, from a technical perspective, between data transmitted from a mobile device on-screen application and data transmitted from tethering. Revealing the use of tethering to the carrier is a massive breach of privacy - yet comments in the Google bug tracker suggest it is a feature rather than a bug. This little table helps put that logic in perspective:
|Product||Person who carries handset|
|User||Mobile network who wants to discriminate against some types of network traffic to squeeze more money out of the Product|
|Feature||Revealing private information about the way the Product uses his/her Internet so the real User can profit.|
It is also bad news for the environment: many people are being tricked into buying un-needed USB dongle modems that will end up in the rubbish in 1-2 years time when their contract expires and the company pushes them to upgrade to the next best thing.
Behind the scenes
What does it really mean in practice, how does Android tell your carrier which data is from tethering?
As my device is rooted and as it is my device and I will do what I want with it, I decided to have a look inside.
The ip command revealed that there are now two network devices, rmmnet_usb0 and rmmnet_usb1. The basic ip route command reveals that traffic from different source addresses is handled differently and split over the different network devices:
shell@android:/ # ip route 0.0.0.0/1 dev tun0 scope link default via 100.66.150.89 dev rmnet_usb0 22.214.171.124 via 100.87.31.214 dev rmnet_usb1 126.96.36.199 via 100.87.31.214 dev rmnet_usb1 100.66.150.88/30 dev rmnet_usb0 proto kernel scope link src 100.66.150.90 100.66.150.89 dev rmnet_usb0 scope link 100.87.31.212/30 dev rmnet_usb1 proto kernel scope link src 100.87.31.213 100.87.31.214 dev rmnet_usb1 scope link 188.8.131.52/1 dev tun0 scope link 192.168.42.0/24 dev rndis0 proto kernel scope link src 192.168.42.129
I then looked more closely and found that there is also an extra routing table, it can be found with ip rule
shell@android:/ # ip rule show 0: from all lookup local 32765: from 192.168.42.0/24 lookup 60 32766: from all lookup main 32767: from all lookup default shell@android:/ # ip route show table 60 default via 100.87.51.57 dev rmnet_usb1 100.87.51.57 dev rmnet_usb1 192.168.42.0/24 dev rndis0 scope link
In this routing table, it is obvious that data from the tethering subnet (192.168.42.0/24) is sent over the extra device rmnet_usb1.
Manually cleaning it up
If the phone is rooted, it is possible to very quickly change the routing table to get all the tethering traffic going through the normal rmnet_usb0 interface again.
It is necessary to get rid of that alternative routing table first:
ip rule del pref 32765
and then update the iptables entries that refer to interface names:
iptables -t nat -I natctrl_nat_POSTROUTING -s 192.168.0.0/16 -o rmnet_usb0 -j MASQUERADE iptables -I natctrl_FORWARD -i rmmnet_usb0 -j RETURN
This immediately resolved the problem for me on the Vodafone network in Italy.
If Google can be bullied into accepting this kind of discriminatory routing in the stock Android builds and it can even propagate into CyanogenMod, then I'm just glad I'm not running one of those Android builds that has been explicitly "enhanced" by a carrier.
It raises big questions about who really is the owner and user of the device and who is receiving the value when a person pays money to "buy" a device.