TTL modification for outgoing traffic with OpenWRT
For OpenWRT 22.03 and above using firewall4, please refer to the following updated article.
Introduction
Do you often find yourself in the position of having to access the internet by tethering your 4G/LTE smartphone? I sure do, because my local cable ISP sucks ass. Unfortunately, however, cellular carriers have a number of tricks up their sleeve to detect (and potentially block you from) tethering. While more advanced techniques such as user agent sniffing and OS TCP/IP stack fingerprinting are harder to circumvent, TTL detection seems to be in much wider use and is also much easier to work around. In this article, we'll go over how to set up TTL modification on an OpenWRT-powered router.
Basic Theory
All IP packets contain an initial time-to-live (TTL) value that is decremented by one for every hop, or router it travels through, until it reaches the destination. By definition, packets are dropped when their TTL reaches zero in order to prevent infinite routing loops. When tethering is enabled on an Android or Apple smartphone, the additional layer of NAT, and therefore routing, decrements the TTL of outgoing packets through the tether interface by 1. Since the default TTL on Android and iOS is 64, it is trivial for phone carriers to distinguish between true on-phone traffic and tethered traffic by checking the value of the TTL and whether or not it equals 64. On Windows, for example, the default TTL is 128 (and therefore 127 after being routed through the tether NAT) - a far cry from 64 and a dead giveaway that tethering is occurring. Therefore, in order to mask tethered traffic, we need to set the TTL of all outgoing tethered packets to a uniform value. Taking into account the extra hop added by the tether interface and the default TTL 64 of most phones, this value is 64 + 1 = 65.
Prerequisites
The instructions below are developed and tested on a GL.iNet AR-300M16 running OpenWRT 18.06.2.
Case 1: Standard routed setup
Scenario: Standard routed setup with separate LAN and WAN (tether) interfaces
- Using the OpenWRT package manager via LuCI or opkg CLI, install the
iptables-mod-ipopt
package. - Navigate to Network → Firewall → Custom Rules.
- Add the following line:
iptables -t mangle -I POSTROUTING -o usb0 -j TTL --ttl-set 65
- Click Restart Firewall to save
If necessary, change usb0
to wlan0
(or whichever interface name corresponds to your tether interface.)
Case 2: Bridged setup
Scenario: Tether WAN interface is bridged to LAN interface, i.e. OpenWRT acting as a transparent USB-to-ethernet bridge for a pre-existing router and network
- Using the OpenWRT package manager via LuCI or opkg CLI, install the
iptables-mod-ipopt
andiptables-mod-physdev
packages. - Navigate to Network → Firewall → Custom Rules.
- Add the following line:
iptables -t mangle -I POSTROUTING -m physdev --physdev-out usb0 -j TTL --ttl-set 65
- SSH into the OpenWRT device
- In
/etc/sysctl.conf
, add the following lines:
net.bridge.bridge-nf-call-arptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
- Click Restart Firewall to save
If necessary, change usb0
to wlan0
(or whichever interface name corresponds to your tether interface.)
If everything was configured properly, you should be able to see the traffic counters slowly incrementing for your new iptables rule in Status → Firewall.