"""Mark VIII RF role manager: radio1/phy1 is shared between an uplink STA (``wlan1up``) and attack work, so the roles are made mutually exclusive. Uplink pauses channel hopping; attack/idle resumes it.""" ROLE_KEY = 'mk8.rfplan.role' IFACE = 'wlan1up' def current_role(): from server import _uci_values cfg = _uci_values('wireless.%s' % IFACE) or {} if cfg.get('disabled') != '1' and cfg.get('mode') == 'sta': return 'uplink' return 'idle' def associated(): """BSSID of the uplink AP when wlan1up is associated, else None.""" from server import device_run rc, out, err = device_run(['iw', 'dev', IFACE, 'link'], timeout=10) if rc != 0 or 'Connected' not in (out or ''): return None for line in (out or '').splitlines(): line = line.strip() if line.startswith('Connected to '): return line.split()[2] return None def hop_paused(): from server import _read_hop return _read_hop() == '0' def set_role(role, ssid=None, psk=None): from server import device_run, _pause_hop, _resume_hop if role not in ('uplink', 'attack', 'idle'): return {'ok': False, 'error': 'role must be uplink, attack or idle'} if role == 'uplink': if not ssid: return {'ok': False, 'error': 'ssid required'} cmds = [ ['uci', 'set', 'wireless.wlan1up=wifi-iface'], ['uci', 'set', 'wireless.wlan1up.device=radio1'], ['uci', 'set', 'wireless.wlan1up.mode=sta'], ['uci', 'set', 'wireless.wlan1up.network=cli'], ['uci', 'set', 'wireless.wlan1up.ssid=%s' % ssid], ['uci', 'set', 'wireless.wlan1up.encryption=%s' % ('psk2' if psk else 'none')], ['uci', 'set', 'wireless.wlan1up.disabled=0'], ] if psk: cmds.append(['uci', 'set', 'wireless.wlan1up.key=%s' % psk]) for c in cmds: device_run(c) # The STA rides network 'cli'; stock firmware ships it disabled but # present. If it is missing entirely, create a minimal DHCP interface # so netifd can bring wlan1up up. rc, _, _ = device_run(['uci', 'show', 'network.cli']) if rc != 0: device_run(['uci', 'set', 'network.cli=interface']) device_run(['uci', 'set', 'network.cli.proto=dhcp']) device_run(['uci', 'commit', 'wireless']) _pause_hop() device_run(['wifi', 'reload'], timeout=60) assoc = associated() if not assoc: disable_uplink() _resume_hop() return {'ok': False, 'error': 'association failed; reverted'} return {'ok': True, 'role': 'uplink', 'assoc': assoc} # attack/idle: tear down the STA so radio1 is free again. disable_uplink() _resume_hop() return {'ok': True, 'role': role} def disable_uplink(): from server import device_run device_run(['uci', 'set', 'wireless.wlan1up.disabled=1']) device_run(['uci', 'commit', 'wireless']) def ensure_attack(): """Exclusivity hook for attack enable paths: switch uplink off first.""" if current_role() == 'uplink': return set_role('attack') return None