, 18 min read

Using Odroid as IP Router

Original post is here eklausmeier.goip.de/blog/2017/04-14-using-odroid-as-ip-router.


I purchased an Odroid-XU4 for ca. 80 EUR including power-supply and case from Pollin. The original manufacturer is hardkernel. I intended to use this small ARM computer as a router and firewall. In the past I had used routers from multiple vendors, e.g., Linksys/Cisco, TP-Link, AVM/FritzBox, Netgear, and so on. There is a rule of thumb with all these devices: Usually you have to reboot them once or twice a month, otherwise they misbehave somehow. At least three of these device went completely catatonic. Now I had enough of this, I also wanted a command line interface to the router, ideally a real Linux system with bash, cron, gcc, etc. Although I already own an Intel NUC and I am very happy with this computer, an Intel NUC is a little bit too expensive to be used as just a router.

I recommend to additionally purchase a RTC backup battery. The Odroid has a realtime clock, but loses all date and time information once powered off. This way the log of the computer is garbled.

1. Installing Arch Linux on Odroid

I followed the description in archlinux|ARM. Below statements are just copied verbatim from that link.

dd if=/dev/zero of=/dev/sdX bs=1M count=8
fdisk /dev/sdX
mkfs.ext4 /dev/sdX1
mkdir root
mount /dev/sdX1 root
wget https://os.archlinuxarm.org/os/ArchLinuxARM-odroid-xu3-latest.tar.gz
bsdtar -xpf ArchLinuxARM-odroid-xu3-latest.tar.gz -C root
cd root/boot
sh sd_fusing.sh /dev/sdX
cd ../..
umount root

All commands above proceed quickly. Only downloading the almost 300MB big Arch Linux image file takes some time according your internet speed.

Once Arch Linux is installed the computer looks like this.

$ df -h
Filesystem      Size  Used Avail Use% Mounted on
dev             931M     0  931M   0% /dev
run             997M  1.4M  996M   1% /run
/dev/mmcblk1p1   30G  2.2G   26G   8% /
tmpfs           997M     0  997M   0% /dev/shm
tmpfs           997M     0  997M   0% /sys/fs/cgroup
tmpfs           997M  4.0K  997M   1% /tmp
. . .

Here is information from cpuinfo:

$ cat /proc/cpuinfo
processor       : 0
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 78.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 1
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 78.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 2
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 78.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 3
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 78.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0xc07
CPU revision    : 3

processor       : 4
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 5
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 6
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

processor       : 7
model name      : ARMv7 Processor rev 3 (v7l)
BogoMIPS        : 120.00
Features        : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae 
CPU implementer : 0x41
CPU architecture: 7
CPU variant     : 0x2
CPU part        : 0xc0f
CPU revision    : 3

Hardware        : SAMSUNG EXYNOS (Flattened Device Tree)
Revision        : 0100
Serial          : 0000000000000000

One can clearly see that the Samsung Exynos processor 5422 has 8 cores, and uses this ARM big.LITTLE architecture, where there are two different core types:

  1. The slow one, 1.5 GHz quad-core Cortex-A7
  2. The fast one, 2.1 GHz quad-core Cortex-A15

See also A big.LITTLE scheduler update as referenced in ODROID-Magazine February 2017:

$ cat /sys/devices/system/cpu/cpu0/cpufreq/affected_cpus
0 1 2 3
$ cat /sys/devices/system/cpu/cpu7/cpufreq/affected_cpus
4 5 6 7

Output from lstopo.

For further information see Odroid-XU4 user manual. For a thorough performance test see ODROID-XU4: Much Better Performance Than The Raspberry Pi Plus USB3 & Gigabit Ethernet @ $60 in Phoronix.

2. Network Cards

The Odroid has two network cards, named eth0 and ethusb0. ethusb0 is directly connected to the cable modem, and gets its IP address per DHCP from the cable modem. eth0 has a static IP address. I used systemd-networkd for this.

$ ls -l /etc/systemd/network
total 12K
-rw-r--r-- 1 root root 95 Apr  1 22:15 10-ethusb0.link
-rw-r--r-- 1 root root 55 Feb  1 02:24 eth0.network
-rw-r--r-- 1 root root 41 Apr  2 13:52 ethusb0.network

I wanted a fixed device name for the network card using USB, i.e., gigabit-USB-dongle, so I used the MAC address of this card to give it a fixed name, here ethusb0:

$ cat /etc/systemd/network/10-ethusb0.link 
[Match]
MACAddress=74:da:38:9f:c8:fa

[Link]
Description=USB to Ethernet Adapter
Name=ethusb0

The built-in gigabit ethernet card gets a fixed IP address but does not use any gateway!

$ cat /etc/systemd/network/eth0.network 
[Match]
Name=eth0

[Network]
Address=192.168.178.1/24

I do not know how the card gets its name eth0. Probably I should add an entry in /etc/systemd/network using the MAC address.

The network card connected to the cable modem, gets its IP address via DHCP.

$ cat /etc/systemd/network/ethusb0.network 
[Match]
Name=ethusb0

[Network]
DHCP=yes

Once connected to the "real" internet, this will lead to the following routing table:

$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         ip-95-222-192-1 0.0.0.0         UG    1024   0        0 ethusb0
95.222.192.0    0.0.0.0         255.255.248.0   U     0      0        0 ethusb0
ip-95-222-192-1 0.0.0.0         255.255.255.255 UH    1024   0        0 ethusb0
192.168.178.0   0.0.0.0         255.255.255.0   U     0      0        0 eth0

