From 08c92151eba002edd9541db63b00198cbaef22bf Mon Sep 17 00:00:00 2001 From: Colin Date: Wed, 11 Oct 2023 02:41:49 +0000 Subject: [PATCH] rtl8723cs-wowlan: automatically derive the IP address to watch for ARP packets on --- hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh | 2 +- pkgs/additional/rtl8723cs-wowlan/default.nix | 3 +- .../rtl8723cs-wowlan/rtl8723cs-wowlan | 46 +++++++++++++++---- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh b/hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh index e90dea12..51b28900 100755 --- a/hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh +++ b/hosts/modules/gui/sxmo/hooks/sxmo_suspend.sh @@ -36,7 +36,7 @@ doas rtl8723cs-wowlan tcp --dest-port 22 # wake on notification (ntfy/Universal Push) doas rtl8723cs-wowlan tcp --source-port 2587 # wake if someone doesn't know how to route to us, because that could obstruct the above -doas rtl8723cs-wowlan arp --dest-ip 10.78.79.54 +doas rtl8723cs-wowlan arp --dest-ip SELF echo "calling suspend for duration: $suspend_time" diff --git a/pkgs/additional/rtl8723cs-wowlan/default.nix b/pkgs/additional/rtl8723cs-wowlan/default.nix index 3e6b9d26..a9b1bc71 100644 --- a/pkgs/additional/rtl8723cs-wowlan/default.nix +++ b/pkgs/additional/rtl8723cs-wowlan/default.nix @@ -1,5 +1,6 @@ { static-nix-shell , iw +, moreutils , wirelesstools }: @@ -7,7 +8,7 @@ static-nix-shell.mkPython3Bin { pname = "rtl8723cs-wowlan"; src = ./.; pkgs = { - inherit iw wirelesstools; + inherit iw moreutils wirelesstools; }; } diff --git a/pkgs/additional/rtl8723cs-wowlan/rtl8723cs-wowlan b/pkgs/additional/rtl8723cs-wowlan/rtl8723cs-wowlan index 03e2865f..7e483c68 100755 --- a/pkgs/additional/rtl8723cs-wowlan/rtl8723cs-wowlan +++ b/pkgs/additional/rtl8723cs-wowlan/rtl8723cs-wowlan @@ -1,5 +1,5 @@ #!/usr/bin/env nix-shell -#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p iw -p wirelesstools +#!nix-shell -i python3 -p "python3.withPackages (ps: [ ])" -p iw -p moreutils -p wirelesstools # vim: set filetype=python : # common operations: @@ -7,6 +7,8 @@ # arp --dest-ip A.B.C.D # tcp --source-port N +KNOWN_INTERFACES = ("wlan0", "wlp3s0", "enp5s0") + import argparse import logging import subprocess @@ -17,6 +19,21 @@ def octet_to_hex(o: int) -> str: ''' format an octet as a two-character hex string (lowercase) ''' return '%02X' % o +def get_ipaddrs() -> list['IpAddr']: + ''' return the IP address of all known interfaces ''' + addrs = [] + for iface in KNOWN_INTERFACES: + try: + # errors if interface isn't known, or ip addr fails to parse (e.g. 'NON-IP') + maybe_addr = subprocess.check_output(['ifdata', '-pa', iface]).decode('utf-8') + addr = IpAddr(maybe_addr) + except: pass + else: + addrs.append(addr) + logger.debug(f'get_ipaddrs got: {[str(a) for a in addrs]}') + return addrs + + class Encodable: @staticmethod def get_octets(e: 'Encodable|None', len_: int) -> int|None: @@ -48,6 +65,13 @@ class IpAddr(Encodable): pieces = addr.split('.') self._octets = [int(p) if p else 0 for p in pieces] + @staticmethod + def parse_any(addr: str) -> list['IpAddr']: + if addr == 'SELF': + return get_ipaddrs() + else: + return [IpAddr(addr)] + def octets(self) -> list[int]: return self._octets @@ -137,9 +161,13 @@ class TcpFrame(Encodable): ] -def build_arp(dest_ip: str|None = None) -> EthernetFrame: - dest_ip = IpAddr(dest_ip) if dest_ip is not None else None - return EthernetFrame(EtherType.ARP, ArpFrame(dest_ip)) +def build_arp(dest_ip: str|None = None) -> list[EthernetFrame]: + dest_ips = IpAddr.parse_any(dest_ip) if dest_ip is not None else [] + # if no known IPs, wake on any ARP. + # the only way we could see an ARP message at this point is if the controller associates with a network + # while we're sleep (or while racing to sleep) + if dest_ips == []: dest_ips = [None] + return [EthernetFrame(EtherType.ARP, ArpFrame(ip)) for ip in dest_ips] def build_tcp(source_port: int|None = None, dest_port: int|None = None) -> EthernetFrame: source_port = Port(source_port) if source_port is not None else None @@ -169,7 +197,7 @@ def main(): arp_parser = subparsers.add_parser('arp', help="wake on ARP request") arp_parser.set_defaults(type_='arp') - arp_parser.add_argument('--dest-ip') + arp_parser.add_argument('--dest-ip', help="a.b.c.d or the special 'SELF' for automatic") tcp_parser = subparsers.add_parser('tcp', help="wake on TCP packet") tcp_parser.set_defaults(type_='tcp') @@ -187,13 +215,13 @@ def main(): exec_(['iw', 'phy0', 'wowlan', 'enable', 'any']) exec_(['iwpriv', 'wlan0', 'wow_set_pattern', 'clean']) - frame = None + frames = [] if args.type_ == 'arp': - frame = build_arp(dest_ip=args.dest_ip) + frames = build_arp(dest_ip=args.dest_ip) if args.type_ == 'tcp': - frame = build_tcp(source_port=args.source_port, dest_port=args.dest_port) + frames = [ build_tcp(source_port=args.source_port, dest_port=args.dest_port) ] - if frame is not None: + for frame in frames: pattern = str(frame) exec_(['iwpriv', 'wlan0', 'wow_set_pattern', f'pattern={pattern}'])