diff --git a/hosts/by-name/moby/default.nix b/hosts/by-name/moby/default.nix index 99b23116a..8bad7dc3f 100644 --- a/hosts/by-name/moby/default.nix +++ b/hosts/by-name/moby/default.nix @@ -25,9 +25,9 @@ sane.services.wg-home.ip = config.sane.hosts.by-name."moby".wg-home.ip; sane.wowlan.enable = true; sane.wowlan.patterns = [ - { ipv4.destPort = 22; } # wake on SSH - { ipv4.srcPort = 2587; } # wake on `ntfy-sh` push from servo - { arp.queryIp = [ 10 78 79 54 ]; } # wake when somebody is doing an ARP query against us + { tcp.destPort = 22; } # wake on SSH + { tcp.sourcePort = 2587; } # wake on `ntfy-sh` push from servo + { arp.destIp = "10.78.79.54"; } # wake when somebody is doing an ARP query against us ]; # XXX colin: phosh doesn't work well with passwordless login, diff --git a/modules/wowlan.nix b/modules/wowlan.nix index ef658bdd0..353fab14d 100644 --- a/modules/wowlan.nix +++ b/modules/wowlan.nix @@ -63,7 +63,7 @@ let cfg = config.sane.wowlan; patternOpts = with lib; types.submodule { options = { - ipv4.destPort = mkOption { + tcp.destPort = mkOption { type = types.nullOr types.port; default = null; description = '' @@ -72,7 +72,7 @@ let (e.g. this machine made a long-running HTTP request, and the other side finally has data for us). ''; }; - ipv4.srcPort = mkOption { + tcp.sourcePort = mkOption { type = types.nullOr types.port; default = null; description = '' @@ -81,115 +81,16 @@ let (e.g. sshing *into* this machine). ''; }; - arp.queryIp = mkOption { - type = types.nullOr (types.listOf types.int); + arp.destIp = mkOption { + type = types.nullOr types.string; default = null; description = '' IP address being queried. - e.g. `[ 192 168 0 100 ]` + e.g. `"192.168.0.100"` ''; }; }; }; - # bytesToStr: [ u8|null ] -> String - # format an array of octets into a pattern recognizable by iwpriv. - # a null byte means "don't care" at its position. - bytesToStr = bytes: lib.concatStringsSep ":" ( - builtins.map - (b: if b == null then "-" else hexByte b) - bytes - ); - # format a byte as hex, with leading zero to force a width of two characters. - # the wlan driver doesn't parse single-character hex bytes. - hexByte = b: if b < 16 then - "0" + (lib.toHexString b) - else - lib.toHexString b; - - etherTypes.ipv4 = [ 08 00 ]; # 0x0800 = IPv4 - etherTypes.arp = [ 08 06 ]; # 0x0806 = ARP - formatEthernetFrame = ethertype: dataBytes: let - bytes = [ - # ethernet frame: - ## dest MAC address (this should be the device's MAC, but i think that's implied?) - null null null null null null - ## src MAC address - null null null null null null - ## ethertype: - ] ++ etherTypes."${ethertype}" - ++ dataBytes; - in bytesToStr bytes; - - formatArpPattern = pat: - formatEthernetFrame "arp" ([ - # ARP frame: - ## hardware type - null null - ## protocol type. same coding as EtherType - 08 00 # 0x0800 = IPv4 - ## hardware address length (i.e. MAC) - 06 - ## protocol address length (i.e. IP address) - 04 - ## operation - 00 01 # 0x0001 = request - ## sender hardware address - null null null null null null - ## sender protocol address - null null null null - ## target hardware address - ## this is left as "Don't Care" because the packets we want to match - ## are those mapping protocol addr -> hw addr. - ## sometimes clients do include this field if they've seen the address before though - null null null null null null - ## target protocol address - ] ++ pat.queryIp); - - # formatIpv4Pattern: patternOpts.ipv4 -> String - # produces a string like this (example matches source port=0x0a1b): - # "-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:-:0a:1b:-:-" - formatIpv4Pattern = pat: let - destPortBytes = if pat.destPort != null then { - high = pat.destPort / 256; - low = lib.mod pat.destPort 256; - } else { - high = null; low = null; - }; - srcPortBytes = if pat.srcPort != null then { - high = pat.srcPort / 256; - low = lib.mod pat.srcPort 256; - } else { - high = null; low = null; - }; - in - formatEthernetFrame "ipv4" [ - # IP frame: - ## Version, Internet Header Length. 0x45 = 69 decimal - null # should be 69 (0x45), but fails to wake if i include this - ## Differentiated Services Code Point (DSCP), Explicit Congestion Notification (ECN) - null - ## total length - null null - ## identification - null null - ## flags, fragment offset - null null - ## Time-to-live - null - ## protocol: - 6 # 6 = TCP - ## header checksum - null null - ## source IP addr - null null null null - ## dest IP addr - null null null null - - # TCP frame: - srcPortBytes.high srcPortBytes.low - destPortBytes.high destPortBytes.low - ## rest is Don't Care - ]; in { options = with lib; { @@ -211,24 +112,33 @@ in config = lib.mkIf cfg.enable { systemd.services.wowlan = { description = "configure the WiFi chip to wake the system on specific activity"; - path = [ pkgs.iw pkgs.wirelesstools ]; + path = [ pkgs.rtl8723cs-wowlan ]; script = let - extractPatterns = pat: lib.flatten [ - (lib.optional (pat.ipv4 != { destPort = null; srcPort = null; }) (formatIpv4Pattern pat.ipv4)) - (lib.optional (pat.arp != { queryIp = null; }) (formatArpPattern pat.arp)) - ]; - allPatterns = lib.flatten (builtins.map extractPatterns cfg.patterns); - encodePattern = pat: '' - iwpriv wlan0 wow_set_pattern pattern=${pat} - ''; - encodedPatterns = lib.concatStringsSep - "\n" - (builtins.map encodePattern allPatterns); + tcpArgs = { destPort, sourcePort }: + [ "tcp" ] + ++ lib.optionals (destPort != null) [ "--dest-port" (builtins.toString destPort) ] + ++ lib.optionals (sourcePort != null) [ "--source-port" (builtins.toString sourcePort) ] + ; + arpArgs = { destIp }: + [ "arp" ] + ++ lib.optionals (destIp != null) [ "--dest-ip" (builtins.toString destIp) ] + ; + maybeCallHelper = maybe: args: + lib.optionalString + maybe + ((lib.escapeShellArgs ([ "rtl8723cs-wowlan" ] ++ args)) + "\n") + ; + applyPattern = pat: + (maybeCallHelper (pat.tcp != { destPort = null; sourcePort = null; }) (tcpArgs pat.tcp)) + + + (maybeCallHelper (pat.arp != { destIp = null; }) (arpArgs pat.arp)) + ; + appliedPatterns = lib.concatStringsSep + "" + (builtins.map applyPattern cfg.patterns); in '' - set -x - iw phy0 wowlan enable any - iwpriv wlan0 wow_set_pattern clean - ${encodedPatterns} + rtl8723cs-wowlan enable-clean + ${appliedPatterns} ''; serviceConfig = { # TODO: re-run this periodically, just to be sure? diff --git a/pkgs/additional/rtl8723cs-wowlan/default.nix b/pkgs/additional/rtl8723cs-wowlan/default.nix index e83e7b686..3e6b9d26c 100644 --- a/pkgs/additional/rtl8723cs-wowlan/default.nix +++ b/pkgs/additional/rtl8723cs-wowlan/default.nix @@ -4,7 +4,7 @@ }: static-nix-shell.mkPython3Bin { - pname = "rtl8723cs_wowlan"; + pname = "rtl8723cs-wowlan"; src = ./.; pkgs = { inherit iw wirelesstools; diff --git a/pkgs/additional/rtl8723cs-wowlan/rtl8723cs_wowlan b/pkgs/additional/rtl8723cs-wowlan/rtl8723cs-wowlan similarity index 100% rename from pkgs/additional/rtl8723cs-wowlan/rtl8723cs_wowlan rename to pkgs/additional/rtl8723cs-wowlan/rtl8723cs-wowlan