feat: get_ssids() macfilter/maclist parsing + push_mac_acl()

Adds MAC ACL (whitelist/blacklist) read+write support for wireless SSIDs,
mirroring push_radio_channel's UCI write style. Backs the new Global MAC
ACL feature in netOrk.
This commit is contained in:
Christian Manivong
2026-07-15 23:19:15 +02:00
parent bfc19c2241
commit af032a3c4d
2 changed files with 187 additions and 3 deletions
+135
View File
@@ -1099,3 +1099,138 @@ class TestPushRadioChannel:
commit_idx = next(i for i, c in enumerate(issued) if "commit" in c)
wifi_idx = next(i for i, c in enumerate(issued) if c.strip() == "wifi")
assert commit_idx < wifi_idx
UCI_WIRELESS_ACL = """\
wireless.radio0=wifi-device
wireless.radio0.band='2g'
wireless.radio1=wifi-device
wireless.radio1.band='5g'
wireless.@wifi-iface[0]=wifi-iface
wireless.@wifi-iface[0].device='radio0'
wireless.@wifi-iface[0].ssid='CorpWiFi'
wireless.@wifi-iface[0].encryption='psk2'
wireless.@wifi-iface[0].macfilter='allow'
wireless.@wifi-iface[0].maclist='AA:BB:CC:DD:EE:01'
wireless.@wifi-iface[0].maclist='AA:BB:CC:DD:EE:02'
wireless.@wifi-iface[1]=wifi-iface
wireless.@wifi-iface[1].device='radio1'
wireless.@wifi-iface[1].ssid='CorpWiFi'
wireless.@wifi-iface[1].encryption='psk2'
wireless.@wifi-iface[2]=wifi-iface
wireless.@wifi-iface[2].device='radio0'
wireless.@wifi-iface[2].ssid='GuestNet'
wireless.@wifi-iface[2].encryption='none'
"""
class TestGetSsidsAcl:
"""Tests for the macfilter/maclist parsing in OpenWrtWirelessMixin.get_ssids()."""
def _send(self, cmd, **kw):
if cmd.strip() == "uci show wireless":
return UCI_WIRELESS_ACL
return ""
def test_whitelist_mode_parsed(self, driver):
driver._send_command = self._send
result = driver.get_ssids()
assert result["CorpWiFi"]["acl_mode"] == "whitelist"
def test_maclist_multiple_entries_parsed(self, driver):
"""UCI list values repeat the same key across lines — must not overwrite."""
driver._send_command = self._send
result = driver.get_ssids()
assert result["CorpWiFi"]["mac_list"] == ["AA:BB:CC:DD:EE:01", "AA:BB:CC:DD:EE:02"]
def test_acl_mode_merged_across_radios(self, driver):
"""Only wifi-iface[0] has macfilter set; wifi-iface[1] (same SSID) must inherit it."""
driver._send_command = self._send
result = driver.get_ssids()
# Both wifi-iface sections belong to CorpWiFi — the merged result carries one acl_mode.
assert result["CorpWiFi"]["acl_mode"] == "whitelist"
assert result["CorpWiFi"]["mac_list"] == ["AA:BB:CC:DD:EE:01", "AA:BB:CC:DD:EE:02"]
def test_no_macfilter_defaults_to_off(self, driver):
driver._send_command = self._send
result = driver.get_ssids()
assert result["GuestNet"]["acl_mode"] == "off"
assert result["GuestNet"]["mac_list"] == []
def test_deny_maps_to_blacklist(self, driver):
deny_uci = UCI_WIRELESS_ACL.replace("macfilter='allow'", "macfilter='deny'")
driver._send_command = lambda cmd, **kw: deny_uci if cmd.strip() == "uci show wireless" else ""
result = driver.get_ssids()
assert result["CorpWiFi"]["acl_mode"] == "blacklist"
class TestPushMacAcl:
"""Tests for OpenWrtWirelessMixin.push_mac_acl()."""
def _make_send(self, sections="wireless.@wifi-iface[0]\nwireless.@wifi-iface[1]", ssid_by_section=None):
ssid_by_section = ssid_by_section or {
"wireless.@wifi-iface[0]": "CorpWiFi",
"wireless.@wifi-iface[1]": "GuestNet",
}
issued: list[str] = []
def _send(cmd, **kw):
issued.append(cmd)
if "grep -oE" in cmd and "sort -u" in cmd:
return sections
for sec, ssid in ssid_by_section.items():
if f"uci -q get {sec}.ssid" in cmd:
return ssid
return ""
return _send, issued
def test_whitelist_sets_macfilter_allow(self, driver):
send, issued = self._make_send()
driver._send_command = send
driver.push_mac_acl("CorpWiFi", "whitelist", ["AA:BB:CC:DD:EE:01"])
assert any("wireless.@wifi-iface[0].macfilter='allow'" in c for c in issued)
def test_blacklist_sets_macfilter_deny(self, driver):
send, issued = self._make_send()
driver._send_command = send
driver.push_mac_acl("CorpWiFi", "blacklist", ["AA:BB:CC:DD:EE:01"])
assert any("wireless.@wifi-iface[0].macfilter='deny'" in c for c in issued)
def test_off_sets_macfilter_disable_and_clears_maclist(self, driver):
send, issued = self._make_send()
driver._send_command = send
driver.push_mac_acl("CorpWiFi", "off", [])
assert any("wireless.@wifi-iface[0].macfilter='disable'" in c for c in issued)
assert any("delete wireless.@wifi-iface[0].maclist" in c for c in issued)
assert not any("add_list wireless.@wifi-iface[0].maclist" in c for c in issued)
def test_maclist_entries_added(self, driver):
send, issued = self._make_send()
driver._send_command = send
driver.push_mac_acl("CorpWiFi", "whitelist", ["AA:BB:CC:DD:EE:01", "AA:BB:CC:DD:EE:02"])
assert any("add_list wireless.@wifi-iface[0].maclist='AA:BB:CC:DD:EE:01'" in c for c in issued)
assert any("add_list wireless.@wifi-iface[0].maclist='AA:BB:CC:DD:EE:02'" in c for c in issued)
def test_maclist_cleared_before_readd(self, driver):
"""Full-rebuild: existing maclist must be deleted before new entries are added."""
send, issued = self._make_send()
driver._send_command = send
driver.push_mac_acl("CorpWiFi", "whitelist", ["AA:BB:CC:DD:EE:01"])
delete_idx = next(i for i, c in enumerate(issued) if "delete wireless.@wifi-iface[0].maclist" in c)
add_idx = next(i for i, c in enumerate(issued) if "add_list wireless.@wifi-iface[0].maclist" in c)
assert delete_idx < add_idx
def test_only_matching_ssid_sections_touched(self, driver):
"""GuestNet section must not be modified when pushing CorpWiFi's ACL."""
send, issued = self._make_send()
driver._send_command = send
driver.push_mac_acl("CorpWiFi", "whitelist", ["AA:BB:CC:DD:EE:01"])
assert not any("wireless.@wifi-iface[1].macfilter" in c for c in issued)
def test_issues_uci_commit_and_wifi_reload(self, driver):
send, issued = self._make_send()
driver._send_command = send
driver.push_mac_acl("CorpWiFi", "whitelist", ["AA:BB:CC:DD:EE:01"])
assert any("uci commit wireless" in c for c in issued)
assert any(c.strip() == "wifi reload" for c in issued)