$ ifconfig
eth0: flags=4163  mtu 1500
        inet 192.168.178.1  netmask 255.255.255.0  broadcast 192.168.178.255
        inet6 fe80::21e:6ff:fe31:a552  prefixlen 64  scopeid 0x20
        ether 00:1e:06:31:a5:52  txqueuelen 1000  (Ethernet)
        RX packets 5097790  bytes 515594426 (491.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 11049724  bytes 2851984084 (2.6 GiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ethusb0: flags=4163  mtu 1500
        inet 95.222.195.135  netmask 255.255.248.0  broadcast 95.222.199.255
        inet6 fe80::76da:38ff:fe9f:c8fa  prefixlen 64  scopeid 0x20
        ether 74:da:38:9f:c8:fa  txqueuelen 1000  (Ethernet)
        RX packets 12352839  bytes 2937916965 (2.7 GiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 5128371  bytes 566073980 (539.8 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Below two photographs show the connections.

Photo

The Odroid-XU4 has a red light, indicating that it has power. A blue light blinks, if the kernel is up and running.

Photo

3. Forwarding

Enabling IP forwarding in the Linux kernel, see e.g., IP forwarding.

$ cat /etc/sysctl.d/network.conf
net.ipv4.ip_forward = 1

It is important to have the extension .conf for entries under /etc/sysctl.d, as systemd only applies setting for /etc/sysctl.d/*.conf files.

The essential part of this forwarding is that IP packets may hop from one ethernet card to another card. Setting ip_forward=0 would disallow this, i.e., not good for a router.

4. iptables

For a tutorial see Iptables Tutorial by Oskar Andreasson.

Adding masquerading to both cards would make the router complete. But I additionally needed the following:

  1. Ports 80+443 should be directed to another server
  2. I need to ssh to two other machines in my network, here 24+118
  3. I want to ssh to the Odroid from outside but not via port 22. Within my network I want to ssh and rsync to the Odroid without fuzz, i.e., using port 22
  4. Block access to ports 53 (DNS), 67+68 (bootp), and 5355 (LLMNR = Link Local Multicast Name Resolution)

So I needed to NAT port 80 to my web-server, NAT = network translation. For the two other ssh-machines I arbitrary chose ports 8022 and 9022, which then direct to port 22 on the respective machines. Internet traffic coming from outside, i.e., coming from ethusb0, for port 22 on the Odroid is redirected to some unused port, here 15001, essentially quiescing port 22 for the outside world. "Secret" port 7022 is NAT'ed to port 22 internally. So sshd on Odroid has its port on 22, as usual, so all ssh/rsync traffic within my network does not need any ssh-config trickery or explicit port specification.

$ iptables-save 
# Generated by iptables-save v1.6.0 on Thu Apr  6 22:55:46 2017
*nat
:PREROUTING ACCEPT [39276:4616181]
:INPUT ACCEPT [14683:1093947]
:OUTPUT ACCEPT [11747:870197]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -i ethusb0 -p tcp -m tcp --dport 8022 -j DNAT --to-destination 192.168.178.118:22
-A PREROUTING -i ethusb0 -p tcp -m tcp --dport 9022 -j DNAT --to-destination 192.168.178.24:22
-A PREROUTING -i ethusb0 -p tcp -m tcp --dport 80 -j DNAT --to-destination 192.168.178.24
-A PREROUTING -i ethusb0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 192.168.178.24
-A PREROUTING -i ethusb0 -p tcp -m tcp --dport 7022 -j DNAT --to-destination 192.168.178.1:22
-A PREROUTING -i ethusb0 -p tcp -m tcp --dport 22 -j DNAT --to-destination 127.0.0.1:15001
-A POSTROUTING -o ethusb0 -j MASQUERADE
COMMIT
# Completed on Thu Apr  6 22:55:46 2017
# Generated by iptables-save v1.6.0 on Thu Apr  6 22:55:46 2017
*filter
:INPUT ACCEPT [97:7702]
:FORWARD ACCEPT [95:13175]
:OUTPUT ACCEPT [59:7201]
-A INPUT -i ethusb0 -p tcp -m tcp --dport 53 -j DROP
-A INPUT -i ethusb0 -p udp -m udp --dport 53:68 -j DROP
-A INPUT -i ethusb0 -p tcp -m tcp --dport 5335 -j DROP
-A INPUT -i ethusb0 -p udp -m udp --dport 5335 -j DROP
COMMIT

Above output of iptables-save is later stored in /etc/iptables/iptables/iptables.rules.

Using MASQUERADE for ethusb0 is clear: Outgoing traffic to the "real" internet must be hidden, as the internal IP addresses make no sense in the "real" internet.

Remark: --to-destination 127.0.0.1:15001 is really -j DROP, but is not allowed in table nat.

iptables uses tables and chains. In above output *nat designates the table "nat". Similarly, *filter designates the table "filter". If you want to type in the single commands, the first rule would be, for example:

iptables -t nat -A PREROUTING -i ethusb0 -p tcp -m tcp --dport 8022 -j DNAT --to-destination 192.168.178.118:22

In the same vein:

iptables -t filter -A INPUT -i ethusb0 -p tcp -m tcp --dport 53 -j DROP

Leaving port 22 open to the outside can be a nuisance, see appendix below.

Finally enabling iptables in systemd.

$ systemctl enable iptables

$ systemctl start iptables

$ systemctl status iptables
  iptables.service - Packet Filtering Framework
   Loaded: loaded (/usr/lib/systemd/system/iptables.service; enabled; vendor preset: disabled)
   Active: active (exited) since Mon 2017-04-03 22:01:12 CEST; 15s ago
  Process: 600 ExecStart=/usr/bin/iptables-restore /etc/iptables/iptables.rules (code=exited, status=0/SUCCESS)
 Main PID: 600 (code=exited, status=0/SUCCESS)

Apr 03 22:01:12 odroid systemd[1]: Starting Packet Filtering Framework...
Apr 03 22:01:12 odroid systemd[1]: Started Packet Filtering Framework.

If you feel shaky about fingering with iptables, then write a script, which deletes all firewall rules, like

iptables -t nat -F
iptables -F

which runs periodically via cron. This way you can get back in to your machine, once you messed up the firewall rules.

5. DNS

I use dnsmasq as DNS server, see dnsmasq Arch ARM package. I also used dnsmasq first on my laptop to test the Odroid acting as DHCP client on ethusb0.

Important entries in /etc/dnsmasq.conf are:

address=/klm.no-ip.org/192.168.178.24
address=/klm.ddns.net/192.168.178.24
address=/edh.ddns.net/192.168.178.24
address=/klmport.no-ip.org/192.168.178.24
address=/borussia.no-ip.org/192.168.178.118
address=/www.eklausmeier.tk/192.168.178.24

address=/2o7.net/127.0.0.1
address=/adbrite.com/127.0.0.1
address=/adimg.uimserv.net/127.0.0.1
address=/adition.net/127.0.0.1
address=/adition.com/127.0.0.1
address=/ads.t-online.de/127.0.0.1
address=/adtech.de/127.0.0.1
address=/doubleclick.net/127.0.0.1
address=/ivwbox.de/127.0.0.1
address=/intellitxt.com/127.0.0.1
address=/kontera.com/127.0.0.1
address=/nuggad.net/127.0.0.1
address=/tfag.de/127.0.0.1
address=/tribalfusion.com/127.0.0.1
address=/quality-channel.de/127.0.0.1
address=/vibrantmedia.com/127.0.0.1

The first block gives special values to my No-IP addresses. The second block is a simple ad-blocker.

If you use dnsmasq, then you have to disable systemd-resolved:

systemctl disable systemd-resolved
systemctl stop systemd-resolved

6. Speed Test

I made multiple speed tests. They showed the router is working as expected.

$ speedtest
Retrieving speedtest.net configuration...
Testing from Unitymedia (95.222.195.135)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by LWLcom GmbH (Frankfurt) [15.28 km]: 18.666 ms
Testing download speed....................
Download: 97.78 Mbit/s
Testing upload speed......................
Upload: 5.31 Mbit/s

In above example I used Arch package community/speedtest-cli, see speedtest-cli Arch package.

Here is another example.

$ speedtest
Retrieving speedtest.net configuration...
Testing from Unitymedia (95.222.195.135)...
Retrieving speedtest.net server list...
Selecting best server based on ping...
Hosted by 23media GmbH (Frankfurt) [15.25 km]: 12.446 ms
Testing download speed....................
Download: 105.46 Mbit/s
Testing upload speed......................
Upload: 5.11 Mbit/s

So speed is sufficient for the internet if you have for example 100 MBit/s. The network interface on the Odroid, though, is limited to max. 800 MBit/s. Below is a test with iperf3:

$ iperf3 -c R
Connecting to host R, port 5201
[  5] local 192.168.178.5 port 38814 connected to 192.168.178.20 port 5201
[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
[  5]   0.00-1.00   sec  89.3 MBytes   746 Mbits/sec    0    115 KBytes       
[  5]   1.00-2.00   sec  88.1 MBytes   742 Mbits/sec    0    141 KBytes       
[  5]   2.00-3.00   sec   100 MBytes   839 Mbits/sec    0    410 KBytes       
[  5]   3.00-4.00   sec   102 MBytes   852 Mbits/sec    0    410 KBytes       
[  5]   4.00-5.00   sec   102 MBytes   853 Mbits/sec    0    431 KBytes       
[  5]   5.00-6.00   sec   102 MBytes   860 Mbits/sec    0    431 KBytes       
[  5]   6.00-7.00   sec   101 MBytes   851 Mbits/sec    0    452 KBytes       
[  5]   7.00-8.00   sec   102 MBytes   852 Mbits/sec    0    452 KBytes       
[  5]   8.00-9.00   sec   102 MBytes   855 Mbits/sec    0    452 KBytes       
[  5]   9.00-10.01  sec   103 MBytes   855 Mbits/sec    0    452 KBytes       
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval           Transfer     Bitrate         Retr
[  5]   0.00-10.01  sec   991 MBytes   830 Mbits/sec    0             sender
[  5]   0.00-10.04  sec   990 MBytes   827 Mbits/sec                  receiver

iperf Done.

7. Problems

a) As I forgot to purchase a backup battery, the Odroid loses its date and time, whenever the machine reboots. Once internet is available I use the following commands to get current date and time:

ntpdate ptbtime2.ptb.de
sleep 1
ntpdate ptbtime2.ptb.de
hwclock --systohc

I ask the NTP server twice, so the drift is not that huge.

Better than this is to automate it via systemd-timers.

Create a file /etc/systemd/system/date-fetch.timer as

[Unit]
Description="Runs periodic time synchronisation using a custom script"

[Timer]
OnBootSec=45sec
OnUnitActiveSec=1w

[Install]
WantedBy=timers.target

The corresponding "service" is:

$ cat /etc/systemd/system/multi-user.target.wants/date-fetch.service
[Unit]
Description="Runs periodic time synchronisation using a custom script"

[Timer]
OnBootSec=45sec
OnUnitActiveSec=1w

[Install]
WantedBy=timers.target

odroid /etc/systemd/system% cat date-fetch.service 
[Unit]
Description="Fetch the current date only under certain circumstances"

[Service]
Type=simple
Restart=on-failure
RestartSec=5sec
ExecStart=/usr/local/bin/date-fetch

[Install]
WantedBy=multi-user.target

The final bash-script /usr/local/bin/date-fetch is as follows:

#!/bin/bash
# Set date via NTP if internet is available
# Gordian Edenhofer, 11-Apr-2017

set -eu

if [[ -n "$(ip addr show ethusb0 | sed -n 's/.*inet \([0-9\.]*\).*/\1/gp')" ]]; then
        ntpdate ptbtime2.ptb.de
        hwclock -w

        # Cleanly exit since an IP address has been assigned to the network interface
        # and the above commands completed without a failure (ensured by `set -e`)
        exit 0
else
        # Return a failure exit code since an IP has not yet been assigned
        exit 1
fi

b) I refrained from using systemd-timesyncd as this may quickly flood your log if the Odroid is not connected to the internet. It is not uncommon that my ISP has an outage, albeit seldom.

c) Rebooting the Odroid via "shutdown -r now" does not always work. Many times I have to disconnect the power cord, then connect again. I had difficulties with shutdown -r now, but they are now long gone.

d) When I am within my own network, I cannot http/https/ssh to internal servers using the external IP address.

e) It is not clear whether the Odroid is fully up to the task for functioning as a router, as exemplified by these kernel messages, time will tell:

