
Materials required
To get this "tethering" setup working, you will need:
- A PC with a serial port running DOS
- The epppd packet driver (it comes with, e.g., Arachne)
- A serial null-modem cable
- A USB-C to serial adapter (or whatever type of USB port your phone has) – this is the one I'm using: http://www.amazon.com/Serial-Adapter-Ch ... rd_w=p3Aah
- A rooted Android (preferably LineageOS) smartphone (I guess it would be easier on a Librem, and conversely impossible on an iPhone!), running a kernel to which one of the following applies:
- The kernel has built-in support for your serial adapter (unlikely I'd say…)
- The kernel supports modules so you can build the module for your serial adapter and insmod it (that's what I had to do)
- You can flash your own kernel, after building one with the serial adapter driver built in
- Termux with the tsu package installed (pkg install tsu)
Before investing any time or money in this project (assuming you're starting with a rooted phone), you can check if your kernel satisfies one of the conditions listed above. To check if it has built-in support for the serial adapter, you can run this command from Termux (you may need to "pkg install gzip" first):
Code: Select all sudo zgrep CONFIG_USB_SERIAL_ /proc/config.gz |
If the support is not built-in, the next step is to check if your kernel has module support:
Code: Select all sudo zgrep CONFIG_MODULES /proc/config.gz |
If CONFIG_MODULES is "n" or "not set", then you'll have to build a new kernel and flash it. Again, this requires patience, and (depending on your device) may be impossible

I don't have any experience of doing this, so I can't really advise on how to proceed…
Compiling the kernel module
I'm going to recount here what I did, even though I'm almost certain that there are better ways. It's very hard to find tutorials on this, and any I could find seemed to be out of date. If anyone has any better methods, please suggest them and I'll update the tutorial!
To compile a kernel module you need to get the kernel source, adjust the config file, and compile it. Given my experience compiling Linux kernels for PCs, I expected this to be straightforward, but in the Android ecosystem everything seems to be extra-complicated… Unfortunately all I can talk about is my own experience, which is with LineageOS on a FairPhone 3.
Basically I followed the instructions for building LineageOS, as far as running "./extract-files.sh". (If you're running LineageOS on something other than FP3, you can find the equivalent instructions for your device on the same wiki. If you're running a different Android distribution, or the stock ROM for your device, again I'm unfortunately unsure as to the best course of action…) Then instead of doing "brunch FP3", I did the following:
Code: Select all croot sed 's/# CONFIG_USB_SERIAL_FTDI_SIO is not set/CONFIG_USB_SERIAL_FTDI_SIO=m/' -i kernel/fairphone/sdm632/arch/arm64/configs/lineageos_FP3_defconfig mka kernel |
The whole process takes ages, since it needs to sync up the entire Android source tree. It is highly wasteful since we only want to compile a little module, but it's the only way I could make it work. Again, if anybody has any better instructions, please post them and I'll happily update the tutorial!
Anyway, at the end of this I had a file "out/target/product/FP3/obj/KERNEL_OBJ/drivers/usb/serial/ftdi_sio.ko" (again replace FP3 with your device). You can copy this ko file to your phone and place it in Termux's home directory. Once this ordeal is over, be sure not to delete anything – you'll need to resync the source and recompile the kernel if any OTA update makes a breaking change!
Setting up Termux scripts
Once you have the kernel module handy (if needed), you can get onto the fun stuff – actually setting up the null modem link from the phone side. All commands in this section are to be run on your phone in the Termux terminal. You can create the scripts as follows:
Code: Select all cat > nullmodem.sh <<EOF cd /data/data/com.termux/files/home # Install the kernel module if needed lsmod|grep ftdi || insmod ftdi_sio.ko # Verify the yoke is plugged in stat /dev/ttyUSB0 || exit "Looks like the serial adapter is not plugged in!" # Setup the actual connection # Based on http://medium.com/swlh/connecting-a-286-dos-pc-to-the-internet-through-a-serial-connection-in-2019-b93a422ff094 # No lock since Android doesn't have "/var/lock" as a location proot -b ppp_scripts:/etc/ppp pppd ttyUSB0 115200 local debug noccp passive proxyarp defaultroute noauth mtu 1500 192.168.122.1:192.168.122.2 EOF mkdir -p ppp_scripts cat > ppp_scripts/ip-down <<EOF #!/system/bin/sh # Clean up the firewall and routing stuff ROUTETABLE=$(/data/data/com.termux/files/usr/bin/ip route get $IPREMOTE | grep table | sed 's/.*table \([0-9]*\) .*/\1/') UPSTREAM=$(/data/data/com.termux/files/usr/bin/ip rule | grep "from all iif lo oif [^ ]* lookup $ROUTETABLE" | sed 's/.*oif \([^ ]*\).*/\1/') /data/data/com.termux/files/usr/bin/iptables -D tetherctrl_FORWARD -i $UPSTREAM -o $IFNAME -m state --state RELATED,ESTABLISHED -g tetherctrl_counters /data/data/com.termux/files/usr/bin/iptables -D tetherctrl_FORWARD -i $IFNAME -o $UPSTREAM -m state --state INVALID -j DROP /data/data/com.termux/files/usr/bin/iptables -D tetherctrl_FORWARD -i $IFNAME -o $UPSTREAM -g tetherctrl_counters /data/data/com.termux/files/usr/bin/iptables -D tetherctrl_counters -i $IFNAME -o $UPSTREAM -j RETURN /data/data/com.termux/files/usr/bin/iptables -D tetherctrl_counters -i $UPSTREAM -o $IFNAME -j RETURN /data/data/com.termux/files/usr/bin/ip rule del from all iif $IFNAME lookup $ROUTETABLE /data/data/com.termux/files/usr/bin/ip rule del to $IPREMOTE lookup $ROUTETABLE EOF cat > ppp_scripts/ip-up <<EOF #!/system/bin/sh # This will resolve to "ip route add $IPREMOTE <BLAH> table <CORRECTNUMBER>" ROUTETABLE=$(/data/data/com.termux/files/usr/bin/ip route get $IPREMOTE | grep table | sed 's/.*table \([0-9]*\) .*/\1/') /data/data/com.termux/files/usr/bin/ip route add $(/data/data/com.termux/files/usr/bin/ip route|grep $IPREMOTE) table $ROUTETABLE # Set up the appropriate forwarding rules in the firewall UPSTREAM=$(/data/data/com.termux/files/usr/bin/ip rule | grep "from all iif lo oif [^ ]* lookup $ROUTETABLE" | sed 's/.*oif \([^ ]*\).*/\1/') /data/data/com.termux/files/usr/bin/iptables -t nat -A tetherctrl_nat_POSTROUTING -o $UPSTREAM -j MASQUERADE /data/data/com.termux/files/usr/bin/iptables -D tetherctrl_FORWARD -j bw_global_alert || true # Remove it if it's already there so we don't duplicate it… /data/data/com.termux/files/usr/bin/iptables -I tetherctrl_FORWARD 1 -j bw_global_alert /data/data/com.termux/files/usr/bin/iptables -I tetherctrl_FORWARD 2 -i $UPSTREAM -o $IFNAME -m state --state RELATED,ESTABLISHED -g tetherctrl_counters /data/data/com.termux/files/usr/bin/iptables -I tetherctrl_FORWARD 3 -i $IFNAME -o $UPSTREAM -m state --state INVALID -j DROP /data/data/com.termux/files/usr/bin/iptables -I tetherctrl_FORWARD 4 -i $IFNAME -o $UPSTREAM -g tetherctrl_counters /data/data/com.termux/files/usr/bin/iptables -A tetherctrl_counters -i $IFNAME -o $UPSTREAM -j RETURN /data/data/com.termux/files/usr/bin/iptables -A tetherctrl_counters -i $UPSTREAM -o $IFNAME -j RETURN # And finally make sure that packets get routed properly from/to the client RULEPRIO=$(/data/data/com.termux/files/usr/bin/ip rule | grep -m 1 "from all iif [^ ]* lookup $ROUTETABLE" | sed 's/^\([0-9]*\):.*/\1/') /data/data/com.termux/files/usr/bin/ip rule add from all iif $IFNAME lookup $ROUTETABLE priority $RULEPRIO /data/data/com.termux/files/usr/bin/ip rule add to $IPREMOTE lookup $ROUTETABLE priority $RULEPRIO EOF |
If your kernel has the support for the serial adapter built-in (i.e. you don't need the module) then remove the lsmod/insmod line in "~/nullmodem.sh". You can also install the Termux:Widget app and create a friendly wrapper script:
Code: Select all cat > ~/.shortcuts/Setup\ Null\ Modem <<EOF termux-wake-lock sudo ~/nullmodem.sh termux-wake-unlock EOF |
Code: Select all pkg install termux-tools iptables iproute2 |
- Finds the ip routing table for Android's current default network connection (e.g. WiFi) and adds an entry that tells it to send any packets destined for 192.168.122.1 over the PPP link (instead of trying to send them over WiFi). Note that because of the hacky way I've done this, the link will probably stop working if you switch between WiFi and mobile data, or enable/disable a VPN, while the connection is up…
- Adds a "masquerade" iptables entry, so that any packets originating from the DOS PC are "masqueraded" as coming from the phone itself, when they are sent out to the external network.
- Adds several iptables entries to the "tetherctrl" chains, to ensure that packets to/from the DOS PC are routed through instead of being dropped.
- Adds some "rules" to the kernel's list to make sure that the correct routing tables are looked up for packets coming from the DOS PC can be sent to the external network, and that any packets coming in response are routed back to the DOS PC. Without this, you'd get "Destination Net Unreachable" errors…
Connecting the DOS PC
Once pppd is running on the phone, and the null-modem is connected to the DOS PC's serial port, you can start epppd from the DOS prompt:
Code: Select all epppd com1 115200 |
Code: Select all my_ip=192.168.122.2 nameserver=192.168.1.1 netmask=255.255.255.0 domain.suffix="" gateway=192.168.122.1 |
If running Windows 3.1 (like me), you can also install Trumpet. One way to use it is to run "winpkt 0x60" after starting epppd, then start Windows. You can then configure Trumpet with the IP and DNS settings given above. I use Trumpet 2.0 (rather than the 3.0 offered here) since it doesn't impose a 30-day trial period. Apparently it is still possible to "register" the newer versions to support the author financially, but I have not succeeded in getting a registration code by doing so

It is also possible (and seems to work a bit better) to bypass epppd by using Trumpet's "Internal PPP", which involves a few different settings. In this case, you don't run epppd at all, but just start Windows. My settings currently look something like this (again, change your "Name server" if needed):
But either way, it's unfortunately quite dodgy. Attempting to download a file from win3x.org from any Windows-based browser seems to lock up the entire connection, while downloading files using Lynx from DOS doesn't cause any problems. I'm not sure if this can be overcome with the correct choice of MTU/MSS/RWIN – again, any guidance would be welcome!
Setting up a HTTP Proxy
Now that you've got Windows 3.1 accessing the internet through your phone, you may wish to set up a WebOne instance on the latter, so that the former can access more of the modern web. To do this, you will need proot-distro, which allows you to install a Linux distro within Termux. I used Debian for this, though admittedly Ubuntu might have been a bit more straightforward.
Basically, I did this:
Code: Select all pkg install daemonize pkg install proot-distro proot-distro install debian cd ../usr/var/lib/proot-distro/installed-rootfs/debian/root/ wget http://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb # May need to change depending on the version of Debian you have installed wget http://github.com/atauenis/webone/releases/download/v0.14.0/webone.0.14.0.linux-arm64.deb proot-distro login debian # FROM HERE EVERYTHING IS INSIDE DEBIAN dpkg -i packages-microsoft-prod.deb apt update apt install ./webone.0.14.0.linux-amd64.deb |
Then you need to add this to the end of ~/ppp_scripts/ip-up:
Code: Select all # Setup our HTTP proxy daemonize /data/data/com.termux/files/usr/bin/proot-distro login debian -- webone --daemon |
Code: Select all # Shut down HTTP proxy killall webone |

Conclusion
So there you have it! It is possible, with a bit of patience and hacking, to use a modern smartphone as a serial modem and connect DOS to the internet over WiFi/mobile! Naturally, this being serial, the connection is very slow, but I guess that adds to the nostalgia factor. In fact, maybe a bit of tweaking to the PPP scripts (an ip-pre-up script?) could be used to make the phone play a recording of the traditional dial-up sound to complete the experience!

Any feedback welcome!
