Files
guix-router/install-mt6000.sh

143 lines
6.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Install Guix (this project's release) on a GL.iNet GL-MT6000 that still has
# GL's stock U-Boot -- from stock firmware, OpenWrt, or an earlier install.
#
# sudo ./install-mt6000.sh IFACE [RELEASE-DIR]
#
# IFACE: this computer's Ethernet port, cabled to one of the router's LAN
# ports (not the 2.5G WAN port). RELEASE-DIR: where the release files are
# (default: this script's directory).
#
# What happens (docs: INSTALL.md, PITFALLS.md #51):
# 1. IFACE gets 192.168.1.2/24; a TFTP server offers OpenWrt's official
# initramfs (downloaded, checksum-verified) the way GL's U-Boot asks for
# it -- about 3 s after every power-on it fetches openwrt-gl-mt6000.bin
# from 192.168.1.2. You power-cycle the router.
# 2. OpenWrt runs from RAM at 192.168.1.1; over SSH this writes the Guix
# root file system to the `rootfs' partition (p7) and the boot image to
# the `kernel' partition (p6), and checks both against the release.
# 3. The router reboots into Guix: the wired ports are then the private
# network, 192.168.2.1 (DHCP); IFACE goes back to DHCP for it.
# Everything on p6 and p7 is replaced (the old system, its settings). The
# `factory' partition (MAC addresses, WiFi calibration) and the bootloader
# are not touched.
set -euo pipefail
IFACE=${1:?usage: sudo $0 IFACE [RELEASE-DIR]}
REL=$(realpath "${2:-$(dirname "$0")}")
[ "$(id -u)" = 0 ] || { echo "run as root (sudo)" >&2; exit 1; }
OPENWRT_URL=https://downloads.openwrt.org/releases/25.12.5/targets/mediatek/filogic/openwrt-25.12.5-mediatek-filogic-glinet_gl-mt6000-initramfs-kernel.bin
OPENWRT_SHA256=d4a86e33260f6fc3e4950438647455e7675e5c6f74fc7e00025255674f8c1a39
ROOT=$REL/guix-router-mt6000-root.ext4.zst
FIT=$REL/guix-router-mt6000-boot.itb
ROUTER=192.168.1.1
HOST=192.168.1.2
need() { command -v "$1" >/dev/null || { echo "missing: $1" >&2; exit 1; }; }
for c in dnsmasq ssh curl zstd sha256sum ip nc setsid; do need "$c"; done
for f in "$ROOT" "$FIT" "$REL/SHA256SUMS"; do
[ -f "$f" ] || { echo "missing release file: $f" >&2; exit 1; }
done
ip link show "$IFACE" >/dev/null
echo "== checking the release files"
(cd "$REL" && sha256sum -c --ignore-missing SHA256SUMS) | sed 's/^/ /'
root_sha=$(sed -n 's/^\([0-9a-f]\{64\}\) .*guix-router-mt6000-root.ext4$/\1/p' "$REL/SHA256SUMS.uncompressed")
root_size=$(sed -n 's/^\([0-9]*\) guix-router-mt6000-root.ext4$/\1/p' "$REL/SIZES.uncompressed")
[ -n "$root_sha" ] && [ -n "$root_size" ] || { echo "no uncompressed root checksum/size in the release" >&2; exit 1; }
work=$(mktemp -d)
nm_was_managed=
cleanup() {
[ -f "$work/dnsmasq.pid" ] && kill "$(cat "$work/dnsmasq.pid")" 2>/dev/null || true
ip addr del $HOST/24 dev "$IFACE" 2>/dev/null || true
if [ -n "$nm_was_managed" ]; then
nmcli device set "$IFACE" managed yes 2>/dev/null || true
fi
rm -rf "$work"
}
trap cleanup EXIT
echo "== OpenWrt initramfs (to run the installation from RAM)"
cache=${XDG_CACHE_HOME:-/var/cache}/guix-router; mkdir -p "$cache"
initramfs=$cache/$(basename "$OPENWRT_URL")
if [ ! -f "$initramfs" ]; then
curl -fL -o "$initramfs.part" "$OPENWRT_URL"
mv "$initramfs.part" "$initramfs"
fi
echo "$OPENWRT_SHA256 $initramfs" | sha256sum -c --quiet || { rm -f "$initramfs"; echo "bad download" >&2; exit 1; }
# GL's U-Boot applies only sysupgrade tars with a root: OpenWrt's initramfs
# as `kernel' (written to p6 and booted) and a dummy 1 MiB `root' (p7, which
# gets the real root next anyway).
mkdir -p "$work/tar/sysupgrade-glinet_gl-mt6000" "$work/tftp"
printf 'BOARD=glinet_gl-mt6000\n' > "$work/tar/sysupgrade-glinet_gl-mt6000/CONTROL"
cp "$initramfs" "$work/tar/sysupgrade-glinet_gl-mt6000/kernel"
head -c 1048576 /dev/zero > "$work/tar/sysupgrade-glinet_gl-mt6000/root"
tar --owner=root --group=root -C "$work/tar" -cf "$work/tftp/openwrt-gl-mt6000.bin" \
sysupgrade-glinet_gl-mt6000/CONTROL sysupgrade-glinet_gl-mt6000/kernel \
sysupgrade-glinet_gl-mt6000/root
echo "== $IFACE: $HOST/24"
if command -v nmcli >/dev/null && nmcli -t -f DEVICE,STATE device | grep -q "^$IFACE:[^u]"; then
nmcli device set "$IFACE" managed no; nm_was_managed=1
fi
ip link set "$IFACE" up
ip addr add $HOST/24 dev "$IFACE" 2>/dev/null || true
dnsmasq --keep-in-foreground --log-facility="$work/tftp.log" --user=root --port=0 \
--enable-tftp --tftp-root="$work/tftp" --interface="$IFACE" --bind-interfaces \
--conf-file=/dev/null --pid-file="$work/dnsmasq.pid" &
sleep 1
echo
echo ">> Now power-cycle the router (unplug, plug back in). Waiting..."
until grep -q 'sent .*openwrt-gl-mt6000.bin' "$work/tftp.log" 2>/dev/null; do sleep 1; done
echo "== U-Boot fetched the image; OpenWrt is starting from RAM"
kill "$(cat "$work/dnsmasq.pid")"; rm -f "$work/dnsmasq.pid"
# Fresh OpenWrt: root, empty password.
printf '#!/bin/sh\necho\n' > "$work/askpass"; chmod +x "$work/askpass"
ow() {
SSH_ASKPASS="$work/askpass" SSH_ASKPASS_REQUIRE=force setsid -w ssh \
-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
-o LogLevel=ERROR -o PubkeyAuthentication=no -o ConnectTimeout=5 \
root@$ROUTER "$@"
}
for i in $(seq 1 60); do nc -z -w2 $ROUTER 22 2>/dev/null && break; sleep 2; done
sleep 3
[ "$(ow 'grep -c mmcblk /proc/mounts' </dev/null)" = 0 ] ||
{ echo "192.168.1.1 is not OpenWrt running from RAM; stopping" >&2; exit 1; }
ow 'grep -q glinet,gl-mt6000 /tmp/sysinfo/board_name 2>/dev/null || grep -q "GL-MT6000" /proc/device-tree/model' </dev/null ||
{ echo "not a GL-MT6000; stopping" >&2; exit 1; }
echo "== writing the root file system (p7, $((root_size / 1048576)) MiB)"
zstd -dc "$ROOT" | ow 'dd of=/dev/mmcblk0p7 bs=1M 2>/dev/null; sync'
got=$(ow "echo 3 > /proc/sys/vm/drop_caches; head -c $root_size /dev/mmcblk0p7 | sha256sum" </dev/null | cut -c1-64)
[ "$got" = "$root_sha" ] || { echo "root check FAILED ($got): run the installer again" >&2; exit 1; }
echo " verified"
echo "== writing the boot image (p6)"
ow 'dd of=/dev/mmcblk0p6 bs=64k conv=fsync 2>/dev/null' < "$FIT"
got=$(ow "head -c $(stat -c %s "$FIT") /dev/mmcblk0p6 | sha256sum" </dev/null | cut -c1-64)
[ "$got" = "$(sha256sum < "$FIT" | cut -c1-64)" ] ||
{ echo "boot image check FAILED: run the installer again" >&2; exit 1; }
echo " verified"
echo "== rebooting into Guix"
ow 'sync; reboot' </dev/null || true
# Guix: wired ports = private network, 192.168.2.1, DHCP.
ip addr del $HOST/24 dev "$IFACE" 2>/dev/null || true
if [ -n "$nm_was_managed" ]; then
nmcli device set "$IFACE" managed yes; nm_was_managed=
echo " $IFACE is back under NetworkManager (DHCP)"
else
echo " give $IFACE an address by DHCP (or 192.168.2.2/24) to reach the router"
fi
echo
echo "Done. The first boot reboots once by itself (WiFi and TollGate setup);"
echo "about two minutes from now: ssh root@192.168.2.1"
echo "(with the key built into the release), LuCI at http://192.168.2.1:8080/."
echo "See INSTALL.md for the passwords to change and the WiFi networks."