Some notes on OpenWrt

What is OpenWrt?

I pronounce it as "Open Wart" but I digress and the notes haven't even begun. It is a Linux(-based), open source operating system for embedded devices but it's most use if found in your routers. If you want your router to have an open source and free operating system, this is a decent bet.

To download firmware for your device/architecture go to OpenWrt Firmware download page and try to find your device.

Architectures and devices

OpenWrt is open to adding new devices. Check how to add new device page for details. If you want to go that route, maybe check if your device is already supported by going to "Table of Hardware" support page. This is also available as an archive to download and grep locally.

Packages

OpenWrt also has a package ecosystem. So, you can package your own application for the OS. In fact, the UI (Web based) called Luci is a package itself that you can install once you have the OS up and running. The packages are divided into many categories and creating a new package means you should choose one of those categories and likely sub-categories.

You can install packages using the opkg installation tool from the commandline. You will need to SSH into the router/device after installing the OS.

Rust and OpenWrt

This is new! Starting OpenWrt version 23.05 you have native Rust package support. This can make life easier if you want to create a simple Rust package for OpenWrt.

For me, my application was far more complex than what the OpenWrt Rust support toolchain can handle right now because we bundle some web artifacts etc. So, in my case it was easier to find the architecture and compile my binary for that using Nix.

You can find a few things by running these commands for your router -

uname -a

Shows you system information in brief. For example,

root@OpenWrt:~# uname -a
Linux OpenWrt 5.14.171 # 21:05:12 2023 armv7l GNU/Linux

lscpu

This will show you architecture in detail. For example,

root@OpenWrt:~# lscpu
Architecture:           armv7l
  Byte Order:           Little Endian
CPU(s):                 2
  On-line CPU(s) list:  0,1
Vendor ID:              ARM
  Model name:           Cortex-A9
    Model:              1
    Thread(s) per core: 1
    Core(s) per socket: 2
    Socket(s):          1

The important part is that you can also get the Model name (Cortex-A9) which maybe needed to choose the right architecture.

ldd --version

This will tell you which base library your system is using. For example, the following uses musl,

root@OpenWrt:~# ldd --version
musl libc (armhf)
Version 1.2.3
Dynamic Program Loader
Usage: ldd [options] [--] pathname

Create a package for OpenWrt

I should begin by saying that in this exercise, you need a lot of GNU Make knowledge. The hell of Make will be enough for you to say no to creating OpenWrt package. However, let's look at a few things I learned during this "simple" exercise.

Easy way out is to build binary yourself

If you know the architectures you want to support and can publish the package yourself, you can avoid the entire drama and just host things yourself. Honestly, it was far easier to do that especially if you have a complex build process. Which brings the next point.

Quick before the next point, the .ipk file is just a tar.gz. So, you can unpack some existing package and see what kind of files are inside. It kinda feels like debian based structure but I could be wrong.

The packaging process is a nightmare

You can try and follow their tutorial for packaging on their Wiki.

If you want to package it using their Make stuff, you will end up having more architectural support but it is a nightmare and not even like the Halloween kind where you get the candy.

No really, look at this makefile -

include $(TOPDIR)/rules.mk
 
PKG_NAME:=bridge
PKG_VERSION:=1.0.6
PKG_RELEASE:=1
 
PKG_BUILD_DIR:=$(BUILD_DIR)/bridge-utils-$(PKG_VERSION)
PKG_SOURCE:=bridge-utils-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=@SF/bridge
PKG_HASH:=9b7dc52656f5cbec846a7ba3299f73bd
 
include $(INCLUDE_DIR)/package.mk
 
define Package/bridge
  SECTION:=base
  CATEGORY:=Network
  TITLE:=Ethernet bridging configuration utility
  #DESCRIPTION:=This variable is obsolete. use the Package/name/description define instead!
  URL:=http://bridge.sourceforge.net/
endef
 
define Package/bridge/description
 Ethernet bridging configuration utility
 Manage ethernet bridging; a way to connect networks together to
 form a larger network.
endef
 
define Build/Configure
  $(call Build/Configure/Default,--with-linux-headers=$(LINUX_DIR))
endef
 
define Package/bridge/install
        $(INSTALL_DIR) $(1)/usr/sbin
        $(INSTALL_BIN) $(PKG_BUILD_DIR)/brctl/brctl $(1)/usr/sbin/
endef
 
$(eval $(call BuildPackage,bridge))

This reminds me of the CMake hell of NCBI C++ Toolkit. You can see there is a configuration file to configure CMake first.