OpenWrt package feed for etherwake-nfqueue

Wake up computers on netfilter match

This repository contains the OpenWrt package feed for etherwake-nfqueue, a fork of the etherwake Wake-on-LAN client, with support to send magic packets only after a queued packet is received from the Linux nfnetlink_queue subsystem.

When running etherwake-nfqueue on a residential gateway or other type of router, it can wake up hosts on its network based on packet filtering rules.

For instance, when your set-top box wants to record a TV programme and tries to access a network share on your NAS, which is in sleep or standby mode, etherwake-nfqueue can wake up your NAS. Or when you set up port forwarding to a host on your home network, etherwake-nfqueue can wake up your host when you try to access it over the Internet.

The documentation below is mostly OpenWrt specific. For more information on etherwake-nfqueue itself and use case examples, please consult its Readme.

Building the package

Currently, no pre-built packages are provided. The following assumes that you already have a working OpenWrt build system for your target device.

If you haven't, you can follow one of these guides:

Dependencies

etherwake-nfqueue depends on these OpenWrt packages:

  • libnetfilter-queue
  • iptables-mod-nfqueue

They will be automatically selected and compiled for you. If they are not installed on your target device, opkg will try to resolve dependencies with packages in the repositories.

Adding the package feed

First, you need to add the etherwake-nfqueue package feed to your build system. In the root directory of your OpenWrt build system, find the file feeds.conf (or feeds.conf.default if the former shouldn't exist) and add the following line to it:

src-git ethernfq https://github.com/mister-benjamin/etherwake-nfqueue-openwrt.git

Then update and install the package feed:

user@host:~/openwrt$ scripts/feeds update ethernfq
user@host:~/openwrt$ scripts/feeds install -a -p ethernfq

After that, enter OpenWrt's configuration menu

user@host:~/openwrt$ make menuconfig

and enable etherwake-nfqueue in the Network sub-menu. It can either be selected as built-in (*) or module (M), depending on your decision to include it into a firmware image or just build the opkg package for installation.

Then you should be able to compile the package:

user@host:~/openwrt$ make package/etherwake-nfqueue/compile

The path of the resulting package depends on your selected Target System. In case of the Linksys WRT1200AC, it can be found here:

bin/packages/arm_cortex-a9_vfpv3/ethernfq/etherwake-nfqueue_2019-09-10-67e9d4ca-1_arm_cortex-a9_vfpv3.ipk

Installation

One way to install the package is by simply copying it over to the device with scp:

user@host:~$ scp etherwake-nfqueue_2019-09-10-67e9d4ca-1_arm_cortex-a9_vfpv3.ipk root@gateway:~

And then, install it on the device:

root@gateway:~# opkg install etherwake-nfqueue_2019-09-10-67e9d4ca-1_arm_cortex-a9_vfpv3.ipk

Configuration

WoL Targets

After a fresh installation, no target is configured. Targets are referred to as the hosts to wake up. Multiple targets can coexist.

Targets can be configured with OpenWrt's UCI.

For example, to add a target called nas, with MAC address 00:25:90:00:d5:fd, which is reachable over the VLAN configured on eth0.3, issue this command sequence on your router:

uci add etherwake-nfqueue target
uci set etherwake-nfqueue.@target[-1].name=nas
uci set etherwake-nfqueue.@target[-1].mac=00:25:90:00:d5:fd
uci set etherwake-nfqueue.@target[-1].interface=eth0.3
uci commit

For each target, one instance of etherwake-nfqueue will be started.

Each instance should bind to a different nfnetlink_queue. A queue can be referenced by its queue number. Counting starts from 0, which is the default. To use a different queue, provide the nfqueue_num option. The following could have been added to the sequence above to use queue 1 instead of 0:

uci set etherwake-nfqueue.@target[-1].nfqueue_num=1

The necessity of a queue number will probably become clear, when the iptables rules are configured in section Setup firewall rules.

The full list of options for a target is:

Option Required Description
name no Name of the target, e.g. name=example
mac yes MAC address of the host to wake up, e.g. mac=00:22:44:66:88:aa
nfqueue_num no The queue number used for receiving filtered packets, default is nfqueue_num=0
interface no The interface used for sending the magic packet, default is interface=eth0
broadcast no Send magic packet to broadcast address, default is broadcast=off
password no Set a password (required by some adapters), e.g. password=00:22:44:66:88:aa or 192.168.1.1
enabled no Optionally disable the target, default is enabled=true

After committing your changes, the settings are persisted to /etc/config/etherwake-nfqueue. This is an illustrative example:

config etherwake-nfqueue 'setup'
        option sudo 'off'
        option debug 'off'

config target
        option name 'nas'
        option mac '00:25:90:00:d5:fd'
        option interface 'eth0.3'

config target
        option name 'xyz-board'
        option mac '00:25:90:00:d5:fc'
        option nfqueue_num '1'
        option enabled 'false'

config target
        option name 'ip-camera'
        option mac '00:25:90:00:d5:fb'
        option nfqueue_num '2'
        option interface 'eth0.3'
        option broadcast 'on'
        option password '00:25:90:00:d5:fb'

When all target(s) are configured, restart the etherwake-nfqueue service:

/etc/init.d/etherwake-nfqueue restart

Setting up filters

Without any firewall rules which tell the kernel to match and add packets to a nfnetlink_queue, etherwake-nfqueue will never send out a magic packet to wake its target.

Prerequisites

In order to let the netfilter framework of the kernel see the packets, they need to pass through the router. This is usually not the case when hosts are on the same subnet and don't require network layer routing. The data will only pass through the router's switch on the link layer.

As a consequence, we can only use packets as a trigger which need to be routed or bridged by the router. Packets being forwarded between WAN and LAN are of that type. For other SOHO use cases, partitioning your network by means of subnets or VLANs might be necessary. The latter is often used to set up a DMZ.

For VLANs:

Guides to setup a DMZ can be found here:

The physical switch layout is device specific. E.g. the layout for the Linksys WRT AC Series is documented here.

Using two LANs or VLANs with the same network address and bridging them again is a trick to setup a transparent (or bridging) firewall on the same subnet. This way, packets can be seen by netfilter on the router even if the packets are not routed. Unfortunately this doesn't help when the host which we want to wake up is offline, as the ARP requests for the destination IP address are not answered and thus the client trying to reach out to its destination will not send any network layer packets. We could use arptables instead to wake the host when someone requests its MAC address, but this would probably happen too often and no fine-grained control would be possible.

As a workaround, it might be possible to configure a static ARP entry on your router (untested), e.g. with:

ip neigh add 192.168.0.10 lladdr 00:25:90:00:d5:fd nud permanent dev eth0.3

Note that this requires the ip-full OpenWrt package to be installed.

To make your firewall rules work with bridging, you need to install the kmod-br-netfilter package and add net.bridge.bridge-nf-call-iptables=1 to /etc/sysctl.conf.

Setup firewall rules

One way to setup custom firewall rules in OpenWrt is through its /etc/firewall.user script. This file can also be edited by means of the LuCI Web Interface (Network -> Firewall -> Custom Rules).

The file is interpreted as a shell script, so we can simply use iptables to add our custom firewall rules.

Notice the comment

# Internal uci firewall chains are flushed and recreated on reload, so
# put custom rules into the root chains e.g. INPUT or FORWARD or into the
# special user chains, e.g. input_wan_rule or postrouting_lan_rule.

Refer to Packet flow for usable chains. In the example below, the chains forwarding_lan_rule and forwarding_wan_rule are used. To inspect the rule sets of the different tables, one can use

iptables --list                  # default is --table filter
iptables --table nat --list
iptables --table mangle --list
iptables --table raw --list      # requires kmod-ipt-raw

The following is an example of what could be added to /etc/firewall.user:

iptables --insert forwarding_lan_rule\
         --protocol tcp --in-interface=br-lan --out-interface=eth0.3\
         --destination 192.168.0.10 --destination-port 445\
         --match conntrack --ctstate NEW\
         --match limit --limit 3/hour --limit-burst 1\
         --jump NFQUEUE --queue-num 0 --queue-bypass\
         --match comment --comment "Wake up NAS on LAN SMB"
iptables --insert forwarding_lan_rule\
         --protocol tcp --in-interface=br-lan --out-interface=eth0.3\
         --destination 192.168.0.11 --match multiport --destination-ports 515,54921,631\
         --match conntrack --ctstate NEW\
         --match limit --limit 3/hour --limit-burst 1\
         --jump NFQUEUE --queue-num 0 --queue-bypass\
         --match comment --comment "Wake up NAS on print request"
iptables --insert forwarding_lan_rule\
         --protocol udp --in-interface=br-lan --out-interface=eth0.3\
         --destination 192.168.0.11 --destination-port 161\
         --match conntrack --ctstate NEW\
         --match limit --limit 3/hour --limit-burst 1\
         --jump NFQUEUE --queue-num 0 --queue-bypass\
         --match comment --comment "Wake up NAS on print request"
iptables --insert forwarding_wan_rule\
         --protocol tcp --in-interface=eth1.2 --out-interface=eth0.3\
         --destination 192.168.0.10 --destination-port 22\
         --match conntrack --ctstate NEW\
         --match limit --limit 3/hour --limit-burst 1\
         --jump NFQUEUE --queue-num 0 --queue-bypass\
         --match comment --comment "Wake up NAS on WAN SSH"

In this example, packets are filtered based on the protocol, their input and output interfaces, their destination (IP address) and their destination port(s).

The option --match conntrack --ctstate NEW only matches packets of a new connection and --match limit --limit 3/hour --limit-burst 1 limits the amount of packets that are matched. The latter option roughly matches only one packet per 20 minutes. The intention here is to not be too intrusive and avoid sending a lot of magic packets.

The --jump NFQUEUE --queue-num 0 options tell the netfilter framework to enqueue a matching packet to the NFQUEUE number 0. In this example, all four rules send the matching packets into queue 0. The additional option --queue-bypass helps in the situation, when etherwake-nfqueue isn't running. Packets will then be handled as if the rule wasn't present.

Disabling targets

To disable targets, first find their index:

uci show etherwake-nfqueue

Then set its enabled option to false and restart the service. For index 0, it can be done like this:

uci set etherwake-nfqueue.@target[0].enabled=false
/etc/init.d/etherwake-nfqueue restart

Troubleshooting

Debug mode

In order to see what's going on in syslog and get some debug output when starting the service, enable etherwake-nfqueue's debug mode:

uci set etherwake-nfqueue.setup.debug=on

In another user session tail the log:

logread -f

And then restart the service:

/etc/init.d/etherwake-nfqueue restart

Inspect netfilter

To inspect the working of your firewall rules, you can print statistics of the chains you used, e.g.:

iptables --verbose --list forwarding_lan_rule

If you happen to have the procps-ng-watch package installed, you can watch them:

watch iptables --verbose --list forwarding_lan_rule

To see, if your queues are in place, use:

cat /proc/net/netfilter/nfnetlink_queue

Potential improvements

  • Add LuCI Web Interface configuration frontend for targets and filter rules
  • Add an option to set nice values for instances