Apr 05 17:50:30 odroid kernel: INFO: rcu_preempt detected stalls on CPUs/tasks:
Apr 05 17:50:30 odroid kernel:         1-...: (53937 GPs behind) idle=b1a/0/0 softirq=890/890 fqs=0 
Apr 05 17:50:30 odroid kernel:         2-...: (116479 GPs behind) idle=8f4/0/0 softirq=645/645 fqs=0 
Apr 05 17:50:30 odroid kernel:         3-...: (116479 GPs behind) idle=ed4/0/0 softirq=484/484 fqs=0 
Apr 05 17:50:30 odroid kernel:         (detected by 0, t=4207 jiffies, g=1365668, c=1365667, q=217)
Apr 05 17:50:30 odroid kernel: Task dump for CPU 1:
Apr 05 17:50:30 odroid kernel: swapper/1       R  running task        0     0      1 0x00000000
Apr 05 17:50:30 odroid kernel: [] (__schedule) from [] (rcu_idle_enter+0x60/0x64)
Apr 05 17:50:30 odroid kernel: [] (rcu_idle_enter) from [] (cpu_startup_entry+0x198/0x218)
Apr 05 17:50:30 odroid kernel: [] (cpu_startup_entry) from [] (0x401015ac)
Apr 05 17:50:30 odroid kernel: Task dump for CPU 2:
Apr 05 17:50:30 odroid kernel: swapper/2       R  running task        0     0      1 0x00000000
Apr 05 17:50:30 odroid kernel: [] (__schedule) from [] (rcu_idle_enter+0x60/0x64)
Apr 05 17:50:30 odroid kernel: [] (rcu_idle_enter) from [] (cpu_startup_entry+0x198/0x218)
Apr 05 17:50:30 odroid kernel: [] (cpu_startup_entry) from [] (0x401015ac)
Apr 05 17:50:30 odroid kernel: Task dump for CPU 3:
Apr 05 17:50:30 odroid kernel: swapper/3       R  running task        0     0      1 0x00000000
Apr 05 17:50:30 odroid kernel: [] (__schedule) from [] (rcu_idle_enter+0x60/0x64)
Apr 05 17:50:30 odroid kernel: [] (rcu_idle_enter) from [] (cpu_startup_entry+0x198/0x218)
Apr 05 17:50:30 odroid kernel: [] (cpu_startup_entry) from [] (0x401015ac)
Apr 05 17:50:30 odroid kernel: rcu_preempt kthread starved for 4228 jiffies! g1365668 c1365667 f0x0 RCU_GP_WAIT_FQS(3) ->state=0x1
Apr 05 17:50:30 odroid kernel: rcu_preempt     S    0     7      2 0x00000000
Apr 05 17:50:30 odroid kernel: [] (__schedule) from [] (schedule+0x4c/0xac)
Apr 05 17:50:30 odroid kernel: [] (schedule) from [] (schedule_timeout+0x1f8/0x34c)
Apr 05 17:50:30 odroid kernel: [] (schedule_timeout) from [] (rcu_gp_kthread+0x5e4/0x948)
Apr 05 17:50:30 odroid kernel: [] (rcu_gp_kthread) from [] (kthread+0xec/0x104)
Apr 05 17:50:30 odroid kernel: [] (kthread) from [] (ret_from_fork+0x14/0x3c)

It is obvious that these problems stem from the "little" cores in the CPU:

$ journalctl | grep -C5 stalls | grep "for CPU"
Apr 04 18:55:29 odroid kernel: Task dump for CPU 3:
Apr 04 20:27:36 odroid kernel: Task dump for CPU 1:
Apr 05 01:49:18 odroid kernel: Task dump for CPU 1:
Apr 05 08:54:50 odroid kernel: Task dump for CPU 1:
Apr 05 17:50:30 odroid kernel: Task dump for CPU 1:
Apr 07 00:00:25 odroid kernel: Task dump for CPU 3:
Apr 08 00:41:53 odroid kernel: Task dump for CPU 1:
Apr 08 12:36:29 odroid kernel: Task dump for CPU 1:
Apr 09 16:48:46 odroid kernel: Task dump for CPU 1:
Apr 10 01:24:28 odroid kernel: Task dump for CPU 1:
Apr 10 20:23:02 odroid kernel: Task dump for CPU 1:
Apr 12 00:00:27 odroid kernel: Task dump for CPU 3:

Adding CPUAffinity=0-3 to /etc/systemd/system/multi-user.target.wants/sshd.service, and similarly adding CPUAffinity=4-7 to /etc/systemd/system/multi-user.target.wants/iptables.service and /etc/systemd/system/multi-user.target.wants/systemd-networkd.service does not remedy above problem.

