Skip to content

Instantly share code, notes, and snippets.

@chrisob
Last active January 22, 2024 12:26
Show Gist options
  • Save chrisob/f4750f94b14f45f9af04 to your computer and use it in GitHub Desktop.
Save chrisob/f4750f94b14f45f9af04 to your computer and use it in GitHub Desktop.
Android reverse tethering over bridged SSH tap interface
#!/bin/bash
# Based on the scripts written by class101 of xda-developers.com:
# http://forum.xda-developers.com/showpost.php?p=57490025&postcount=205
#
# This script enables a secure tunnel for your android phone to "reverse tether"
# and access the internet/a private network via the following steps:
#
# 1. Establish a level 3 (TAP) tunnel from your local host to a remote server via SSH (tap0)
# 2. Establish a level 3 interface between your local host and your android phone via USB (usb0)
# 3. Bridge these two devices on the local host, so your android uses the remote server as a gateway
# 4. Configure the appropriate addresses, routes, firewall rules, and DNS servers
#
# Initial required setup:
#
# On all hosts:
# -root access
# -iptables and iproute installed
#
# On the remote SSH host:
# -At least OpenSSH server 4.3
# -Your SSH public key is in /root/.ssh/authorized_keys
# -"PermitRootLogin yes" is in /etc/ssh/sshd_config
# -"PermitTunnel yes" is in /etc/ssh/sshd_config
#
# On the local host:
# -At least OpenSSH client 4.3
# -The bridge kernel module loaded
# -Android ADB is installed
# -Access to your device with "adb shell"
#
# On your android device (tested with 5.1):
# -Permanent access to your device via ADB from your PC (by fingerprint)
#
#
# Interfaces:
# tap0 - Level 3 (ethernet) tunnel between local host and remote host (both called tap0)
# usb0 - Local NIC to android phone
# br0 - Local bridge NIC bridging tap0 and usb0
# rndis0 - Android internal NIC; other side of usb0
#
# Limitations:
# Only tested with Android 5.1, and CentOS-6 for both the remote and local hosts.
# All android apps that rely on android's "ConnectivityManager" for networking will not work,
# such as the android market, hangouts, etc.
# More information found at https://code.google.com/p/android/issues/detail?id=99034
REMOTE_SSH_IP=1.2.3.4
REMOTE_SSH_PORT=22
SSH_KEY=/root/.ssh/id_rsa
SSH_SOCK=/root/tunnel.sock
SSH_PROXY_COMMAND="none"
BRIDGE_SUBNET=10.123.123.0
PREFIX=24
REMOTE_GATEWAY=10.123.123.100
ANDROID_IP=10.123.123.200
DNS1=8.8.8.8
DNS2=8.8.4.4
ANDROID_UP="
echo -- rndis0: adding ip rule --
ip rule add from all lookup main
echo -- rndis0: flushing interface --
ip addr flush dev rndis0
echo -- rndis0: setting ip --
ip addr add ${ANDROID_IP}/${PREFIX} dev rndis0
echo -- rndis0: starting the interface --
ip link set rndis0 up
echo -- rndis0: setting route --
ip route add default via ${REMOTE_GATEWAY} dev rndis0
echo -- rndis0: setting iptables --
iptables -t nat -I POSTROUTING 1 -o rndis0 -j MASQUERADE
echo -- rndis0: setting ip_forward --
echo 1 > /proc/sys/net/ipv4/ip_forward
echo -- rndis0: setting properties --
setprop net.dns1 ${DNS1}
setprop net.dns2 ${DNS2}
setprop net.rndis0.dns1 ${DNS1}
setprop net.rndis0.dns2 ${DNS2}
setprop net.rndis0.gw ${REMOTE_GATEWAY}
setprop net.rndis0.gateway ${REMOTE_GATEWAY}
echo -- rndis0: starting dnsmasq --
killall dnsmasq &> /dev/null
dnsmasq --no-poll --pid-file --interface=rndis0 --interface=wlan0 --interface=rmnet0 --bogus-priv --filterwin2k --no-resolv --server=${DNS1} --server=${DNS2} < /dev/null
"
ANDROID_DOWN="
echo -- rndis0: stopping the interface --
ip link set rndis0 down
echo -- rndis0: flushing interface --
ip addr flush dev rndis0
echo -- rndis0: deleting ip rule --
ip rule del from all lookup main
echo -- rndis0: stopping dnsmasq --
killall dnsmasq &> /dev/null
echo -- rndis0: deleting iptables rule --
iptables -t nat -D POSTROUTING -o rndis0 -j MASQUERADE
echo -- rndis0: setting back usb mode to mtp --
setprop sys.usb.config 'mtp,adb'
"
REMOTE_UP="
ip addr add ${REMOTE_GATEWAY}/${PREFIX} dev tap0
ip link set tap0 up
iptables -t filter -I FORWARD 1 -i tap0 -j ACCEPT
iptables -t filter -I INPUT 1 -i tap0 -s ${BRIDGE_SUBNET}/${PREFIX} -j ACCEPT
iptables -t nat -I POSTROUTING 1 -o eth0 -s ${BRIDGE_SUBNET}/${PREFIX} -j MASQUERADE
"
REMOTE_DOWN="
iptables -t filter -D FORWARD -i tap0 -j ACCEPT
iptables -t filter -D INPUT -i tap0 -s ${BRIDGE_SUBNET}/${PREFIX} -j ACCEPT
iptables -t nat -D POSTROUTING -o eth0 -s ${BRIDGE_SUBNET}/${PREFIX} -j MASQUERADE
"
function usage {
echo -e "Usage:\n$0 {up|down}\n"
}
echo
if [ "$1" == "up" ]; then
echo "Checking if local tap0 exists..."
ip addr show tap0 > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "Creating local and remote tap0..."
ssh -f root@${REMOTE_SSH_IP} \
-o "Port ${REMOTE_SSH_PORT}" \
-o "IdentityFile ${SSH_KEY}" \
-o "ProxyCommand ${SSH_PROXY_COMMAND}" \
-o "Tunnel ethernet" \
-o "TunnelDevice 0:0" \
-o "ExitOnForwardFailure yes" \
-o "ServerAliveInterval 60" \
-o "ControlMaster yes" \
-o "ControlPath ${SSH_SOCK}" \
"${REMOTE_UP}"
fi
echo "Waiting for android to become available..."
adb wait-for-device
echo "Enabling android rndis0..."
adb shell su -c "setprop sys.usb.config rndis,adb"
echo "Waiting for android to become available..."
sleep 5
adb wait-for-device
# Disable local usb0 to android device to prevent receiving a dhcp address
ip link set usb0 down
echo "Configuring android rndis0..."
adb shell su -c "${ANDROID_UP}"
echo "Configuring local br0..."
brctl addbr br0
brctl addif br0 tap0
brctl addif br0 usb0
ip addr flush dev usb0
ip addr flush dev tap0
ip addr flush dev br0
echo "Enabling local br0..."
ip link set br0 up
echo "Enabling local tap0..."
ip link set tap0 up
echo "Enabling local usb0..."
ip link set usb0 up
echo "Creating local iptables rules..."
iptables -t filter -I FORWARD 1 -i br0 -s ${BRIDGE_SUBNET}/${PREFIX} -j ACCEPT
iptables -t filter -I FORWARD 1 -i br0 -d ${BRIDGE_SUBNET}/${PREFIX} -j ACCEPT
elif [ "$1" == "down" ]; then
# TODO: teardown isn't as thorough as configuration, some configs aren't reset
echo "Checking if local tap0 exists..."
ip addr show tap0 > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "Disabling local and remote tap0..."
ssh -S "${SSH_SOCK}" -O exit does-not-matter-what-goes-here-blaahh > /dev/null
echo "Removing remote iptables rules..."
ssh root@${REMOTE_SSH_IP} \
-o "Port ${REMOTE_SSH_PORT}" \
-o "IdentityFile ${SSH_KEY}" \
-o "ProxyCommand ${SSH_PROXY_COMMAND}" \
"${REMOTE_DOWN}"
fi
echo "Disabling local br0..."
ip link set usb0 down
USB0_IS_PRESENT=$?
ip link set br0 down
brctl delbr br0
echo "Removing local iptables rules..."
iptables -t filter -D FORWARD -i br0 -s ${BRIDGE_SUBNET}/${PREFIX} -j ACCEPT
iptables -t filter -D FORWARD -i br0 -d ${BRIDGE_SUBNET}/${PREFIX} -j ACCEPT
if [ $USB0_IS_PRESENT -eq 0 ]; then
echo "Waiting for android to become available..."
adb wait-for-device
echo "Disabling android rndis0..."
adb shell su -c "${ANDROID_DOWN}"
fi
else
usage
exit 1
fi
echo "Done!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment