Connect Raspberry Pi to Asus router through USB port using Ethernet Gadget functionality
MIT License
This makes any Raspberry Pi capable of becoming USB Gadget to connect to LAN network through router's USB port.
Great way to run Pi-hole in your network on a budget Raspberry Pi Zero!
[!WARNING] This cannot be used together with Optware / Asus Download Master on stock firmware.
Asus routers have the capability to run a script when USB storage device is mounted.
This is how this magic is happening:
The script on the router also is monitoring for the interface changes in case the Raspberry Pi reboots.
[!IMPORTANT] Make sure you have
debugfs
command available - if not install it withapt-get install e2fsprogs
.
Add dtoverlay=dwc2
to /boot/config.txt and modules-load=dwc2
to /boot/cmdline.txt after rootwait
.
Install asuswrt-usb-network
script:
wget -O - "https://raw.githubusercontent.com/jacklul/asuswrt-usb-raspberry-pi/master/install_pi.sh" | sudo bash
Then enable it:
sudo systemctl enable asuswrt-usb-network.service
Modify configuration - sudo nano /etc/asuswrt-usb-network.conf
:
If you're running Asuswrt-Merlin set SKIP_MASS_STORAGE=true
services-start
script on the router side - no need to use command startup methodIf you're running official firmware in most cases you will need to set FAKE_ASUS_OPTWARE=true
script_usbmount
NVRAM variable so we need a workaroundASUS_OPTWARE_ARCH
to reflect architecture of the router (set to arm
by default)/bin/sh /jffs/scripts-startup.sh start
command is executed on the router - you can change this with FAKE_ASUS_OPTWARE_CMD
variableFor the full list of configuration variables - look below.
Enable the SSH access in the router, connect to it and then execute this command to install required scripts:
curl -fsSL "https://raw.githubusercontent.com/jacklul/asuswrt-usb-raspberry-pi/master/install_router.sh" | sh
This command will install required scripts from jacklul/asuswrt-scripts repository.
On Asuswrt-Merlin /jffs/scripts/services-start
will be used instead of /jffs/scripts-startup.sh
.
[!IMPORTANT] Do not run
/jffs/scripts-startup.sh install
as it will overwrite the value ofscript_usbmount
NVRAM variable set byinstall_router.sh
script!
Power off the router, connect your Pi to the router's USB port and then turn it on - in a few minutes it should all be working smoothly!
If it does not work and you're running stock firmware then make sure you are using build-in workaround - see "Modify configuration" step above.
You can set configuration variables in /etc/asuswrt-usb-network.conf
.
Variable | Default | Description |
---|---|---|
NETWORK_FUNCTION | "ecm" | Network gadget function to useSupported values are: rndis, ecm (recommended), eem, ncm
|
VERIFY_CONNECTION | true | Verify that we can reach gateway after enabling network gadget?Recommended if using services depending on systemd's network-online.target
|
SKIP_MASS_STORAGE | false | Skip adding initial mass storage gadget - instead setup network gadget right away?This is only useful on Asuswrt-Merlin firmware |
FAKE_ASUS_OPTWARE | false | Launch startup command through fake Asus' Optware installation?(requires SKIP_MASS_STORAGE=false ) |
FAKE_ASUS_OPTWARE_ARCH | "arm" | Optware architecture supported by the routerKnown values are: arm, mipsbig, mipsel
|
FAKE_ASUS_OPTWARE_CMD | "/bin/sh /jffs/scripts-startup.sh start" | Command to execute when fake Asus' Optware startsSetting this to empty value will use script_usbmount NVRAM variable |
TEMP_IMAGE_FILE | "/tmp/asuswrt-usb-network.img" | Temporary image file that will be created |
TEMP_IMAGE_SIZE | 1 | Image size in MB, might need to be increased in case your router doesn't want to mount the storage due to partition size errors |
TEMP_IMAGE_FS | "ext2" | Filesystem to use, must be supported by mkfs. command and the router, ext2 should be fine in most cases |
TEMP_IMAGE_DELETE | true | Delete temporary image after it is no longer useful? |
WAIT_TIMEOUT | 90 | Maximum seconds to wait for the router to write to the storage image fileAfter this time is reached the script will continue as normal |
WAIT_RETRY | 0 | How many seconds to wait before recreating the gadget deviceMust be set to at least 10 and lower than WAIT_TIMEOUT to workGadget restart can happen multiple times if WAIT_TIMEOUT / WAIT_RETRY is 2 or bigger |
WAIT_SLEEP | 1 | Time to sleep between each image contents checks, in seconds |
VERIFY_TIMEOUT | 60 | Maximum seconds to wait for the connection check |
VERIFY_SLEEP | 1 | Time to sleep between each gateway ping, in seconds |
GADGET_ID | "usbnet" | Gadget ID used in configfs path /sys/kernel/config/usb_gadget/[ID]
|
GADGET_PRODUCT | (generated) |
Product name, for example: "Raspberry Pi Zero W USB Gadget"(generated from /sys/firmware/devicetree/base/model ) |
GADGET_MANUFACTURER | "Raspberry Pi Foundation" | Product manufacturer |
GADGET_SERIAL | (generated) |
Device serial number, by default uses CPU serial(generated from /proc/cpuinfo ) |
GADGET_VENDOR_ID | "0x1d6b" |
0x1d6b = Linux Foundation |
GADGET_PRODUCT_ID | "0x0104" |
0x0104 = Multifunction Composite Gadget |
GADGET_USB_VERSION | "0x0200" |
0x0200 = USB 2.0, should be left unchanged |
GADGET_DEVICE_VERSION | "0x0100" | Should be incremented every time you change your setupThis only matters for Windows, no need to change it when plugging into Linux machines |
GADGET_DEVICE_CLASS | "0xef" |
0xef = Multi-interface devicesee https://www.usb.org/defined-class-codes
|
GADGET_DEVICE_SUBCLASS | "0x02" |
0x02 = Interface Association Descriptor sub class |
GADGET_DEVICE_PROTOCOL | "0x01" |
0x01 = Interface Association Descriptor protocol |
GADGET_MAX_PACKET_SIZE | "0x40" | Declare max packet size, decimal or hex |
GADGET_MAX_POWER | "250" | Declare max power usage, decimal or hex |
GADGET_ATTRIBUTES | "0x80" |
0xc0 = self powered, 0x80 = bus powered, should be left as bus powered |
GADGET_MAC_VENDOR | "B8:27:EB" | Vendor MAC prefix to use in generated MAC address (B8:27:EB = Raspberry Pi Foundation) |
GADGET_MAC_HOST | " " | Host MAC address, if empty - MAC address is generated from GADGET_MAC_VENDOR and CPU serial |
GADGET_MAC_DEVICE | " " | Device MAC address, if empty - MAC address is generated from CPU serial with 02: prefix |
GADGET_STORAGE_FILE | " " | Path to the image file that will be mounted as mass storage together with network function |
GADGET_STORAGE_FILE_CHECK | true | Whenever to run e2fsck (check and repair) on image file with each mount |
GADGET_STORAGE_STALL | " " | Change value of stall option, empty means system default |
GADGET_STORAGE_REMOVABLE | " " | Change value of removable option, empty means system defaultAutomatically set to 1 when attaching image file |
GADGET_STORAGE_CDROM | " " | Change value of cdrom option, empty means system default |
GADGET_STORAGE_RO | " " | Change value of ro option, empty means system default |
GADGET_STORAGE_NOFUA | " " | Change value of nofua option, empty means system default |
GADGET_STORAGE_INQUIRY_STRING | " " | Change value of inquiry_string , empty means system defaultMust be in this format: vendor(len 8) + model(len 16) + rev(len 4)
|
GADGET_SCRIPT | " " | Run custom script just before gadget creation, must be a valid path to executable script file, receives argument with device's configfs path |
Install force-dns.sh
script to force LAN and Guest WiFi clients to use the Pi-hole:
curl -fsSL "https://raw.githubusercontent.com/jacklul/asuswrt-scripts/master/scripts/force-dns.sh" -o /jffs/scripts/force-dns.sh
chmod +x /jffs/scripts/force-dns.sh
Edit /jffs/scripts/force-dns.conf
and paste the following:
PERMIT_MAC="01:02:03:04:05:06"
#PERMIT_IP="192.168.1.251-192.168.1.254"
REQUIRE_INTERFACE="usb*"
BLOCK_ROUTER_DNS=true
#FALLBACK_DNS_SERVER="9.9.9.9"
Replace 01:02:03:04:05:06
with the MAC address of the usb0
interface on the Pi - to grab it execute the following command on the Pi:
sudo asuswrt-usb-network status
# the `Host MAC` is the value you want to pick
You can add IPs or IP ranges to PERMIT_IP
variable to prevent that IPs from having their DNS server forced.
Use FALLBACK_DNS_SERVER
in case the Pi disconnects from the router, it can also be set to the router's IP address.
When running Pi-hole on the Pi it will be beneficial to run force-dns.sh
right after Pi connect to the router - edit /jffs/scripts/usb-network.conf
and paste the following:
EXECUTE_COMMAND="/jffs/scripts/force-dns.sh run"
Create an image that will serve as storage:
sudo dd if=/dev/zero of=/mass_storage.img bs=1M count=1024
# OR use fallocate which is faster
sudo fallocate -l 1G /mass_storage.img
# format as ext2 for compatibility
sudo mkfs.ext2 /mass_storage.img
Modify the configuration in /etc/asuswrt-usb-network.conf
:
GADGET_STORAGE_FILE="/mass_storage.img"
Then you will need to install few scripts from jacklul/asuswrt-scripts repository on the router:
curl -fsSL "https://raw.githubusercontent.com/jacklul/asuswrt-scripts/master/scripts/usb-mount.sh" -o /jffs/scripts/usb-mount.sh
curl -fsSL "https://raw.githubusercontent.com/jacklul/asuswrt-scripts/master/scripts/entware.sh" -o /jffs/scripts/entware.sh
chmod +x /jffs/scripts/usb-mount.sh /jffs/scripts/entware.sh
Reboot the Pi, wait for the storage to be mounted by usb-mount.sh
script then install Entware by using this command:
/jffs/scripts/entware.sh install
It will now automatically mount and boot Entware after scripts are started.