OpenWRT

From Segfault
Jump to navigation Jump to search

Installation

Please refer to the hardware model on how to install OpenWRT.

Trunk

If, for some reason we have to install from trunk, we may have to install some LuCI modules:

#!/bin/sh
# Required: grep, wget, ssh, and scp, signify

# Adjust if needed
user="root"
ip_address="192.168.1.1"

if [ $# -ne 1 ]; then
    echo "Usage: $0 [arch]"
    exit 1
else
    architecture="$1"
fi

url="https://downloads.openwrt.org/snapshots/trunk/${architecture}/generic/packages/"
tmpdir="/tmp/luci-offline"
packages_base="liblua lua libuci-lua libubus libubus-lua uhttpd rpcd"
packages_luci="luci-base luci-lib-ip luci-lib-nixio luci-theme-bootstrap luci-mod-admin-full luci-lib-jsonc"

mkdir -p "$tmpdir" && cd "$tmpdir"

echo "### Getting base/packages..."
wget -Nq "${url}base/Packages"
for pkg in $packages_base; do
    pkgfile="$(grep -Eoe " ${pkg}_.+" Packages | tail -c +2)"
    pkgurl="${url}base/${pkgfile}"
    wget -Nq "$pkgurl"
done

echo "### Getting luci/packages..."
wget -Nq "${url}luci/Packages"
for pkg in $packages_luci; do
    pkgfile="$(grep -Eoe " ${pkg}_.+" Packages | tail -c +2)"
    pkgurl="${url}luci/${pkgfile}"
    wget -Nq "$pkgurl"
done

echo "### Getting luci..."
wget -Nq "${url}luci"
for pkg in $packages_luci; do
    pkgfile="$(grep -Eoe " ${pkg}_.+" Packages | tail -c +2)"
    pkgurl="${url}luci/${pkgfile}"
    wget -Nq "$pkgurl"
done

scp -r "$tmpdir" "${user}@${ip_address}":/tmp/
ssh "${user}@${ip_address}" opkg install /tmp/luci-offline-packages/*.ipk
ssh "${user}@${ip_address}" rm -rf /tmp/luci-offline-packages/

ssh "${user}@${ip_address}" "/etc/init.d/uhttpd start; /etc/init.d/uhttpd enable"

Some devices require special drivers and firmware too:

opkg install ath10k-firmware-qca988x kmod-ath10k

Being trunk, some packages may not work properly yet:

Configuration

Packages

Install some essential packages:

opkg install ca-certificates curl diffutils dnscrypt-proxy haveged iftop iperf nrpe rsync script-utils snmpd vnstat

DNS

WAN interface

For some reason, dnsmasq is listening on the WAN interface[1] too, answering DNS lookups to the entire internet and even gives out internal records. Instead of creating a firewall rule[2] we just disable dnsmasq on the WAN interface:

$ grep ^interface /etc/dnsmasq.conf 
interface=lo
interface=br-lan

Or, in /etc/config/dhcp:

config dnsmasq
       [...]
       option interface 'lo br-lan'

Restart DNSmasq:

$ /etc/init.d/dnsmasq restart

With that in place, dnsmasq only listens on loopback and the local network interface.

CNAME

The documentation states that it is possible to add a CNAME resource record:

$ grep -A2 cname /etc/config/dhcp 
config 'cname'
       option cname 'mail'
       option target 'server'

However, as of 10.03.1 this feature does not seem to be implemented. Patching the initscript helps:

--- /etc/init.d/dnsmasq.orig    Wed Nov 23 17:13:22 2011
+++ /etc/init.d/dnsmasq Sun Jul 15 16:43:44 2012
@@ -346,6 +346,19 @@
        done
 }
 
+dhcp_cname_add() {
+       local cfg="$1"
+       local cname target
+
+       config_get cname "$cfg" cname
+       [ -n "$cname" ] || return 0
+
+       config_get target "$cfg" target
+       [ -n "$target" ] || return 0
+
+       append args "--cname=${cname},${target}"
+}
+
 start() {
        include /lib/network
        scan_interfaces
@@ -367,6 +380,7 @@
        config_foreach dhcp_subscrid_add subscrid
        config_foreach dhcp_domain_add domain
        config_foreach dhcp_add dhcp
+       config_foreach dhcp_cname_add cname
 
        # add own hostname
        [ -z "$lanaddr" ] || {

AAAA Records

With nowhere else to put this right now, this is a neat trick[3] to have dnsmasq only ask for A records:

server=/example.net/1.2.3.4
address=/example.net/::

DNSCrypt

DNSCrypt secures the client-server communication for DNS lookups. For trunk, installation is quite straightforward:

opkg update
opkg install dnscrypt-proxy

Configure /etc/config/dnscrypt-proxy like this:

config dnscrypt-proxy ns1
       option address '127.0.0.1'
       option port '5353'
       option resolver 'fvz-anyone'
       # option resolvers_list '/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv'
       option ephemeral_keys '1'
  • The resolvers_list is hosted in a Github repository and should already be in place.
  • The OpenNIC project also lists public DNS servers, some with DNSCrypt enabled.
  • The resolver is the name of a group of public DNS servers in the resolvers_list. One group per dnscrypt-proxy instance can be specified.
$ cut -d, -f1 /usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv | cut -d- -f1 | sort | uniq -c | sort -n | tail -5
     4 adguard
     4 cisco
     4 fvz
    12 cs
    35 d0wn

Now that dnscrypt-proxy is configured, we need to configure dnsmasq too. In /etc/config/dhcp, change the following:

config dnsmasq
       option domainneeded '1'
       option boguspriv '1'
       option localise_queries '1'
       option rebind_protection '1'
       option rebind_localhost '1'
       option expandhosts '1'
       option authoritative '1'
       option readethers '1'
       option leasefile '/tmp/dhcp.leases'
#      option resolvfile '/tmp/resolv.conf.auto'
       option noresolv '1'
       option localservice '1'
       option local '/lan/'
       option domain 'lan'
       option nonwildcard '1'
       list server '127.0.0.1#5353'
       list server '/pool.ntp.org/208.67.222.222'
       list interface 'br-lan'
       list notinterface 'eth0'

DNS over TLS

Install stubby:

opkg install stubby

In /etc/config/dhcp:

config dnsmasq
  [...]
  list server '127.0.0.1#5453'
  list server '0::1#5453'

Enable & start:

/etc/init.d/stubby enable
/etc/init.d/stubby start
/etc/init.d/dnsmasq restart

Adding other DNS over TLS providers[4] in /etc/config/stubby:

config resolver
      option address '9.9.9.9'
      option tls_auth_name 'dns.quad9.net'

config resolver
      option address '149.112.112.112'
      option tls_auth_name 'dns.quad9.net'
[...]

Dynamic DNS

The DDNS Client configuration changed quite a bit over the last OpenWRT versions, the following should be valid for OpenWRT 15.05.1.

Install the DDNS client and the LuCI frontend:

opkg install ddns-scripts luci-app-ddns

Configure our DDNS service:

uci set ddns.myddns_ipv4.service_name='freedns.afraid.org'     # Must match a name from /usr/lib/ddns/services
uci set ddns.myddns_ipv4.use_https='1'
uci set ddns.myddns_ipv4.domain='foo.example.org'
uci set ddns.myddns_ipv4.username='joe'
uci set ddns.myddns_ipv4.password='s3cr3t'
uci set ddns.myddns_ipv4.check_interval='10'
uci set ddns.myddns_ipv4.check_unit='hours'
uci set ddns.myddns_ipv4.enabled='1'

uci set ddns.myddns_ipv6.service_name='freedns.afraid.org'     # Must match a name from /usr/lib/ddns/services
uci set ddns.myddns_ipv6.use_https='1'
uci set ddns.myddns_ipv6.domain='foo.example.org'
uci set ddns.myddns_ipv6.username='joe'
uci set ddns.myddns_ipv6.password='s3cr3t'
uci set ddns.myddns_ipv6.check_interval='10'
uci set ddns.myddns_ipv6.check_unit='hours'
uci set ddns.myddns_ipv6.enabled='1'
uci commit ddns

With the last command, /etc/config/ddns will be written. The DDNS daemon will be started with the next reboot or can be started as such:

sh
. /usr/lib/ddns/dynamic_dns_functions.sh
start_daemon_for_all_ddns_sections "wan"
exit

The wan parameter is the ip_network value in /etc/config/ddns. Let's see if this worked:

$ nslookup foo.dyndns.org ns.dyndns.org
Server:    204.13.248.75
Address 1: 204.13.248.75 ns1.dyndns.org
 
Name:      foo.dyndns.org
Address 1: 61.161.53.31 pppoe321.isp.example.com

DuckDNS

TBD

FreeDNS

In earlier OpenWRT versions, /usr/lib/ddns/services had to be tweaked for afraid.org hosts:[5][6]

"freedns.afraid.org"  "http://freedns.afraid.org/dynamic/update.php?[PASSWORD]&address=[IP]"

DynDNS

dyndns.org supported[7][8] options like wildcard DNS or MX records[9], but this can only be set online and the OpenWrt DynDNS client must be tought to handle these settings. This can only be done via tweaking /usr/lib/ddns/services:

"dyndns.org" "http://[USERNAME]:[PASSWORD]@members.dyndns.org/nic/update?hostname=[DOMAIN]&myip=[IP]&wildcard=ON&mx=NOCHG"

Ad-Blocker

YoYo

We use dnsmasq to block adservers and use a blocklist from yoyo.org:

$ wget -O /etc/dnsmasq.adservers "http://pgl.yoyo.org/adservers/serverlist.php?hostformat=dnsmasq&mimetype=plaintext"

$ tail -3 /etc/dnsmasq.conf
bind-interfaces
conf-file=/etc/dnsmasq.adservers         # Adjust the target address as needed, e.g.
conf-file=/etc/dnsmasq.work              # address=/.corp/10.10.0.30 to blackhole all .corp

$ /etc/init.d/dnsmasq restart

Add a cronjob to update the blocklists

$ crontab -l
0   23 * * 1 /root/bin/dl-adservers.sh restart

Pi-hole

The Pi-hole™ distribution uses several blocklists to generate a single list[10][11], and we could do the same:

TBD

Guest WLAN

TBD

NRPE

With OpenWRT 15.05, nrpe appears to be unmaintained[12]. We should built the package manually[13]. In the meantime, we have just copied the package from our backups:

/usr/lib/opkg/info/nrpe.list
/usr/lib/opkg/info/nagios-plugins.list

Example:

cd ~/backup/router/ && find . -name "*nrpe*" -o -name "check_*" | xargs tar -cf - ) | \
   ssh router "tar -C / -xvf -"

Almost there:

$ ldd /usr/sbin/nrpe                                           # Note: ldd is used to be a shell function!
                                                               # LD_TRACE_LOADED_OBJECTS=1
       libssl.so.1.0.0 => not found
       libcrypto.so.1.0.0 => not found
       libwrap.so.0 => not found
       libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x77a64000)
       libc.so.0 => /lib/libc.so.0 (0x779f7000)
       ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x77a88000)

Install dependencies:

opkg install libopenssl libwrap

Add nagios user:

echo 'nagios:x:50:' >> /etc/group
echo 'nagios:x:50:50:nagios:/var/run/nagios:/bin/false' >> /etc/passwd
echo 'nagios::16874:0:99999:7:::' >> /etc/shadow

Configure nrpe:

$ grep ^[a-z] /etc/nrpe.cfg
pid_file=/var/run/nrpe.pid
server_port=5666
server_address=192.168.0.2
nrpe_user=nagios
nrpe_group=nagios
allowed_hosts=192.168.0.106,192.168.0.116
dont_blame_nrpe=0
debug=0
command_timeout=60
connection_timeout=300

command[check_dummy]=/usr/libexec/nagios/check_dummy 0
command[check_dns]=/usr/libexec/nagios/check_dns -H ping.example.com -s localhost -w 0.1 -c 0.5
command[check_entropy]=/root/bin/check_entropy.sh -w 1024 -c 512
command[check_http]=/usr/libexec/nagios/check_http -H localhost -w 0.1 -c 0.5
command[check_load]=/usr/libexec/nagios/check_load -w 4,3,2 -c 5,4,3
command[check_ntp_time]=/usr/libexec/nagios/check_ntp_time -H 0.openwrt.pool.ntp.org -w 0.5 -c 1.0
command[check_ssh]=/usr/libexec/nagios/check_ssh -4 router
command[check_softwareupdate_opkg]=/root/bin/check_softwareupdate.sh opkg
command[check_users]=/usr/libexec/nagios/check_users -w 3 -c 5

Let's try to start it, and enable it if it works:

$ /etc/init.d/nrpe start
$ ps | grep nrp[e]
5320 nagios    2908 S    /usr/sbin/nrpe -c /etc/nrpe.cfg -d

$ /etc/init.d/nrpe enable

haveged

$ sysctl kernel.random.entropy_avail
kernel.random.entropy_avail = 140

Install haveged:

opkg update
opkg install haveged

With that, haveged should already be enabled and running, otherwise:

/etc/init.d/haveged enable
/etc/init.d/haveged start

Now it should look like this:

$ sysctl kernel.random.entropy_avail
kernel.random.entropy_avail = 1393

IPv6

There's an excellent OpenWrt Howto on how to get IPv6 up & running. Here's the short story:

$ opkg update && opkg install 6in4
Downloading http://downloads.openwrt.org/backfire/10.03.1/brcm-2.4/packages/Packages.gz.
Inflating http://downloads.openwrt.org/backfire/10.03.1/brcm-2.4/packages/Packages.gz.
Updated list of available packages in /var/opkg-lists/packages.
Installing 6in4 (10-1) to root...
Installing ip (2.6.29-1-2) to root...
Installing kmod-ipv6 (2.4.37.9-1) to root...
Installing kmod-sit (2.4.37.9-1) to root...
Configuring ip.
Configuring kmod-ipv6.
Configuring kmod-sit.
Warning: loading sit will taint the kernel: no license
 See http://www.tux.org/lkml/#export-tainted for information about tainted modules
Configuring 6in4.

When using HE, one can just use their "Example Configuration", since they're providing a full OpenWRT configuration:

uci set network.henet=interface
uci set network.henet.proto=6in4
uci set network.henet.peeraddr=1.2.3.4
uci set network.henet.ip6addr='2001:123:45:67::8/64'
uci set network.henet.tunnelid=123456
uci set network.henet.username=UserName
uci set network.henet.password='s3cr3t'
uci commit network

uci set firewall.@zone[1].network='wan henet'
uci commit firewall

ifup henet
/etc/init.d/firewall restart

We want to setup routing through this tunnel interface. For HE, we have to use the "Routed IPv6 Prefix" (Routed /64) and add it to our configuration. This prefix can be calculated by incrementing the last digit of the third quad in the tunneling prefix:

$ uci show network.henet.ip6addr
network.henet.ip6addr=2001:123:45:67::8/64

$ uci set network.lan.ip6addr='2001:123:46:67::8/64'
$ uci commit network


muninlite

MuninLite is a shell script implementing the Munin protocol and providing some Linux specific plugins.

$ opkg install muninlite                    # This will also install xinetd
$ /etc/init.d/xinetd enable

Be sure to add the bind option to xinetd's config, otherwise we'd listen even on the WAN interface:

$ cat /etc/xinetd.d/munin 
service munin
{
       socket_type     = stream
       protocol        = tcp
       wait            = no
       user            = root
       group           = root
       server          = /usr/sbin/munin-node
       disable         = no
       bind            = 10.0.0.1
}

Start xinetd:

$ /etc/init.d/xinetd start

$ echo list | nc localhost 4949 
# munin node at router
cpu if_eth0 if_eth0VLAN0 if_eth0VLAN1 if_br_lan if_err_eth0 if_err_eth0VLAN0 \
if_err_eth0VLAN1 if_err_br_lan  load memory processes uptime interrupts irqstats

nrpe

Note: NRPE is currently not available for OpenWRT, as it's unmaintained.[14]

opkg update
opkg install nrpe

Configure in /etc/nrpe.cfg, then:

/etc/init.d/nrpe enable
/etc/init.d/nrpe start

SNMP

opkg install snmpd                         # mini-snmpd can't handle 64-bit counters[15]

Adjust /etc/config/snmpd, e.g.:

config agent
       option agentaddress udp:10.0.0.1:161
[...]
config system
       option sysLocation      'UK'
       option sysContact       'root@mailhub'
       option sysName          'router'
#      option sysServices      72
#      option sysDescr         'home router'
#      option sysObjectID      '1.2.3.4'

Enable & start snmpd:

/etc/init.d/snmpd enable
/etc/init.d/snmpd start

snmpd should be running now:

# netstat -an | grep :161
udp        0      0 192.168.0.2:161         0.0.0.0:*

# ls -l /var/run/snmpd.conf 
-rw-r--r--    1 root     root           430 Dec  9 22:52 /var/run/snmpd.conf

vnStat

opkg update
opkg install vnstat

With that, vnstat should already be configured and vnstatd should be running:

$ ps | grep vn[s]
5288 root       988 S    /usr/sbin/vnstatd -d

We may want to move the traffic database to a permanent storage[16], so it will be saved across reboots:

$ mv /var/lib/vnstat/ /root/.vnstat
$ cat /etc/vnstat.conf
Interface "eth1"
DatabaseDir "/root/.vnstat"

$ /etc/init.d/vnstat restart

If we need to configure vnstat manually:

$ uci get network.wan.ifname
eth1

Our LAN interface is often handled by a bridge:

$  brctl show
bridge name     bridge id               STP enabled     interfaces
br-lan          6fff.2be01a132618       no              eth0.1
                                                        wlan0
                                                        wlan1

Initialize the databases for both the WAN and LAN interface:

vnstat -u -i eth1
vnstat -u -i br-lan

If we decide against running the vnstatd daemon, we'll have to update the database with a cronjob:

$ crontab -l                                          # /etc/crontabs/root
*/5 * * * * /usr/bin/vnstat -u -i eth1
*/5 * * * * /usr/bin/vnstat -u -i br-lan

$ /etc/init.d/cron enable
$ /etc/init.d/cron start

Backup

sysupgrade

To backup the OpenWrt configuratin[17], use sysupgrade:

sysupgrade --create-backup /tmp/backup-$(uname -n)-$(date +%F).tar.gz

This can be restored via the web interface later on:

System → Backup / Flash Firmware  → Backup / Restore

Or, manually on the command line:

tar -xzvf -C / backup.tar.gz

Manual

We want to save some other parts of the system too:

uci show   > uci-show.txt
uci export > uci-export.txt
opkg list-installed > opkg-installed.txt

Backups can be created from the GUI (SystemBackup) or via command line. Since Busybox tar(1) does not support --exclude but only -X (exclude from a file),[18] a backup command will look something like this:

$ ssh router "cat /root/backup.exclude"
dev
lib/modules
usr/lib
proc
sys

$ ssh router "cd / && tar -X /root/backup.exclude -cf - ." | xz -9ec > router-backup.tar.xz

And the other way around, to restore:

xz -dc router-backup.tar.xz | ssh router "cd / && tar -xvf -"

Update

Caution: although upgrading packages is possible[19] via opkg it's not recommended and can be dangerous![20] Use only when you how to reset[21] and debrick[22] your device!

To update all (outdated) packages:

opkg update && opkg list-upgradable

opkg upgrade $(opkg list-upgradable | awk '{print $1}')                        # See the warning above!

Find all updated files:

find /overlay/ -type f -exec ls -ltrd '{}' + | grep "$(date +%b\ \ %-d)"       # May not work, since it's padding the 
                                                                               # days and +%-d may not supported yet.

Note: the -mtime option in OpenWRT's version of BusyBox/findutils was disabled and may not be available. It has been enabled again[23][24] in late 2015:

find /overlay/ -type f -mtime -1 -exec ls -ltrd '{}' +

Find leftover configuration files:

find /etc/ -name "*opkg" -exec ls -lhtrd '{}' +

Sometimes configuration formats have changed and uci throws an error:

uci: Parse error (too many arguments) at line 124, byte 23

Let's find out where the error was generated:

$ for c in /etc/config/*; do uci show ${c#/etc/config/} > /dev/null || echo $c; done
uci: Parse error (too many arguments) at line 124, byte 23
/etc/config/snmpd

After fixing this configuration file, the error should be gone.

Upgrade

As a preparation step, let's check and save modified configuration files:

cd /root
for f in $(opkg list-changed-conffiles); do grep -q $f /etc/sysupgrade.conf || echo "missing: $f"; done

Save package lists:

opkg list-installed | awk '{print $1}' | sort > opkg_installed
awk '/^Package:/ {PKG= $2} /^Status: .*user installed/ {print PKG}' /usr/lib/opkg/status | sort > status_installed

After backing up the device once more, start the actual upgrade over a wired connection using the correct image and after verifying its checksum:

sysupgrade -v /tmp/*sysupgrade.bin

Postinstall:

opkg list-installed | sort > foo
for p in `awk '!/^lib/ {print $1}' opkg_installed`; do grep -qw $p foo || echo "to be installed: $p"; done

Install missing packages and check for leftover configuration files:

find /etc -name "*opkg"

Enable necessary services again:

for s in haveged snmpd vnstat; do /etc/init.d/$s enable; done
for s in haveged snmpd vnstat; do /etc/init.d/$s start; done

Decommissioning

To decommision a router with OpenWRT installed:

  • Reset via Failsafe mode: unplug the power cord, press and hold the reset button, put in the power cord, release the reset button when the DMZ-LED lits up.
> telnet 192.168.1.1                               # no password required
$ mount_root
switching to jffs2                                 # rootfs is now mounted read/write

$ passwd
Changing password for root
...
Password for root changed by root                  # password set to "root"

$ firstboot
firstboot has already been run
jffs2 partition is mounted, only resetting files

This should've been enough, but just for fun:

$ mtd -r erase rootfs_data
Unlocking rootfs_data ...
Erasing rootfs_data ...
Rebooting ...

And again, to overwrite the backing device:

> telnet 192.168.1.1
$ cat /dev/urandom > /foo                           # This is in fact /overlay/upper/foo
cat: writing 'foo': No space left on device

$ rm -f /foo
$ echo s > /proc/sysrq-trigger 
$ echo u > /proc/sysrq-trigger 
$ dmesg | tail
SysRq : Emergency Sync
Syncing device 1f:02 ... OK
Syncing device 1f:04 ... OK
Done.
SysRq : Emergency Remount R/O
Remounting device 1f:02 ... R/O
Remounting device 1f:04 ... OK
Done.

We can also install a fresh firmware image onto the system:

  • Download the correct squashfs-factory.img image.
  • Reset router and put it into failsafe mode:[25]
    • Power off the router
    • Press and hold the reset button
    • Power on the router and continue to press the reset button.
    • After a while, the router will respond to ping on 192.168.1.1/24
    • Release the reset button
  • Install via TFTP[26]:
$ echo -e "binary\nrexmt 1\ntimeout 60\ntrace\nput openwrt-XXX-squashfs-factory.img\n" | tftp 192.168.1.1
Packet tracing on.
sent DATA <block=6655, 512 bytes>
received ACK <block=6655>
[...]
Sent 3408005 bytes in 1.2 seconds

The router will automatically reboot into the new firmware.

LEDE

The LEDE project and OpenWRT merged back together in January 2018.[27]

Links

References