Appendix

Excerpt from lastb: Kinky people trying to login to the Odroid-XU4 by brute force.

xbmc     ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
xbmc     ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
xbian    ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
xbian    ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
wwwrun   ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
wwwrun   ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
workshop ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
workshop ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
windowse ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
windowse ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
webpop   ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
webpop   ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
webmaste ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
webmaste ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
webmaste ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
webadmin ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
webadmin ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
vyatta   ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
vyatta   ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
visitor  ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
visitor  ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
virus    ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
virus    ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
vagrant  ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
vagrant  ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
uucp     ssh:notty    185.86.77.119    Tue Apr  4 20:37 - 20:37  (00:00)
uucp     ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
users    ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
users    ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
user     ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
user     ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
user     ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
user     ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
user     ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
username ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
user     ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)
username ssh:notty    185.86.77.119    Tue Apr  4 20:36 - 20:36  (00:00)

Similarly, journalctl | grep "Received disc":

Apr 04 22:57:30 odroid sshd[2812]: Received disconnect from 221.194.44.224 port 54372:11:  [preauth]
Apr 04 23:00:42 odroid sshd[2815]: Received disconnect from 221.194.44.211 port 44199:11:  [preauth]
Apr 04 23:11:58 odroid sshd[2818]: Received disconnect from 221.194.47.208 port 36286:11:  [preauth]
Apr 04 23:12:17 odroid sshd[2820]: Received disconnect from 221.194.47.224 port 37452:11:  [preauth]
Apr 04 23:16:59 odroid sshd[2824]: Received disconnect from 121.18.238.104 port 52330:11:  [preauth]
Apr 04 23:18:51 odroid sshd[2827]: Received disconnect from 221.194.44.211 port 33700:11:  [preauth]
Apr 04 23:26:47 odroid sshd[2830]: Received disconnect from 221.194.47.249 port 35392:11:  [preauth]
Apr 04 23:45:05 odroid sshd[2836]: Received disconnect from 121.18.238.98 port 49376:11:  [preauth]
Apr 04 23:45:54 odroid sshd[2838]: Received disconnect from 221.194.47.208 port 57745:11:  [preauth]
Apr 04 23:54:31 odroid sshd[2841]: Received disconnect from 221.194.44.211 port 50781:11:  [preauth]
Apr 05 00:04:00 odroid sshd[2932]: Received disconnect from 221.194.47.249 port 43806:11:  [preauth]
Apr 05 00:15:29 odroid sshd[2964]: Received disconnect from 121.18.238.104 port 47782:11:  [preauth]
Apr 05 00:17:31 odroid sshd[2966]: Received disconnect from 221.194.47.208 port 53492:11:  [preauth]

