#!/usr/bin/env bash ## a basic script for building openwrt images for ## the TPLink Archer C7 v2 on ubuntu 20.04 ## install all dependencies before running # $ sudo apt install -y subversion g++ zlib1g-dev build-essential git python rsync man-db gawk libncurses5-dev gettext unzip file libssl-dev wget zip time ## recovery instructions: # set up tftpd server listening on 192.168.0.66 serving ArcherC7v2_tp_recovery.bin (stripped, non-boot) # available at https://cdn.seedno.de/misc/ArcherC7v2_tp_recovery.bin # plug ethernet cable into LAN1 on Archer C7, unplugging any cable in WAN port # power up router, then immediately press and hold the WPS/Reset button until the WPS light turns on # wait ~2 minutes for router to complete reset ## interfaces # eth0 - LAN (bridged) # eth1 - LAN (bridged) # br-LAN - LAN bridge # wlan0 - 5GHz WiFi tether # wlan1 - 2.4GHz WiFi tether # usb0 - Android USB tether # eth2 - iPhone USB tether ## switch0 ports # Port 0: LAN CPU(?) # Port 1: WAN # Port 2: LAN 1 # Port 3: LAN 2 # Port 4: LAN 3 # Port 5: LAN 4 # Port 6: WAN CPU(?) ## front leds, from left to right # tp-link:green:power (indicates system is powered on) # tp-link:green:system (flashes when system is booting or upgrading) # tp-link:green:wlan2g (indicates whether 2.4ghz uplink is in use) # tp-link:green:wlan5g (indicates whether 5ghz uplink is in use) # tp-link:green:qss (indicates internet connectivity status) ## rear lcds, from top to bottom # tp-link:green:usb1 (indicates whether usb uplink 1 is in use) # tp-link:green:usb2 (indicates whether usb uplink 2 is in use) # fail in a sane manner set -eo pipefail # make sure the correct number of arguments are passed; if not, output syntax and exit if [ "$#" -ne 0 ]; then echo -e "\nUsage: openwrtimagebuilder\n" exit 1 fi # set hostname to first argument hostname="tether" # set base directory basedir="$(pwd)/$hostname" # set logfile location logfile="$basedir/build-$(date +%Y%m%d%H%M%S).log" # set imagebuilder directory imagebuilderdir="$basedir/imagebuilder" # set OpenWRT imagebuilder version to use release="19.07.4" target="ath79" profile="tplink_archer-c7-v2" version="openwrt-imagebuilder-$release-$target-generic.Linux-x86_64" tarball="https://downloads.openwrt.org/releases/$release/targets/$target/generic/$version.tar.xz" # delete any old builds if they exist if [ -d "$imagebuilderdir/$version" ]; then echo -e "\nRemoving old imagebuilder files..." >> "$logfile" 2>&1 rm -rf "$imagebuilderdir/$version" fi # create base directory for account mkdir -p "$imagebuilderdir" # display log location echo -e "\nLogging all build output to $logfile" # display git commit hash echo -e "\nBuilding from commit $(git rev-parse HEAD)." >> "$logfile" 2>&1 # prepend timestamp to logfile echo -e "\nBuild began at $(date +%Y/%m/%d-%H:%M).\n" >> "$logfile" 2>&1 # wrap the script into a function for logging purposes { # display imagebuilder version echo "Imagebuilder version set to $version" # display imagebuilder directory echo "Imagebuilder directory is $imagebuilderdir" # set and display working directory workdir="$imagebuilderdir/$version" echo "Working directory is $workdir" # download and extract the openwrt imagebuilder tool echo "Downloading imagebuilder tarball..." mkdir -p "$imagebuilderdir" curl -s "$tarball" | tar -xJf - -C "$imagebuilderdir" echo "Imagebuilder downloaded." # create directory for file overrides; this will serve as the effective / for any overwriting files # i.e. files/etc/dropbear/authorized_keys will be located at /etc/dropbear/authorized_keys in the final squashfs image echo -e "\nCreating required directories..." mkdir -p "$workdir"/files/etc/config mkdir -p "$workdir"/files/etc/crontabs mkdir -p "$workdir"/files/etc/hotplug.d/iface mkdir -p "$workdir"/files/etc/init.d mkdir -p "$workdir"/files/etc/rc.button mkdir -p "$workdir"/files/etc/uci-defaults mkdir -p "$workdir"/files/www # finished creating directories echo -e "Finished creating directories.\n" # begin generating config files echo -e "\nGenerating system config files." # set login credentials echo -e "\nBegin login credentials." cat <<'EOL' | tee "$workdir"/files/etc/shadow root:$1$Bi5SZzpW$o3ymak7u35zX.fuuGCjt2.:18396:0:99999:7::: daemon:*:0:0:99999:7::: ftp:*:0:0:99999:7::: network:*:0:0:99999:7::: nobody:*:0:0:99999:7::: dnsmasq:x:0:0:99999:7::: EOL echo -e "End login credentials.\n" # configure shell echo -e "\nBegin shell config." cat <<'EOL' | tee "$workdir"/files/etc/profile #!/bin/sh [ -e /tmp/.failsafe ] && export FAILSAFE=1 [ -f /etc/banner ] && cat /etc/banner [ -n "$FAILSAFE" ] && cat /etc/banner.failsafe fgrep -sq '/ overlay ro,' /proc/mounts && { echo 'Your JFFS2-partition seems full and overlayfs is mounted read-only.' echo 'Please try to remove files from /overlay/upper/... and reboot!' } export PATH="/usr/sbin:/usr/bin:/sbin:/bin" export HOME=$(grep -e "^${USER:-root}:" /etc/passwd | cut -d ":" -f 6) export HOME=${HOME:-/root} export PS1='\[\e[1;32m\][\[\e[1;37m\]\u@\h \[\e[1;35m\]\w\[\e[1;32m\]]\[\e[1;31m\]\\$\[\e[m\] ' case "$TERM" in xterm*|rxvt*) export PS1='\[\e]0;\u@\h: \w\a\]'$PS1 ;; esac [ -x /bin/more ] || alias more=less [ -x /usr/bin/vim ] && alias vi=vim || alias vim=vi alias ll='ls -alF --color=auto' [ -z "$KSH_VERSION" -o \! -s /etc/mkshrc ] || . /etc/mkshrc [ -x /usr/bin/arp -o -x /sbin/arp ] || arp() { cat /proc/net/arp; } [ -x /usr/bin/ldd ] || ldd() { LD_TRACE_LOADED_OBJECTS=1 $*; } [ -n "$FAILSAFE" ] || { for FILE in /etc/profile.d/*.sh; do [ -e "$FILE" ] && . "$FILE" done unset FILE } if ( grep -qs '^root::' /etc/shadow && \ [ -z "$FAILSAFE" ] ) then cat << EOF === WARNING! ===================================== There is no root password defined on this device! Use the "passwd" command to set up a new password in order to prevent unauthorized SSH logins. -------------------------------------------------- EOF fi service() { [ -f "/etc/init.d/$1" ] || { echo "service "'"'"$1"'"'" not found, the following services are available:" ls "/etc/init.d" return 1 } /etc/init.d/$@ } EOL echo -e "End shell config.\n" # create dnsmasq configs and disable win2k filtering (which breaks jabber/xmpp) echo -e "\nBegin dnsmasq config." cat <<'EOL' | tee "$workdir"/files/etc/config/dhcp config dnsmasq option domainneeded '1' option localise_queries '1' option rebind_protection '1' option rebind_localhost '1' option local '/lan/' option domain 'lan' option expandhosts '1' option authoritative '1' option readethers '1' option leasefile '/tmp/dhcp.leases' option resolvfile '/tmp/resolv.conf.auto' option localservice '1' option nonwildcard '0' option filterwin2k '0' option nonegcache '1' option sequential_ip '1' config dhcp 'LAN' option interface 'LAN' option ignore '1' config dhcp 'WAN_ANDROID_USB' option interface 'WAN_ANDROID_USB' option ignore '1' config dhcp 'WAN_IPHONE_USB' option interface 'WAN_IPHONE_USB' option ignore '1' config dhcp 'WAN_WIFI_2' option interface 'WAN_WIFI_2' option ignore '1' config dhcp 'WAN_WIFI_5' option interface 'WAN_WIFI_5' option ignore '1' config odhcpd 'odhcpd' option maindhcp '0' option leasefile '/tmp/hosts/odhcpd' option leasetrigger '/usr/sbin/odhcpd-update' EOL echo -e "End dnsmasq config.\n" # set default firewall policy to allow all traffic; this is a relay, not a firewall echo -e "\nBegin firewall config." cat <<'EOL' | tee "$workdir"/files/etc/config/firewall config defaults option input 'ACCEPT' option forward 'ACCEPT' option output 'ACCEPT' option drop_invalid '1' option synflood_protect '1' option flow_offloading '1' option flow_offloading_hw '1' EOL echo -e "End firewall config.\n" # set ssh listen port to tcp/9022 echo -e "\nBegin dropbear config." cat <<'EOL' | tee "$workdir"/files/etc/config/dropbear config dropbear option PasswordAuth 'on' option Port '9022' EOL echo -e "End dropbear config.\n" # set up a uhttpd instance listening on 443/tcp echo -e "\nBegin uhttpd config." cat <<'EOL' | tee "$workdir"/files/etc/config/uhttpd config uhttpd 'luci' list listen_https '0.0.0.0:443' option home '/www' option rfc1918_filter '1' option max_requests '3' option max_connections '100' option cert '/etc/uhttpd.crt' option key '/etc/uhttpd.key' option cgi_prefix '/cgi-bin' option script_timeout '60' option network_timeout '30' option http_keepalive '20' option tcp_keepalive '1' option ubus_prefix '/ubus' list interpreter ".php=/usr/bin/php-cgi" config cert 'px5g' option days '3650' option bits '1024' option country 'US' option state 'Minnesota' option location 'Minneapolis' option commonname 'OpenWRT' EOL echo -e "End uhttp config.\n" # set hostname, timezone, and other system info echo -e "\nBegin system config." cat </dev/null" # turn on wps light when connectivity is restored command off "echo '255' | tee /sys/class/leds/tp-link:green:qss/brightness >/dev/null" } # targets to probe target "1.1.1.1" { description "Cloudflare Anycast DNS" interval 1s alarms "internet" } EOL echo -e "End apinger config.\n" # create an empty script for the rfkill button # we only want to disable it via script or webui echo -e "\nBegin rfkill toggle disable." cat <<'EOL' | tee "$workdir"/files/etc/rc.button/rfkill # do nothing on rfkill switch toggle EOL echo -e "End rfkill toggle disable.\n" # create an empty script for the wps trigger # we only want wps to be activated via script echo -e "\nBegin wps button disable." cat <<'EOL' | tee "$workdir"/files/etc/rc.button/wps # do nothing on wps button press EOL echo -e "End wps button disable.\n" # create relayd address update script echo -e "\nBegin /etc/init.d/relayaddr script." cat <<'EOL' | tee "$workdir"/files/etc/init.d/relayaddr #!/usr/bin/env ash # check for lock file if { set -C; 2>/dev/null >/tmp/relayaddr.lock; }; then trap "rm -f /tmp/relayaddr.lock" EXIT else echo "Another instance is already running. Exiting." exit 1 fi # retrieve current relay ip address OLD_RELAY_IP="$(uci get network.RELAY.ipaddr)" # retrieve current relay interface OLD_RELAY_INTERFACE="$(uci get network.RELAY.network | awk '{print $2}')" # update 2.4ghz wifi tether WIFI_2_INTERFACE="wlan1" WIFI_2_IP="$(ip -f inet addr show $WIFI_2_INTERFACE 2>/dev/null | grep inet | awk '{print $2}' | cut -d "/" -f1)" if [ "$WIFI_2_IP" = "" ]; then echo "No 2.4GHz WiFi tether detected." else echo "2.4GHz WiFi tether with address $WIFI_2_IP found." # store relay interface ip address NEW_RELAY_IP="$WIFI_2_IP" # store relay network device NEW_RELAY_INTERFACE="WAN_WIFI_2" # store led to light up NEW_RELAY_LED="wlan2g" # store expected interface argument for relayd EXPECTED_INTERFACE="$WIFI_2_INTERFACE" fi # update 5ghz wifi tether WIFI_5_INTERFACE="wlan0" WIFI_5_IP="$(ip -f inet addr show $WIFI_5_INTERFACE 2>/dev/null | grep inet | awk '{print $2}' | cut -d "/" -f1)" if [ "$WIFI_5_IP" = "" ]; then echo "No 5GHz WiFi tether detected." else echo "5GHz WiFi tether with address $WIFI_5_IP found." # store relay interface ip address NEW_RELAY_IP="$WIFI_5_IP" # store relay network device NEW_RELAY_INTERFACE="WAN_WIFI_5" # store led to light up NEW_RELAY_LED="wlan5g" # store expected interface argument for relayd EXPECTED_INTERFACE="$WIFI_5_INTERFACE" fi # update android usb tether USB_ANDROID_INTERFACE="usb0" USB_ANDROID_IP="$(ip -f inet addr show $USB_ANDROID_INTERFACE 2>/dev/null | grep inet | awk '{print $2}' | cut -d "/" -f1)" if [ "$USB_ANDROID_IP" = "" ]; then echo "No USB Android tether detected." else echo "USB Android tether with address $USB_ANDROID_IP found." # store new relay interface ip address NEW_RELAY_IP="$USB_ANDROID_IP" # store new relay network device NEW_RELAY_INTERFACE="WAN_ANDROID_USB" # store led to light up USB_PORT="$(dmesg | grep "usb0: register 'rndis_host'" | tail -n1 | sed -e 's/^.*rndis_host\(.*\)usb0\(.*\)/\1/' | cut -d"-" -f1 | awk '{$1=$1};1')" NEW_RELAY_LED="usb$USB_PORT" # store expected interface argument for relayd EXPECTED_INTERFACE="$USB_ANDROID_INTERFACE" fi # update iphone usb tether USB_IPHONE_INTERFACE="eth2" USB_IPHONE_IP="$(ip -f inet addr show $USB_IPHONE_INTERFACE 2>/dev/null | grep inet | awk '{print $2}' | cut -d "/" -f1)" if [ "$USB_IPHONE_IP" = "" ]; then echo "No USB iPhone tether detected." else echo "USB iPhone tether with address $USB_IPHONE_IP found." # store new relay interface ip address NEW_RELAY_IP="$USB_IPHONE_IP" # store new relay network device NEW_RELAY_INTERFACE="WAN_IPHONE_USB" # store led to light up USB_PORT="$(dmesg | grep "usb0: register 'rndis_host'" | tail -n1 | sed -e 's/^.*rndis_host\(.*\)usb0\(.*\)/\1/' | cut -d"-" -f1 | awk '{$1=$1};1')" NEW_RELAY_LED="usb$USB_PORT" # store expected interface argument for relayd EXPECTED_INTERFACE="$USB_IPHONE_INTERFACE" fi # check if ip has changed if [ "$NEW_RELAY_IP" = "$OLD_RELAY_IP" ]; then echo "IP hasn't changed. Skipping address update." else echo "IP has changed. Updating to new address of $NEW_RELAY_IP." # set new relay interface ip address uci set network.RELAY.ipaddr="$NEW_RELAY_IP" # queue changes SHOULD_COMMIT="true" fi # check if interface has changed if [ "$NEW_RELAY_INTERFACE" = "$OLD_RELAY_INTERFACE" ]; then echo "Interface hasn't changed. Skipping interface update." else echo "Interface has changed. Updating to new interface $NEW_RELAY_INTERFACE." # set new relay interface ip address uci set network.RELAY.network="LAN $NEW_RELAY_INTERFACE" # queue changes SHOULD_COMMIT="true" fi # update status leds if needed CURRENT_LED_STATUS="$(cat /sys/class/leds/tp-link:green:"$NEW_RELAY_LED"/brightness)" if ! [ "$CURRENT_LED_STATUS" = "255" ]; then # turn off all inactive uplink leds for led in usb1 usb2 wlan2g wlan5g; do if ! [ "$led" = "$NEW_RELAY_LED" ]; then echo '0' | tee /sys/class/leds/tp-link:green:"$led"/brightness >/dev/null fi done # turn on the led for the active uplink echo '255' | tee /sys/class/leds/tp-link:green:"$NEW_RELAY_LED"/brightness >/dev/null # queue changes SHOULD_COMMIT="true" fi # commit changes if needed if [ "$SHOULD_COMMIT" = "true" ]; then # commit changes to interface and/or ip address uci commit # queue relayd restart SHOULD_RESTART="true" fi # if relayd is using the wrong interface, queue a restart if ! ps | grep "/usr/sbin/relayd -I br-LAN -I $EXPECTED_INTERFACE" 2>&1 >/dev/null; then echo "Wrong interface selected in relayd. Restarting process." # queue relayd restart SHOULD_RESTART="true" fi # restart relayd if needed if [ "$SHOULD_RESTART" = "true" ]; then # restart relayd to apply new interface and/or ip address /etc/init.d/relayd restart fi EOL echo -e "End /etc/init.d/relayaddr script.\n" # add root crontab to run the relayd address update script and connectivity test every minute echo -e "\nBegin root crontab." cat <<'EOL' | tee "$workdir"/files/etc/crontabs/root * * * * * /etc/init.d/relayaddr EOL echo -e "End root crontab.\n" # create usbmuxd launch script for iphone tether detection echo -e "\nBegin /etc/init.d/usbmux script." cat <<'EOL' | tee "$workdir"/files/etc/init.d/usbmux #!/bin/sh /etc/rc.common START=30 STOP=60 start() { /usr/sbin/usbmuxd -v } stop() { kill -9 "$(pgrep usbmuxd)" } EOL echo -e "End /etc/init.d/usbmux script.\n" # add uci-defaults script to enable usbmuxd service echo -e "\nBegin usbmux service enable." cat <<'EOL' | tee "$workdir"/files/etc/uci-defaults/usbmux #!/bin/sh /etc/init.d/usbmux enable EOL echo -e "End usbmux service enable.\n" # add hotplug event triggering relayd update echo -e "\nBegin relayaddr hotplug event." cat <<'EOL' | tee "$workdir"/files/etc/hotplug.d/iface/01-relayaddr # update relay interfaces /etc/init.d/relayaddr EOL echo -e "End relayaddr hotplug event.\n" # add html form for updating ssid and psk echo -e "\nBegin /www/wifi.html." cat <<'EOL' | tee "$workdir"/files/www/wifi.html

