OPENWRT SURGERY

After a while when I didn't have ideas for new phlog posts, now I
have ideas and no time to write them. But I did finally get around
to setting up my final OpenWRT configuration for my home router, v.
23 within my router's 32MB of RAM much better than the default.
It's final because, as noted in my post 2025-01-09Hit_and_MIPS.txt
(oh dear, this took me six month to get back to!), OpenWRT 24 has
dropped the build option for router boards in my router's hardware
family, which at least gives me the excuse to hack deeper into the
works without fearing breakage after upgrades.

I'd like to write my usual long ramble, but since allowing time for
writing this post is under the excuse of it being notes for my own
later reference, I'll go into quick dot-point mode:

* Building with OpenWRT Image Builder on x86_64 VPS (because the
image builder is a huge download so I don't want to do that over my
home internet connection)

https://openwrt.org/docs/guide-user/additional-software/imagebuilder
 - Make directory named "files" in the root of the image builder
directory, pointed to with the FILES argument to make. In here go
all files to be added/changed compared with the default router file
system, with all their final directory paths and permissions (you
need root permissions to make them).
 - Instructions say not to run the Image Builder Makefile as root,
but if you want to include custom files that are only readable by
root, it will fail to read them unless you run it as root. It runs
fine as root.

* OpenWRT releases supported for six months after release of next
major version:
   https://openwrt.org/docs/guide-developer/security#support_status
 OpenWRT 23 therefore supported until July this year. Latest
release is 23.05.5.

* The OpenWRT Wiki page for my router pointed to the "SMP" images
(until it became officially unsupported after OpenWRT 19 due to
lack of RAM). This was wrong, it only has one processor so an SMP
kernel is bloat. I should have been using the image builder
configured without SMP support, called "generic":

https://downloads.openwrt.org/releases/23.05.5/targets/bcm63xx/generic/openwrt-imagebuilder-23.05.5-bcm63xx-generic.Linux-x86_64.tar.xz
 https://openwrt.org/docs/techref/targets/bcm63xx

* Openwrt comes with DHCPv6 via a separate DHCP server package. I'm
not using IPv6 so I don't need that and can just use the IPv4 DHCP
server in dnsmasq:
- Add "option dhcpv6 'disabled'" in /etc/config/dhcp.
- Also disable automatic IPv6 support for USB modem in
/etc/config/network
- Also comment-out IPv6 rules in /etc/config/firewall
 - "disable_ipv6" option is not supported since OpenWrt 22.
- Remove default packages odhcp6c, odhcpd-ipv6only, and odhcpd6c,
from image builder PACKAGES
- Disabled odhcpd in /etc/config/dhcp by setting in the odhcpd
section:
     option dhcpv6 'disabled'
     option ra 'disabled'

* Disable other unused default services
 - ntpd (NTP daemon)
  - Router doesn't have a RTC so this means it won't know the
correct time anymore (will count from the date of the current
OpenWRT version's release after booting), but I don't care.
  - Can be disabled in /etc/config/system or put "sysntpd" in
DISABLED_SERVICES for image builder.
 - urngd (Enthropy harvester daemon)
  - This might be important for encrypted WiFi, for which drivers
sometimes need a fresh enthropy source), but I'm not using that so
it gets the axe.
  - Remove default urngd package from image builder PACKAGES.
  - Put urngd in DISABLED_SERVICES for image builder.
 - logd (syslog daemon)
  - Networking wouldn't come up when I disabled this in the old
image, so I left it enabled at boot but put this at the start of
/etc/rc.local to kill logd at the end of the boot process:
    # Wait until firewall confguration is finished
    while killall -q -0 hotplug-call || killall -q -0 mac80211.sh || killall -q -0 fw4
    do
      sleep 5
    done

    # Kill syslog daemon
    killall logd
  - Use "/etc/init.d/log start" to start it after boot for
debugging purposes. Read syslog afterwards with the "logread"
command.