Even after a few hours leaving port 22 open to the public, your log is full of this silliness.

Now that port 22 is blocked, iptables shows you how many times people attack you:

$ iptables -t nat -t nat -L -n -v
. . .
  225  9840 DNAT       tcp  --  ethusb0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:22 to:127.0.0.1:15001

I.e., 225 times iptables routed port 22 to nirvana.

Similarly, DNS request from outside.

$ iptables -L -n -v
. . .
 2173  792K DROP       udp  --  ethusb0 *       0.0.0.0/0            0.0.0.0/0            udp dpts:53:68

I.e., 2173 times people from the "real" internet asked the Odroid about DNS within a few hours. I guess these people did not have the best intentions.

Comment from Paul Alesius, 29-Apr-2017:

Great comprehensive post. I am experiencing similar issues with the scheduling as you mention in section 7.e) and the CPU affinity is an interesting observation. The kernel is unable to use swap and I’ve tried different kernel configurations with no success, I believe disabling swapping should fix it. Strangely, by writing a small program that uses memory beyond RAM, it does seem to start consuming swap, but the kernel fails to use swap automatically. It might work by disabling big.LITTLE support in the kernel, but I haven’t tried this. Let me know if you fix it somehow.

On point 7.c) and shutdown -r now, I believe this is due to the eMMC requiring a special reset procedure, example: https://lists.denx.de/pipermail/u-boot/2015-January/200880.html I don’t believe this board is stable enough to remain up for more than a month or two, heavy read/write/network ends up killing mine (an USB connected 2.5″ drive is specially problematic), but works fine otherwise when mostly idle.

Reply from me, 29-Apr-2017: Thank you for your detailed comment. Indeed, I do not use swap, but not for your mentioned reasons, but rather because I never intended to run anything “large” on the Odroid.

Added 21-May-2017: Deleted text on MASQUERADE from nat-table.

-A POSTROUTING -o eth0 -j MASQUERADE

Above rule maps real internet to Odroid IP address in the inner internet. It doesn't do any harm but hides all outer internet addresses. Using MASQUERADE for eth0 was not initially clear to me, but makes sense in retrospect: Ingoing traffic from the "real" internet makes no sense in the internal network, so must be replaced by an internal IP address.

Added 06-Aug-2017: The router has stood the test of time. All these "stalls on CPUs/tasks" warnings are still there but apparently do not harm. The router works reliably and flawless.

Added 16-Nov-2017: Warnings on "stalls on CPUs/tasks" are completely gone with Linux kernel >= linux-odroid-xu3 (4.9.47-4).

Added 10-Jan-2019: I previously added ca. 3000 iptables rules for blocking IP address ranges which attacked me on port 22 (ssh). That many rules will deteriorate your network performance significantly. My download speed went down from 100 MBit/s to 20 MBit/s.

Added 05-Jun-2021: Decommissioning the Odroid as WiFi-router. Now the nuc will take over this duty. The Odroid proved to be very reliable. There is no failure or dissatisfaction with the router that lead to the decommissioning. It is just that the nuc is running anyway, so can take over this task, and the Odroid can be powered down to save some electricity, round about 7W.