Scan for nearby SSIDs

Band:

Update WiFi credentials

Band:
SSID:
PSK: 
EOL echo -e "End /www/wifi.html.\n" # add php script for updating ssid and password echo -e "\nBegin /www/wifi.php." cat <<'EOL' | tee "$workdir"/files/www/wifi.php 34) { exit("SSID is longer than 32 characters. Not updating."); } // if ssid contains a comma, error out if (strpos($ssid, ',') !== false) { exit("SSID contains invalid characters. Not updating."); } // if psk contains spaces or non-printable characters, error out if (!ctype_graph($psk)) { exit("PSK contains invalid characters. Not updating."); } // if psk is empty, shorter than 8 characters, or longer than 63, error out if (strlen($psk) == 2) { exit("No PSK provided. Not updating."); } elseif (strlen($psk) < 10) { exit("PSK is shorter than 8 characters. Not updating."); } elseif (strlen($psk) > 65) { exit("PSK is longer than 63 characters. Not updating."); } // if psk contains a comma, error out if (strpos($psk, ',') !== false) { exit("PSK contains invalid characters. Not updating."); } // select which band to modify if ("$band" == "5") { $wifinet = "wifinet0"; $wifiradio = "radio0"; } elseif ("$band" == "2.4") { $wifinet = "wifinet1"; $wifiradio = "radio1"; } else { exit("No valid wireless band provided. Not updating."); } // commit changes shell_exec('uci set wireless.'.$wifinet.'.ssid='.$ssid); shell_exec('uci set wireless.'.$wifinet.'.key='.$psk); shell_exec('uci commit'); // restart radio shell_exec('wifi down '.$wifiradio); shell_exec('wifi up '.$wifiradio); // report success echo "Submitted changes successfully."; ?>

