Configuring USB tethering manually with root I've exhausted the documented methods of programmatically enabling USB tethering:


The service method doesn't work on my phone (Samsung Note 4). It seems to be some sort of protected API, and poking it from adb has no effect.
The shell input method is not applicable, as it requires that the device is unlocked, and there doesn't seem to be a way to programmatically and non-destructively unlock a device locked with a fingerprint.


So, I'm looking into achieving this goal "the hard way", i.e. do everything that the Android userspace does when you tap the USB tether checkbox.

As far as I can tell, this involves the following steps (most of which would be similar to how you'd do it on a Linux machine):


Establish an IP connection to the host computer through USB. This involves putting USB in rndis mode, setting up routes, assigning an IP, etc. 

I've already figured out how to achieve this and posted the answer here.
Launch a dnsmasq instance, to assign an IP to the connected PC via DHCP, and forward DNS requests.

This step shouldn't be necessary if you don't need DNS server detection and assign static IPs on the USB interfaces yourself.
Enable IP forwarding (/proc/sys/net/ipv4/ip_forward).
Set up iptables to enable forwarding/masquerading.
Update routing tables for traffic accounting.


I managed to write a bunch of scripts which replicate exactly the visible effects (described above) as performed by the Android OS when enabling USB tethering through its UI. To create the scripts, I recorded the system state (iptables -S, ip route show table all) with and without tethering, then converted the differences into shell commands to apply the changes to the system. However, my replicas are still not enough; some part of the system remains unconfigured which prevents traffic from going through.

Does anyone have a clue as to what I might be missing, or how I can narrow down / debug the problem?
A buddy let me know that there is more than one sysctl setting for forwarding (a global one and one per interface, then more for IPv6). 

On my phone, the relevant settings are:

vars=(
    net.ipv4.conf.all.accept_redirects=0
    net.ipv4.conf.all.forwarding=1
    net.ipv4.conf.default.forwarding=1
    net.ipv4.conf.ip6tnl0.forwarding=1
    net.ipv4.conf.lo.forwarding=1
    net.ipv4.conf.p2p0.forwarding=1
    net.ipv4.conf.rmnet0.forwarding=1
    net.ipv4.conf.rmnet1.forwarding=1
    net.ipv4.conf.rmnet2.forwarding=1
    net.ipv4.conf.rmnet3.forwarding=1
    net.ipv4.conf.rmnet4.forwarding=1
    net.ipv4.conf.rndis0.forwarding=1
    net.ipv4.conf.sit0.forwarding=1
    net.ipv4.conf.wlan0.forwarding=1
    net.ipv4.ip_forward=1
    net.ipv6.conf.all.forwarding=2
    net.ipv6.conf.all.proxy_ndp=2
    net.ipv6.conf.default.forwarding=2
    net.ipv6.conf.ip6tnl0.forwarding=2
    net.ipv6.conf.lo.forwarding=2
    net.ipv6.conf.p2p0.forwarding=2
    net.ipv6.conf.rmnet0.forwarding=2
    net.ipv6.conf.rmnet1.forwarding=2
    net.ipv6.conf.rmnet2.forwarding=2
    net.ipv6.conf.rmnet3.forwarding=2
    net.ipv6.conf.rmnet4.forwarding=2
    net.ipv6.conf.rndis0.forwarding=2
    net.ipv6.conf.sit0.forwarding=2
    net.ipv6.conf.wlan0.forwarding=2
) ; sysctl "${vars[@]}"


With these and the other ones, forwarding and tethering works!