diff --git a/net/etherwake-nfqueue/Makefile b/net/etherwake-nfqueue/Makefile new file mode 100644 index 000000000..b72e78638 --- /dev/null +++ b/net/etherwake-nfqueue/Makefile @@ -0,0 +1,51 @@ +# +# Copyright (C) 2019 Mister Benjamin <144dbspl@gmail.com> +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=etherwake-nfqueue +PKG_RELEASE:=1 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/mister-benjamin/etherwake-nfqueue.git +PKG_SOURCE_DATE:=2019-09-28 +PKG_SOURCE_VERSION:=f71c269b58585e93575fa3e9fcc1793806fb3080 +PKG_MIRROR_HASH:=4960dc592abc4ca06504c92ca09fc736c678353df0dcc32d4081e17b137a9164 + +PKG_MAINTAINER:=Mister Benjamin <144dbspl@gmail.com> +PKG_LICENSE:=GPL-2.0+ + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +define Package/etherwake-nfqueue + SECTION:=net + CATEGORY:=Network + DEPENDS:=+libnetfilter-queue +iptables-mod-nfqueue + TITLE:=Wake up computers on netfilter match + URL:=https://github.com/mister-benjamin/etherwake-nfqueue +endef + +define Package/etherwake-nfqueue/description + Fork of etherwake with additional support for sending WOL packets + when a router added a filtered packet to an NFQUEUE. +endef + +define Package/etherwake-nfqueue/conffiles +/etc/config/etherwake-nfqueue +endef + +define Package/etherwake-nfqueue/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/etherwake-nfqueue $(1)/usr/bin/ + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) files/$(PKG_NAME).config $(1)/etc/config/$(PKG_NAME) + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) files/$(PKG_NAME).init $(1)/etc/init.d/$(PKG_NAME) +endef + +$(eval $(call BuildPackage,etherwake-nfqueue)) diff --git a/net/etherwake-nfqueue/README.md b/net/etherwake-nfqueue/README.md new file mode 100644 index 000000000..b44ac9efc --- /dev/null +++ b/net/etherwake-nfqueue/README.md @@ -0,0 +1,378 @@ +# OpenWrt package feed for etherwake-nfqueue + + +## Wake up computers on netfilter match + +This repository contains the OpenWrt package feed for +[etherwake-nfqueue](https://github.com/mister-benjamin/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](https://github.com/mister-benjamin/etherwake-nfqueue/blob/master/README.md). + + +## 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: + +* If you only want to compile packages, and no firmware image: + [Build system – Installation](https://openwrt.org/docs/guide-developer/build-system/install-buildsystem) and + [Using the SDK](https://openwrt.org/docs/guide-developer/using_the_sdk) + +* To quickly build a firmware image off a development snapshot: + [Quick Image Building Guide](https://openwrt.org/docs/guide-developer/quickstart-build-images) + +* Or when you are completely new to using build systems: + [Beginners guide to building your own firmware](https://openwrt.org/docs/guide-user/additional-software/beginners-build-guide) + +### 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](#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: + +* There's a mini howto referring to the **LuCI Web Interface** + *(Network -> Switch)* way of configuring VLANs: + [How-To: Creating an additional virtual switch on a typical home router](https://openwrt.org/docs/guide-user/network/vlan/creating_virtual_switches) + +* The manual approach is documented here: + [VLAN](https://openwrt.org/docs/guide-user/network/vlan/switch_configuration) + +Guides to setup a DMZ can be found here: + +* [Guide to set up DMZ via LUCI](https://forum.openwrt.org/t/guide-to-set-up-dmz-via-luci/21616) + +* [fw3 DMZ Configuration Using VLANs](https://openwrt.org/docs/guide-user/firewall/fw3_configurations/fw3_dmz) + +The physical switch layout is device specific. E.g. the layout for the Linksys +WRT AC Series is documented +[here](https://oldwiki.archive.openwrt.org/toh/linksys/wrt_ac_series#switch_layout). + + +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](https://oldwiki.archive.openwrt.org/doc/uci/firewall#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 diff --git a/net/etherwake-nfqueue/files/etherwake-nfqueue.config b/net/etherwake-nfqueue/files/etherwake-nfqueue.config new file mode 100644 index 000000000..541bbe2c2 --- /dev/null +++ b/net/etherwake-nfqueue/files/etherwake-nfqueue.config @@ -0,0 +1,21 @@ +config etherwake-nfqueue 'setup' + option sudo 'off' + option debug 'off' + +# You can add targets with uci: +# # uci add etherwake-nfqueue target +# Set a name for the target +# # uci set etherwake-nfqueue.@target[-1].name=example +# Set MAC address of the host to wake up +# # uci set etherwake-nfqueue.@target[-1].mac=00:22:44:66:88:aa +# Set the nfqueue num used for receiving filtered packets, defaults to 0 +# # uci set etherwake-nfqueue.@target[-1].nfqueue_num=0 +# Set the interface used for sending the magic packet, defaults to 'eth0' +# # uci set etherwake-nfqueue.@target[-1].interface=eth0 +# Configure if it should be sent to broadcast address, defaults to off +# # uci set etherwake-nfqueue.@target[-1].broadcast=off +# Optionally provide a password (required by some adapters) +# e.g. 00:22:44:66:88:aa or 192.168.1.1 +# # uci set etherwake-nfqueue.@target[-1].password=00:22:44:66:88:aa +# Optionally disable the target, by default it is enabled +# # uci set etherwake-nfqueue.@target[-1].enabled=false diff --git a/net/etherwake-nfqueue/files/etherwake-nfqueue.init b/net/etherwake-nfqueue/files/etherwake-nfqueue.init new file mode 100644 index 000000000..0be740db8 --- /dev/null +++ b/net/etherwake-nfqueue/files/etherwake-nfqueue.init @@ -0,0 +1,69 @@ +#!/bin/sh /etc/rc.common +# +# Copyright (C) 2019 Mister Benjamin <144dbspl@gmail.com> + +NAME='etherwake-nfqueue' + +START=60 +USE_PROCD=1 + +PROGRAM=${NAME} + +start_service() +{ + local value + + config_load ${NAME} + + config_get_bool value setup sudo 0 + [ "${value}" -ne 0 ] && PROGRAM="sudo ${PROGRAM}" + + config_get_bool value setup debug 0 + if [ "${value}" -ne 0 ]; then + PROCD_DEBUG=1 + append PROGRAM '-D' + fi + + config_foreach start_instance target +} + +start_instance() +{ + local section="$1" + local value name mac + + config_get_bool value "${section}" enabled 1 + [ "${value}" -ne 1 ] && return 0 + + config_get value "${section}" name + [ -z "${value}" ] && value="{section}" + name=${value} + + config_get mac "${section}" mac + [ -z "${mac}" ] && { + echo "${initscript}: Target ${name} has no MAC address" + return 1 + } + + procd_open_instance ${name} + procd_set_param command ${PROGRAM} + procd_set_param respawn + procd_set_param stdout 1 + procd_set_param stderr 1 + + config_get_bool value "${section}" broadcast 0 + [ "${value}" -ne 0 ] && procd_append_param command -b + + config_get value "${section}" interface + [ -n "${value}" ] && procd_append_param command -i "${value}" + + config_get value "${section}" password + [ -n "${value}" ] && procd_append_param command -p "${value}" + + config_get value "${section}" nfqueue_num 0 + procd_append_param command -q "${value}" + + procd_append_param command "${mac}" + + procd_close_instance +}