Return to Main Page EOL echo -e "End /www/wifi.php.\n" # add php script for scanning nearby ssids echo -e "\nBegin /www/scan.php." cat <<'EOL' | tee "$workdir"/files/www/scan.php $ssid";done'); echo "Nearby ".$band."GHz SSIDs:"; echo "
$ssids
"; ?>
Return to Main Page EOL echo -e "End /www/scan.php.\n" # finished writing config files echo -e "Config files written.\n" # fix permissions echo -e "\nSetting permissions for all modified directories..." chmod 0755 "$workdir"/files/etc/rc.button/rfkill chmod 0755 "$workdir"/files/etc/rc.button/wps chmod 0755 "$workdir"/files/etc/init.d/relayaddr chmod 0755 "$workdir"/files/etc/init.d/usbmux chmod 0755 "$workdir"/files/etc/uci-defaults/usbmux echo -e "Permissions set.\n" # build the image; the output will be located in $basedir/bin/targets/$target/generic/ # - image for web ui upgrade: "$HOME"/"$hostname"/factory.bin # - image for cli upgrade: "$HOME"/"$hostname"/sysupgrade.bin echo -e "\nBuilding OpenWRT image..." cd "$workdir" && \ make image \ PROFILE="$profile" \ PACKAGES="luci-ssl php7 php7-cgi php7-mod-ctype qrencode vnstat apinger luci-app-vnstat iwinfo curl tcpdump ncat snmp-utils wireguard luci-app-wireguard mtr arp-scan wpad hostapd-utils relayd luci-proto-relay kmod-usb-net kmod-usb-net-cdc-ether kmod-usb-net-rndis kmod-usb-net-ipheth usbmuxd libimobiledevice usbutils -wpad-basic -wpad-mini -ppp -ppp-mod-pppoe -luci-proto-ppp" \ FILES=files && \ cp "$workdir"/bin/targets/"$target"/generic/openwrt-"$release"-"$target"-generic-"$profile"-squashfs-factory-us.bin "$basedir"/factory.bin && \ cp "$workdir"/bin/targets/"$target"/generic/openwrt-"$release"-"$target"-generic-"$profile"-squashfs-sysupgrade.bin "$basedir"/sysupgrade.bin # end function } 2>&1 | tee -a "$logfile" >/dev/null # append timestamp to logfile echo -e "Finished building OpenWRT image.\n\nBuild finished at $(date +%Y/%m/%d-%H:%M).\n" >> "$logfile" # display build completion status echo -e "Build complete!\n"