* Ditch Dropbear and SSH/SCP. Telnet and FTP have less overhead and
won't break when new clients demand newer server versions than the
last OpenWrt 23 dropbear package.
 - Remove "dropbear" package from image builder PACKAGES.
 - put "dropbear" in DISABLED_SERVICES for image builder.
 - put GNU Inetutils telnetd and inetd static binaries (see
2025-01-09Hit_and_MIPS.txt) in custom files path:
    /usr/bin/telnetd
    /usr/bin/inetd
 - Make symlinks:
    /usr/local/bin -> /bin
    /usr/local/var -> /var
 - Disable vsftpd service, since I'll use it in inetd mode which
takes up much less RAM, by putting vsftpd in DISABLED_SERVICES for
image builder.
 - Make /etc/inetd.conf:
    telnet stream tcp nowait root /usr/bin/telnetd telnetd
    ftp stream tcp nowait root /usr/sbin/vsftpd vsftpd
 - Set "listen=NO" in /etc/vsftpd.conf
 - Create this directory for vsftpd at start-up with this command
in /etc/rc.local:
    mkdir -m 0755 -p /var/run/vsftpd
   (/var/run is in tmpfs so can't put the directory in FILES)
 - Inetd now needs a custom init script to start it at boot, in
/etc/init.d/inetd:
     #!/bin/sh /etc/rc.common

     # start after and stop before networking
     START=19
     STOP=50

     start() {
       SERVICE_USE_PID=1 service_start /usr/bin/inetd /etc/inetd.conf
     }

     stop() {
       service_stop /usr/bin/inetd
     }

     reload() {
       service_reload /usr/bin/inetd
     }
   - Make the script executable and owned+writable by root only.
The symlinks to this script in /etc/rc.d are created by the image
builder.

* If the router does run out of RAM, processes get killed by the
kernel "OOM Reaper", but respawned by the (OpenWRT-specific) init
system. I tweaked the "respawn" settings to be more forgiving and
better suit my usage. Following docs here:

https://openwrt.org/docs/guide-developer/procd-init-scripts#service_parameters
 /etc/init.d/network - in start_service(): procd_set_param respawn 3600 20 0
  - This sets a 20sec wait between respawning if the network
process is killed, and
    retries indefinitely.
 /etc/init.d/dnsmasq - in dnsmasq_start(): procd_set_param respawn 3600 40 0
  - This sets a 40sec wait between respawning if the DNS server
process is killed, and
    retries indefinitely.
 /etc/init.d/log - in start_service_daemon(): #       procd_set_param respawn 5 1 -1
  - Disable respawning the log process if it is killed by
commenting out this line.

* Probably not an issue anymore, but before the above changes I had
trouble with running out of RAM at boot because the OpenWRT init
system starts all services in the background and therefore has
their init scripts all running in parallel. That's great for boot
speed (at least if I had multiple processors), but terrible for RAM
usage since there needs to be room for everything to run at once (a
really odd choice from the OpenWRT developers). Disabling dnsmasq
in DISABLED_SERVICES then launching it at the end of the boot
sequence before starting the mobile broadband modem avoids the
choke point, and can be achieved with this in /etc/rc.local (first
part already included under "Disable other unused default services"
above):
    # Wait until firewall confguration is finished
    while killall -q -0 hotplug-call || killall -q -0 mac80211.sh || killall -q -0 fw4
    do
      sleep 5
    done

    # Start these services at the end of the boot process
    # to avoid running out of RAM
    sleep 30
    /etc/init.d/dnsmasq start
    sleep 15
    ifup wan

    # Restart firewall if it was killed during ifup
    sleep 45
    /etc/init.d/firewall boot

* Finally, my idata.sh script (from 2024-03-10Off_and_On_Line.txt)
used SSH to run the data-collector script on the router. Looking at
the inetd docs, I discovered I could turn that into its own
protocol using TCPMUX and make requests using "nc" instead of
dropbear without changing the router-side script at all.
- Put these lines in /etc/inetd.conf:
  tcpmux stream tcp nowait root internal
  tcpmux/+idata stream tcp nowait root /root/send_idata.sh idata
- But all the best things in computing are obsolete, so I needed
to add the tcpmux protocol to the /etc/services file which is
missing it by default:
   tcpmux              1/tcp
- Now in idata.sh, instead of:
    # Grab data stats from router via SSH
    newdata="`DROPBEAR_PASSWORD=$ROUTER_PASSWORD dbclient $ROUTER_SSH $ROUTER_SCRIPT`"
  I've got:
    # Grab data stats from router via TCPMUX
    newdata="`echo idata | nc $ROUTER 1 | tail -n 1`"

* This is my final OpenWRT Image Builder build command. Note that
LuCI was never included in these builds by default (hence I've only
ever known OpenWRT configuration via SSH), so it doesn't need to be
excluded. I'm not sure if that's the case for other build targets:

make image FILES="files" PROFILE="brcm_bcm96358vw" PACKAGES="chat comgt hostapd-basic hostapd-common iw-full iwinfo kmod-b43 kmod-bcma kmod-cfg80211 kmod-crypto-hash kmod-mac80211 kmod-mac80211-hwsim kmod-nls-base kmod-usb2 kmod-usb-acm kmod-usb-core kmod-usb-ehci kmod-usb-ohci kmod-usb-serial kmod-usb-serial-option kmod-usb-serial-wwan kmod-usb-uhci libiwinfo librt libusb-1.0 terminfo usb-modeswitch vsftpd wireless-regdb wireless-tools coreutils-stty picocom kmod-usb-net-sierrawireless kmod-usb-serial-sierrawireless kmod-mii -odhcpd-ipv6only -odhcpd6c -odhcp6c -urng -dropbear" DISABLED_SERVICES="urngd dnsmasq sysntpd dropbear vsftpd"
- Note that the "kmod-" packages are specific to my router
hardware. No longer needing to figure out when these change
(without documentation of the changes) is another big win from not
upgrading to new major OpenWRT versions anymore.

* Still takes me a few tries to flash the firmware via TFTP (via
serial console command). Dunno why the connection always seems to
fail the first few times, but it works eventually.

The stats for this new image compared to the previous default SMP
one which I'd tried to optimise after installation are significant.

Specs: 32MB RAM, 8MB Flash

Old OpenWRT 23 (SMP) installation:
 Total RAM (minus kernel size):        23504KB
 RAM available after boot:             4148KB
 Flash free after boot:                568KB

New OpenWRT 23 (non-SMP) installation:
 Total RAM (minus kernel size):        24712KB
 RAM available after boot:             9512KB
 Flash free after boot:                1960KB

But most important, it now starts up in a reasonable time again (it
was taking over 5min before) and no longer has random crashes when
the mobile broadband modem signal drops out. Before it would run
out of RAM while trying to reconnect, sometimes failing, or killing
other important services in the process. Also the router I thought
was broken when I first installed OpenWRT 23 on it is actually
fine, it was just an early case of those issues causing the
firewall process to be killed which made it look like the network
interface was intermittent. It has been running solidly for days
since installing this lightweight build.

Oh and typically now that I've finally discovered the RAM/storage
advantage of the Linux kernel without SMP support, the kernel
developers are talking about removing that option:
https://www.phoronix.com/news/Linux-6.17-Maybe-SMP-Uncond

Linux seems to be looking less attractive lately, clearly aiming to
serve a different user-base than me. Also see this tally of how the
size of OpenWrt has increased over the years:
https://openwrt.org/supported_devices/432_warning#analysis_of_firmware_size_growth

I wonder if a BSD-based router OS would have done better, such as
ZRouter ( https://zrouter.org - looks dead)? Although drivers would
be a roadblock with that anyway. Anyway I don't think there's any
good reason that a basic router now needs better specs than these
ones have. Plus newer basic ISP-supplied models that are common
second-hand in Australia have little-better specs and never got any
sort of OpenWRT support to begin with.

- The Free Thinker