mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2025-04-21 12:17:50 +02:00
Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4ded663e68 | ||
![]() |
c00bc3a065 | ||
![]() |
f469db8a95 | ||
![]() |
c3a27354ff | ||
![]() |
d927fd07c1 | ||
![]() |
8dff156eb6 | ||
![]() |
e5c41a8024 | ||
![]() |
603d6b2881 | ||
![]() |
d1aa4009bc | ||
![]() |
3c5081c7a6 | ||
![]() |
7d5bcd738b | ||
![]() |
0a93c55552 | ||
![]() |
7f9ea7640d | ||
![]() |
c52d913f20 | ||
![]() |
651b647570 | ||
![]() |
8cf9734d2b | ||
![]() |
d70fbade44 | ||
![]() |
f0e515b6ff | ||
![]() |
47ef40d645 | ||
![]() |
9e10efbef5 | ||
![]() |
224afbb9ca | ||
![]() |
bf69e6210c | ||
![]() |
258482d509 | ||
![]() |
1caad4ca88 | ||
![]() |
59464a0ca4 | ||
![]() |
93b25c87d6 | ||
![]() |
e971ef71d5 |
@ -1,3 +1,8 @@
|
|||||||
|
ver 3.6:
|
||||||
|
Fix issue with handling blacklisting and roaming requests.
|
||||||
|
Fix issue with handling CQM thresholds for FullMAC devices.
|
||||||
|
Add support for PMKSA when using FullMAC devices.
|
||||||
|
|
||||||
ver 3.5:
|
ver 3.5:
|
||||||
Add support for option to disable blacklist handling.
|
Add support for option to disable blacklist handling.
|
||||||
Add support for option to disable SAE for broken drivers.
|
Add support for option to disable SAE for broken drivers.
|
||||||
|
@ -441,7 +441,7 @@ unit_tests += unit/test-cmac-aes \
|
|||||||
unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \
|
unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \
|
||||||
unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \
|
unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \
|
||||||
unit/test-dpp unit/test-json unit/test-nl80211util \
|
unit/test-dpp unit/test-json unit/test-nl80211util \
|
||||||
unit/test-pmksa
|
unit/test-pmksa unit/test-storage
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if CLIENT
|
if CLIENT
|
||||||
@ -605,6 +605,11 @@ unit_test_nl80211util_LDADD = $(ell_ldadd)
|
|||||||
unit_test_pmksa_SOURCES = unit/test-pmksa.c src/pmksa.c src/pmksa.h \
|
unit_test_pmksa_SOURCES = unit/test-pmksa.c src/pmksa.c src/pmksa.h \
|
||||||
src/module.h src/util.h
|
src/module.h src/util.h
|
||||||
unit_test_pmksa_LDADD = $(ell_ldadd)
|
unit_test_pmksa_LDADD = $(ell_ldadd)
|
||||||
|
|
||||||
|
unit_test_storage_SOURCES = unit/test-storage.c src/storage.c src/storage.h \
|
||||||
|
src/crypto.c src/crypto.h \
|
||||||
|
src/common.c src/common.h
|
||||||
|
unit_test_storage_LDADD = $(ell_ldadd)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if CLIENT
|
if CLIENT
|
||||||
|
@ -11,52 +11,58 @@ from iwd import NetworkType
|
|||||||
from hostapd import HostapdCLI
|
from hostapd import HostapdCLI
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
|
def initial_connection(self):
|
||||||
def validate(self, expect_roam=True):
|
ordered_network = self.device.get_ordered_network('TestAPRoam')
|
||||||
wd = IWD()
|
|
||||||
|
|
||||||
devices = wd.list_devices(1)
|
|
||||||
device = devices[0]
|
|
||||||
|
|
||||||
ordered_network = device.get_ordered_network('TestAPRoam')
|
|
||||||
|
|
||||||
self.assertEqual(ordered_network.type, NetworkType.psk)
|
self.assertEqual(ordered_network.type, NetworkType.psk)
|
||||||
|
|
||||||
condition = 'not obj.connected'
|
condition = 'not obj.connected'
|
||||||
wd.wait_for_object_condition(ordered_network.network_object, condition)
|
self.wd.wait_for_object_condition(ordered_network.network_object, condition)
|
||||||
|
|
||||||
device.connect_bssid(self.bss_hostapd[0].bssid)
|
self.device.connect_bssid(self.bss_hostapd[0].bssid)
|
||||||
|
|
||||||
condition = 'obj.state == DeviceState.connected'
|
condition = 'obj.state == DeviceState.connected'
|
||||||
wd.wait_for_object_condition(device, condition)
|
self.wd.wait_for_object_condition(self.device, condition)
|
||||||
|
|
||||||
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED')
|
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED')
|
||||||
|
|
||||||
self.assertFalse(self.bss_hostapd[1].list_sta())
|
self.assertFalse(self.bss_hostapd[1].list_sta())
|
||||||
|
|
||||||
self.bss_hostapd[0].send_bss_transition(device.address,
|
def validate_roam(self, from_bss, to_bss, expect_roam=True):
|
||||||
[(self.bss_hostapd[1].bssid, '8f0000005102060603000000')],
|
from_bss.send_bss_transition(self.device.address,
|
||||||
|
self.neighbor_list,
|
||||||
disassoc_imminent=expect_roam)
|
disassoc_imminent=expect_roam)
|
||||||
|
|
||||||
if expect_roam:
|
if expect_roam:
|
||||||
from_condition = 'obj.state == DeviceState.roaming'
|
from_condition = 'obj.state == DeviceState.roaming'
|
||||||
to_condition = 'obj.state == DeviceState.connected'
|
to_condition = 'obj.state == DeviceState.connected'
|
||||||
wd.wait_for_object_change(device, from_condition, to_condition)
|
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
|
||||||
|
|
||||||
self.bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % device.address)
|
to_bss.wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
|
||||||
else:
|
else:
|
||||||
device.wait_for_event("no-roam-candidates")
|
self.device.wait_for_event("no-roam-candidates")
|
||||||
|
|
||||||
device.disconnect()
|
|
||||||
|
|
||||||
condition = 'not obj.connected'
|
|
||||||
wd.wait_for_object_condition(ordered_network.network_object, condition)
|
|
||||||
|
|
||||||
def test_disassoc_imminent(self):
|
def test_disassoc_imminent(self):
|
||||||
self.validate(expect_roam=True)
|
self.initial_connection()
|
||||||
|
self.validate_roam(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
|
||||||
def test_no_candidates(self):
|
def test_no_candidates(self):
|
||||||
self.validate(expect_roam=False)
|
self.initial_connection()
|
||||||
|
# We now have BSS0 roam blacklisted
|
||||||
|
self.validate_roam(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
# Try and trigger another roam back, which shouldn't happen since now
|
||||||
|
# both BSS's are roam blacklisted
|
||||||
|
self.validate_roam(self.bss_hostapd[1], self.bss_hostapd[0], expect_roam=False)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.wd = IWD(True)
|
||||||
|
|
||||||
|
devices = self.wd.list_devices(1)
|
||||||
|
self.device = devices[0]
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.wd = None
|
||||||
|
self.device = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@ -65,6 +71,10 @@ class Test(unittest.TestCase):
|
|||||||
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
|
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
|
||||||
HostapdCLI(config='ssid2.conf'),
|
HostapdCLI(config='ssid2.conf'),
|
||||||
HostapdCLI(config='ssid3.conf') ]
|
HostapdCLI(config='ssid3.conf') ]
|
||||||
|
cls.neighbor_list = [
|
||||||
|
(cls.bss_hostapd[0].bssid, "8f0000005101060603000000"),
|
||||||
|
(cls.bss_hostapd[1].bssid, "8f0000005102060603000000"),
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
[SETUP]
|
[SETUP]
|
||||||
num_radios=4
|
num_radios=4
|
||||||
|
hwsim_medium=true
|
||||||
|
start_iwd=false
|
||||||
|
|
||||||
[HOSTAPD]
|
[HOSTAPD]
|
||||||
rad0=ssid1.conf
|
rad0=ssid1.conf
|
||||||
|
6
autotests/testAPRoam/main.conf.roaming
Normal file
6
autotests/testAPRoam/main.conf.roaming
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[General]
|
||||||
|
RoamThreshold=-72
|
||||||
|
CriticalRoamThreshold=-72
|
||||||
|
|
||||||
|
[Blacklist]
|
||||||
|
InitialRoamRequestedTimeout=20
|
183
autotests/testAPRoam/roam_blacklist_test.py
Normal file
183
autotests/testAPRoam/roam_blacklist_test.py
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append('../util')
|
||||||
|
import iwd
|
||||||
|
from iwd import IWD, IWD_CONFIG_DIR
|
||||||
|
from iwd import NetworkType
|
||||||
|
|
||||||
|
from hostapd import HostapdCLI
|
||||||
|
from hwsim import Hwsim
|
||||||
|
|
||||||
|
class Test(unittest.TestCase):
|
||||||
|
def validate_connected(self, hostapd):
|
||||||
|
ordered_network = self.device.get_ordered_network('TestAPRoam')
|
||||||
|
|
||||||
|
self.assertEqual(ordered_network.type, NetworkType.psk)
|
||||||
|
|
||||||
|
condition = 'not obj.connected'
|
||||||
|
self.wd.wait_for_object_condition(ordered_network.network_object, condition)
|
||||||
|
|
||||||
|
self.device.connect_bssid(hostapd.bssid)
|
||||||
|
|
||||||
|
condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_condition(self.device, condition)
|
||||||
|
|
||||||
|
hostapd.wait_for_event('AP-STA-CONNECTED')
|
||||||
|
|
||||||
|
def validate_ap_roamed(self, from_hostapd, to_hostapd):
|
||||||
|
from_hostapd.send_bss_transition(
|
||||||
|
self.device.address, self.neighbor_list, disassoc_imminent=True
|
||||||
|
)
|
||||||
|
|
||||||
|
from_condition = 'obj.state == DeviceState.roaming'
|
||||||
|
to_condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
|
||||||
|
|
||||||
|
to_hostapd.wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
|
||||||
|
|
||||||
|
self.device.wait_for_event("ap-roam-blacklist-added")
|
||||||
|
|
||||||
|
def test_roam_to_optimal_candidates(self):
|
||||||
|
# In this test IWD will naturally transition down the list after each
|
||||||
|
# BSS gets roam blacklisted. All BSS's are above the RSSI thresholds.
|
||||||
|
self.rule_ssid1.signal = -5000
|
||||||
|
self.rule_ssid2.signal = -6500
|
||||||
|
self.rule_ssid3.signal = -6900
|
||||||
|
|
||||||
|
# Connect to BSS0
|
||||||
|
self.validate_connected(self.bss_hostapd[0])
|
||||||
|
|
||||||
|
# AP directed roam to BSS1
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
|
||||||
|
# AP directed roam to BSS2
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[1], self.bss_hostapd[2])
|
||||||
|
|
||||||
|
def test_avoiding_under_threshold_bss(self):
|
||||||
|
# In this test IWD will blacklist BSS0, then roam the BSS1. BSS1 will
|
||||||
|
# then tell IWD to roam, but it should go back to BSS0 since the only
|
||||||
|
# non-blacklisted BSS is under the roam threshold.
|
||||||
|
self.rule_ssid1.signal = -5000
|
||||||
|
self.rule_ssid2.signal = -6500
|
||||||
|
self.rule_ssid3.signal = -7300
|
||||||
|
|
||||||
|
# Connect to BSS0
|
||||||
|
self.validate_connected(self.bss_hostapd[0])
|
||||||
|
|
||||||
|
# AP directed roam to BSS1
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
|
||||||
|
# AP directed roam, but IWD should choose BSS0 since BSS2 is -73dB
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[1], self.bss_hostapd[0])
|
||||||
|
|
||||||
|
def test_connect_to_roam_blacklisted_bss(self):
|
||||||
|
# In this test a BSS will be roam blacklisted, but all other options are
|
||||||
|
# below the RSSI threshold so IWD should roam back to the blacklisted
|
||||||
|
# BSS.
|
||||||
|
self.rule_ssid1.signal = -5000
|
||||||
|
self.rule_ssid2.signal = -8000
|
||||||
|
self.rule_ssid3.signal = -8500
|
||||||
|
|
||||||
|
# Connect to BSS0
|
||||||
|
self.validate_connected(self.bss_hostapd[0])
|
||||||
|
|
||||||
|
# AP directed roam, should connect to BSS1 as its the next best
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
|
||||||
|
# Connected to BSS1, but the signal is bad, so IWD should try to roam
|
||||||
|
# again. BSS0 is still blacklisted, but its the only reasonable option
|
||||||
|
# since both BSS1 and BSS2 are below the set RSSI threshold (-72dB)
|
||||||
|
|
||||||
|
from_condition = 'obj.state == DeviceState.roaming'
|
||||||
|
to_condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
|
||||||
|
|
||||||
|
# IWD should have connected to BSS0, even though its roam blacklisted
|
||||||
|
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
|
||||||
|
|
||||||
|
def test_blacklist_during_roam_scan(self):
|
||||||
|
# Tests that an AP roam request mid-roam results in the AP still being
|
||||||
|
# blacklisted even though the request itself doesn't directly trigger
|
||||||
|
# a roam.
|
||||||
|
self.rule_ssid1.signal = -7300
|
||||||
|
self.rule_ssid2.signal = -7500
|
||||||
|
self.rule_ssid3.signal = -8500
|
||||||
|
|
||||||
|
# Connect to BSS0 under the roam threshold so IWD will immediately try
|
||||||
|
# roaming elsewhere
|
||||||
|
self.validate_connected(self.bss_hostapd[0])
|
||||||
|
|
||||||
|
self.device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
self.bss_hostapd[0].send_bss_transition(
|
||||||
|
self.device.address, self.neighbor_list, disassoc_imminent=True
|
||||||
|
)
|
||||||
|
self.device.wait_for_event("ap-roam-blacklist-added")
|
||||||
|
|
||||||
|
# BSS0 should have gotten blacklisted even though IWD was mid-roam,
|
||||||
|
# causing IWD to choose BSS1 when it gets is results.
|
||||||
|
|
||||||
|
from_condition = 'obj.state == DeviceState.roaming'
|
||||||
|
to_condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
|
||||||
|
|
||||||
|
self.bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.wd = IWD(True)
|
||||||
|
|
||||||
|
devices = self.wd.list_devices(1)
|
||||||
|
self.device = devices[0]
|
||||||
|
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.wd = None
|
||||||
|
self.device = None
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
IWD.copy_to_storage("main.conf.roaming", IWD_CONFIG_DIR, "main.conf")
|
||||||
|
IWD.copy_to_storage('TestAPRoam.psk')
|
||||||
|
hwsim = Hwsim()
|
||||||
|
|
||||||
|
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
|
||||||
|
HostapdCLI(config='ssid2.conf'),
|
||||||
|
HostapdCLI(config='ssid3.conf') ]
|
||||||
|
HostapdCLI.group_neighbors(*cls.bss_hostapd)
|
||||||
|
|
||||||
|
rad0 = hwsim.get_radio('rad0')
|
||||||
|
rad1 = hwsim.get_radio('rad1')
|
||||||
|
rad2 = hwsim.get_radio('rad2')
|
||||||
|
|
||||||
|
cls.neighbor_list = [
|
||||||
|
(cls.bss_hostapd[0].bssid, "8f0000005101060603000000"),
|
||||||
|
(cls.bss_hostapd[1].bssid, "8f0000005102060603000000"),
|
||||||
|
(cls.bss_hostapd[2].bssid, "8f0000005103060603000000"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
cls.rule_ssid1 = hwsim.rules.create()
|
||||||
|
cls.rule_ssid1.source = rad0.addresses[0]
|
||||||
|
cls.rule_ssid1.bidirectional = True
|
||||||
|
cls.rule_ssid1.enabled = True
|
||||||
|
|
||||||
|
cls.rule_ssid2 = hwsim.rules.create()
|
||||||
|
cls.rule_ssid2.source = rad1.addresses[0]
|
||||||
|
cls.rule_ssid2.bidirectional = True
|
||||||
|
cls.rule_ssid2.enabled = True
|
||||||
|
|
||||||
|
cls.rule_ssid3 = hwsim.rules.create()
|
||||||
|
cls.rule_ssid3.source = rad2.addresses[0]
|
||||||
|
cls.rule_ssid3.bidirectional = True
|
||||||
|
cls.rule_ssid3.enabled = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
IWD.clear_storage()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(exit=True)
|
2
autotests/testBSSBlacklist/TestBlacklist.psk
Normal file
2
autotests/testBSSBlacklist/TestBlacklist.psk
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[Security]
|
||||||
|
Passphrase=secret123
|
@ -260,12 +260,69 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
self.wd.unregister_psk_agent(psk_agent)
|
self.wd.unregister_psk_agent(psk_agent)
|
||||||
|
|
||||||
|
def test_blacklist_disabled(self):
|
||||||
|
wd = self.wd
|
||||||
|
bss_hostapd = self.bss_hostapd
|
||||||
|
|
||||||
|
rule0 = self.rule0
|
||||||
|
rule1 = self.rule1
|
||||||
|
rule2 = self.rule2
|
||||||
|
|
||||||
|
psk_agent = PSKAgent(["secret123", 'secret123'])
|
||||||
|
wd.register_psk_agent(psk_agent)
|
||||||
|
|
||||||
|
devices = wd.list_devices(1)
|
||||||
|
device = devices[0]
|
||||||
|
|
||||||
|
rule0.drop = True
|
||||||
|
rule0.enabled = True
|
||||||
|
|
||||||
|
device.autoconnect = True
|
||||||
|
|
||||||
|
condition = 'obj.state == DeviceState.connected'
|
||||||
|
wd.wait_for_object_condition(device, condition)
|
||||||
|
|
||||||
|
ordered_network = device.get_ordered_network("TestBlacklist", full_scan=True)
|
||||||
|
|
||||||
|
self.assertEqual(ordered_network.type, NetworkType.psk)
|
||||||
|
|
||||||
|
# The first BSS should fail, and we should connect to the second. This
|
||||||
|
# should not result in a connection blacklist though since its disabled.
|
||||||
|
bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % device.address)
|
||||||
|
|
||||||
|
device.disconnect()
|
||||||
|
|
||||||
|
rule0.drop = False
|
||||||
|
device.autoconnect = True
|
||||||
|
|
||||||
|
# Verify the first BSS wasn't blacklisted.
|
||||||
|
bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % device.address)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
_, _, name = self.id().split(".")
|
||||||
|
|
||||||
|
# TODO: If we have this pattern elsewhere it might be nice to turn this
|
||||||
|
# into a decorator e.g.
|
||||||
|
#
|
||||||
|
# @config("main.conf.disabled")
|
||||||
|
# @profile("TestBlacklist.psk")
|
||||||
|
# def test_blacklist_disabled(self)
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
if name == "test_blacklist_disabled":
|
||||||
|
IWD.copy_to_storage("main.conf.disabled", IWD_CONFIG_DIR, "main.conf")
|
||||||
|
IWD.copy_to_storage("TestBlacklist.psk")
|
||||||
|
else:
|
||||||
|
IWD.copy_to_storage("main.conf.default", IWD_CONFIG_DIR, "main.conf")
|
||||||
|
|
||||||
self.wd = IWD(True)
|
self.wd = IWD(True)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
IWD.clear_storage()
|
IWD.clear_storage()
|
||||||
self.wd = None
|
self.wd = None
|
||||||
|
self.rule0.drop = False
|
||||||
|
self.rule1.drop = False
|
||||||
|
self.rule2.drop = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
2
autotests/testBSSBlacklist/main.conf.disabled
Normal file
2
autotests/testBSSBlacklist/main.conf.disabled
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[Blacklist]
|
||||||
|
InitialTimeout=0
|
@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ([2.69])
|
AC_PREREQ([2.69])
|
||||||
AC_INIT([iwd],[3.5])
|
AC_INIT([iwd],[3.6])
|
||||||
|
|
||||||
AC_CONFIG_HEADERS(config.h)
|
AC_CONFIG_HEADERS(config.h)
|
||||||
AC_CONFIG_AUX_DIR(build-aux)
|
AC_CONFIG_AUX_DIR(build-aux)
|
||||||
|
@ -1915,7 +1915,7 @@ static void print_ie_interworking(unsigned int level,
|
|||||||
size--;
|
size--;
|
||||||
ptr++;
|
ptr++;
|
||||||
|
|
||||||
if (!size)
|
if (size < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -7433,7 +7433,7 @@ static bool check_pcap(struct nlmon *nlmon, size_t next_size)
|
|||||||
|
|
||||||
pcap_close(nlmon->pcap);
|
pcap_close(nlmon->pcap);
|
||||||
|
|
||||||
/* Exausted the single PCAP file */
|
/* Exhausted the single PCAP file */
|
||||||
if (nlmon->max_files < 2) {
|
if (nlmon->max_files < 2) {
|
||||||
printf("Reached maximum size of PCAP, exiting\n");
|
printf("Reached maximum size of PCAP, exiting\n");
|
||||||
nlmon->pcap = NULL;
|
nlmon->pcap = NULL;
|
||||||
|
107
src/blacklist.c
107
src/blacklist.c
@ -45,22 +45,42 @@
|
|||||||
|
|
||||||
static uint64_t blacklist_multiplier;
|
static uint64_t blacklist_multiplier;
|
||||||
static uint64_t blacklist_initial_timeout;
|
static uint64_t blacklist_initial_timeout;
|
||||||
|
static uint64_t blacklist_roam_initial_timeout;
|
||||||
static uint64_t blacklist_max_timeout;
|
static uint64_t blacklist_max_timeout;
|
||||||
|
|
||||||
struct blacklist_entry {
|
struct blacklist_entry {
|
||||||
uint8_t addr[6];
|
uint8_t addr[6];
|
||||||
uint64_t added_time;
|
uint64_t added_time;
|
||||||
uint64_t expire_time;
|
uint64_t expire_time;
|
||||||
|
enum blacklist_reason reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct blacklist_search {
|
||||||
|
const uint8_t *addr;
|
||||||
|
enum blacklist_reason reason;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct l_queue *blacklist;
|
static struct l_queue *blacklist;
|
||||||
|
|
||||||
|
static uint64_t get_reason_timeout(enum blacklist_reason reason)
|
||||||
|
{
|
||||||
|
switch (reason) {
|
||||||
|
case BLACKLIST_REASON_CONNECT_FAILED:
|
||||||
|
return blacklist_initial_timeout;
|
||||||
|
case BLACKLIST_REASON_ROAM_REQUESTED:
|
||||||
|
return blacklist_roam_initial_timeout;
|
||||||
|
default:
|
||||||
|
l_warn("Unhandled blacklist reason: %u", reason);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool check_if_expired(void *data, void *user_data)
|
static bool check_if_expired(void *data, void *user_data)
|
||||||
{
|
{
|
||||||
struct blacklist_entry *entry = data;
|
struct blacklist_entry *entry = data;
|
||||||
uint64_t now = l_get_u64(user_data);
|
uint64_t now = l_get_u64(user_data);
|
||||||
|
|
||||||
if (l_time_diff(now, entry->added_time) > blacklist_max_timeout) {
|
if (l_time_after(now, entry->expire_time)) {
|
||||||
l_debug("Removing entry "MAC" on prune", MAC_STR(entry->addr));
|
l_debug("Removing entry "MAC" on prune", MAC_STR(entry->addr));
|
||||||
l_free(entry);
|
l_free(entry);
|
||||||
return true;
|
return true;
|
||||||
@ -87,20 +107,53 @@ static bool match_addr(const void *a, const void *b)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blacklist_add_bss(const uint8_t *addr)
|
static bool match_addr_and_reason(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct blacklist_entry *entry = a;
|
||||||
|
const struct blacklist_search *search = b;
|
||||||
|
|
||||||
|
if (entry->reason != search->reason)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!memcmp(entry->addr, search->addr, 6))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason)
|
||||||
{
|
{
|
||||||
struct blacklist_entry *entry;
|
struct blacklist_entry *entry;
|
||||||
|
uint64_t timeout;
|
||||||
if (!blacklist_initial_timeout)
|
|
||||||
return;
|
|
||||||
|
|
||||||
blacklist_prune();
|
blacklist_prune();
|
||||||
|
|
||||||
|
timeout = get_reason_timeout(reason);
|
||||||
|
if (!timeout)
|
||||||
|
return;
|
||||||
|
|
||||||
entry = l_queue_find(blacklist, match_addr, addr);
|
entry = l_queue_find(blacklist, match_addr, addr);
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
uint64_t offset = l_time_diff(entry->added_time,
|
uint64_t offset;
|
||||||
entry->expire_time);
|
|
||||||
|
if (reason < entry->reason) {
|
||||||
|
l_debug("Promoting "MAC" blacklist to reason %u",
|
||||||
|
MAC_STR(addr), reason);
|
||||||
|
/* Reset this to the new timeout and reason */
|
||||||
|
entry->reason = reason;
|
||||||
|
entry->added_time = l_time_now();
|
||||||
|
entry->expire_time = l_time_offset(entry->added_time,
|
||||||
|
timeout);
|
||||||
|
return;
|
||||||
|
} else if (reason > entry->reason) {
|
||||||
|
l_debug("Ignoring blacklist extension of "MAC", "
|
||||||
|
"current blacklist status is more severe!",
|
||||||
|
MAC_STR(addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = l_time_diff(entry->added_time, entry->expire_time);
|
||||||
|
|
||||||
offset *= blacklist_multiplier;
|
offset *= blacklist_multiplier;
|
||||||
|
|
||||||
@ -115,40 +168,36 @@ void blacklist_add_bss(const uint8_t *addr)
|
|||||||
entry = l_new(struct blacklist_entry, 1);
|
entry = l_new(struct blacklist_entry, 1);
|
||||||
|
|
||||||
entry->added_time = l_time_now();
|
entry->added_time = l_time_now();
|
||||||
entry->expire_time = l_time_offset(entry->added_time,
|
entry->expire_time = l_time_offset(entry->added_time, timeout);
|
||||||
blacklist_initial_timeout);
|
entry->reason = reason;
|
||||||
memcpy(entry->addr, addr, 6);
|
memcpy(entry->addr, addr, 6);
|
||||||
|
|
||||||
l_queue_push_tail(blacklist, entry);
|
l_queue_push_tail(blacklist, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool blacklist_contains_bss(const uint8_t *addr)
|
bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason)
|
||||||
{
|
{
|
||||||
bool ret;
|
struct blacklist_search search = {
|
||||||
uint64_t time_now;
|
.addr = addr,
|
||||||
struct blacklist_entry *entry;
|
.reason = reason
|
||||||
|
};
|
||||||
|
|
||||||
blacklist_prune();
|
blacklist_prune();
|
||||||
|
|
||||||
entry = l_queue_find(blacklist, match_addr, addr);
|
return l_queue_find(blacklist, match_addr_and_reason, &search) != NULL;
|
||||||
|
|
||||||
if (!entry)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
time_now = l_time_now();
|
|
||||||
|
|
||||||
ret = l_time_after(time_now, entry->expire_time) ? false : true;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void blacklist_remove_bss(const uint8_t *addr)
|
void blacklist_remove_bss(const uint8_t *addr, enum blacklist_reason reason)
|
||||||
{
|
{
|
||||||
struct blacklist_entry *entry;
|
struct blacklist_entry *entry;
|
||||||
|
struct blacklist_search search = {
|
||||||
|
.addr = addr,
|
||||||
|
.reason = reason
|
||||||
|
};
|
||||||
|
|
||||||
blacklist_prune();
|
blacklist_prune();
|
||||||
|
|
||||||
entry = l_queue_remove_if(blacklist, match_addr, addr);
|
entry = l_queue_remove_if(blacklist, match_addr_and_reason, &search);
|
||||||
|
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return;
|
return;
|
||||||
@ -167,6 +216,14 @@ static int blacklist_init(void)
|
|||||||
/* For easier user configuration the timeout values are in seconds */
|
/* For easier user configuration the timeout values are in seconds */
|
||||||
blacklist_initial_timeout *= L_USEC_PER_SEC;
|
blacklist_initial_timeout *= L_USEC_PER_SEC;
|
||||||
|
|
||||||
|
if (!l_settings_get_uint64(config, "Blacklist",
|
||||||
|
"InitialRoamRequestedTimeout",
|
||||||
|
&blacklist_roam_initial_timeout))
|
||||||
|
blacklist_roam_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
|
||||||
|
|
||||||
|
/* For easier user configuration the timeout values are in seconds */
|
||||||
|
blacklist_roam_initial_timeout *= L_USEC_PER_SEC;
|
||||||
|
|
||||||
if (!l_settings_get_uint64(config, "Blacklist",
|
if (!l_settings_get_uint64(config, "Blacklist",
|
||||||
"Multiplier",
|
"Multiplier",
|
||||||
&blacklist_multiplier))
|
&blacklist_multiplier))
|
||||||
|
@ -20,6 +20,21 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void blacklist_add_bss(const uint8_t *addr);
|
enum blacklist_reason {
|
||||||
bool blacklist_contains_bss(const uint8_t *addr);
|
/*
|
||||||
void blacklist_remove_bss(const uint8_t *addr);
|
* When a BSS is blacklisted using this reason IWD will refuse to
|
||||||
|
* connect to it via autoconnect
|
||||||
|
*/
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED,
|
||||||
|
/*
|
||||||
|
* This type of blacklist is added when a BSS requests IWD roams
|
||||||
|
* elsewhere. This is to aid in preventing IWD from roaming/connecting
|
||||||
|
* back to that BSS in the future unless there are no other "good"
|
||||||
|
* candidates to connect to.
|
||||||
|
*/
|
||||||
|
BLACKLIST_REASON_ROAM_REQUESTED,
|
||||||
|
};
|
||||||
|
|
||||||
|
void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason);
|
||||||
|
bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason);
|
||||||
|
void blacklist_remove_bss(const uint8_t *addr, enum blacklist_reason reason);
|
||||||
|
@ -1166,21 +1166,34 @@ struct dpp_uri_info *dpp_parse_uri(const char *uri)
|
|||||||
|
|
||||||
switch (*pos) {
|
switch (*pos) {
|
||||||
case 'C':
|
case 'C':
|
||||||
|
if (L_WARN_ON(info->freqs))
|
||||||
|
goto free_info;
|
||||||
|
|
||||||
info->freqs = dpp_parse_class_and_channel(pos + 2, len);
|
info->freqs = dpp_parse_class_and_channel(pos + 2, len);
|
||||||
if (!info->freqs)
|
if (!info->freqs)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
|
if (L_WARN_ON(!l_memeqzero(info->mac,
|
||||||
|
sizeof(info->mac))))
|
||||||
|
goto free_info;
|
||||||
|
|
||||||
ret = dpp_parse_mac(pos + 2, len, info->mac);
|
ret = dpp_parse_mac(pos + 2, len, info->mac);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
|
if (L_WARN_ON(info->version != 0))
|
||||||
|
goto free_info;
|
||||||
|
|
||||||
ret = dpp_parse_version(pos + 2, len, &info->version);
|
ret = dpp_parse_version(pos + 2, len, &info->version);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
break;
|
break;
|
||||||
case 'K':
|
case 'K':
|
||||||
|
if (L_WARN_ON(info->boot_public))
|
||||||
|
goto free_info;
|
||||||
|
|
||||||
info->boot_public = dpp_parse_key(pos + 2, len);
|
info->boot_public = dpp_parse_key(pos + 2, len);
|
||||||
if (!info->boot_public)
|
if (!info->boot_public)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
|
@ -544,7 +544,8 @@ static bool eap_mschapv2_load_settings(struct eap_state *eap,
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
free(state);
|
l_free(state->user);
|
||||||
|
l_free(state);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1239,7 +1239,7 @@ static struct pmksa *handshake_state_steal_pmksa(struct handshake_state *s)
|
|||||||
s->have_pmksa = false;
|
s->have_pmksa = false;
|
||||||
|
|
||||||
if (l_time_after(now, pmksa->expiration)) {
|
if (l_time_after(now, pmksa->expiration)) {
|
||||||
l_free(pmksa);
|
pmksa_cache_free(pmksa);
|
||||||
pmksa = NULL;
|
pmksa = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1280,7 +1280,7 @@ void handshake_state_cache_pmksa(struct handshake_state *s)
|
|||||||
l_debug("Caching PMKSA for "MAC, MAC_STR(s->aa));
|
l_debug("Caching PMKSA for "MAC, MAC_STR(s->aa));
|
||||||
|
|
||||||
if (L_WARN_ON(pmksa_cache_put(pmksa) < 0))
|
if (L_WARN_ON(pmksa_cache_put(pmksa) < 0))
|
||||||
l_free(pmksa);
|
pmksa_cache_free(pmksa);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handshake_state_remove_pmksa(struct handshake_state *s)
|
bool handshake_state_remove_pmksa(struct handshake_state *s)
|
||||||
@ -1294,7 +1294,7 @@ bool handshake_state_remove_pmksa(struct handshake_state *s)
|
|||||||
if (!pmksa)
|
if (!pmksa)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
l_free(pmksa);
|
pmksa_cache_free(pmksa);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -292,6 +292,12 @@ control how long a misbehaved BSS spends on the blacklist.
|
|||||||
|
|
||||||
The initial time that a BSS spends on the blacklist. Setting this to zero
|
The initial time that a BSS spends on the blacklist. Setting this to zero
|
||||||
will disable blacklisting functionality in IWD.
|
will disable blacklisting functionality in IWD.
|
||||||
|
* - InitialRoamRequestedTimeout
|
||||||
|
- Values: uint64 value in seconds (default: **30**)
|
||||||
|
|
||||||
|
The initial time that a BSS will be marked after a BSS requests a roam.
|
||||||
|
This is to aid in avoiding roaming back to BSS's which are likely
|
||||||
|
overloaded. Setting this to zero will disabled this form of blacklisting.
|
||||||
* - Multiplier
|
* - Multiplier
|
||||||
- Values: unsigned int value greater than zero, in seconds
|
- Values: unsigned int value greater than zero, in seconds
|
||||||
(default: **30**)
|
(default: **30**)
|
||||||
|
142
src/netdev.c
142
src/netdev.c
@ -463,6 +463,14 @@ uint8_t netdev_get_rssi_level_idx(struct netdev *netdev)
|
|||||||
return netdev->cur_rssi_level_idx;
|
return netdev->cur_rssi_level_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int netdev_get_low_signal_threshold(uint32_t frequency)
|
||||||
|
{
|
||||||
|
if (frequency > 4000)
|
||||||
|
return LOW_SIGNAL_THRESHOLD_5GHZ;
|
||||||
|
|
||||||
|
return LOW_SIGNAL_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
static void netdev_set_powered_result(int error, uint16_t type,
|
static void netdev_set_powered_result(int error, uint16_t type,
|
||||||
const void *data,
|
const void *data,
|
||||||
uint32_t len, void *user_data)
|
uint32_t len, void *user_data)
|
||||||
@ -1101,6 +1109,7 @@ static void netdev_free(void *data)
|
|||||||
l_timeout_remove(netdev->rssi_poll_timeout);
|
l_timeout_remove(netdev->rssi_poll_timeout);
|
||||||
|
|
||||||
scan_wdev_remove(netdev->wdev_id);
|
scan_wdev_remove(netdev->wdev_id);
|
||||||
|
frame_watch_wdev_remove(netdev->wdev_id);
|
||||||
|
|
||||||
watchlist_destroy(&netdev->station_watches);
|
watchlist_destroy(&netdev->station_watches);
|
||||||
|
|
||||||
@ -1498,6 +1507,105 @@ static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
|
|||||||
handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err);
|
handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool netdev_match_addr(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct netdev *netdev = a;
|
||||||
|
const uint8_t *addr = b;
|
||||||
|
|
||||||
|
return memcmp(netdev->addr, addr, ETH_ALEN) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct netdev *netdev_find_by_address(const uint8_t *addr)
|
||||||
|
{
|
||||||
|
return l_queue_find(netdev_list, netdev_match_addr, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_pmksa_driver_add(const struct pmksa *pmksa)
|
||||||
|
{
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
struct netdev *netdev = netdev_find_by_address(pmksa->spa);
|
||||||
|
uint32_t expiration = (uint32_t)pmksa->expiration;
|
||||||
|
|
||||||
|
if (!netdev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Only need to set the PMKSA into the kernel for fullmac drivers */
|
||||||
|
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
|
||||||
|
return;
|
||||||
|
|
||||||
|
l_debug("Adding PMKSA to kernel");
|
||||||
|
|
||||||
|
msg = l_genl_msg_new(NL80211_CMD_SET_PMKSA);
|
||||||
|
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PMKID, 16, pmksa->pmkid);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, pmksa->aa);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
|
||||||
|
pmksa->ssid_len, pmksa->ssid);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK_LIFETIME, 4, &expiration);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK,
|
||||||
|
pmksa->pmk_len, pmksa->pmk);
|
||||||
|
|
||||||
|
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
|
||||||
|
l_error("error sending SET_PMKSA");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_pmksa_driver_remove(const struct pmksa *pmksa)
|
||||||
|
{
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
struct netdev *netdev = netdev_find_by_address(pmksa->spa);
|
||||||
|
|
||||||
|
if (!netdev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Only need to set the PMKSA into the kernel for fullmac drivers */
|
||||||
|
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
|
||||||
|
return;
|
||||||
|
|
||||||
|
l_debug("Removing PMKSA from kernel");
|
||||||
|
|
||||||
|
msg = l_genl_msg_new(NL80211_CMD_DEL_PMKSA);
|
||||||
|
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PMKID, 16, pmksa->pmkid);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, pmksa->aa);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
|
||||||
|
pmksa->ssid_len, pmksa->ssid);
|
||||||
|
|
||||||
|
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
|
||||||
|
l_error("error sending DEL_PMKSA");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_flush_pmksa(struct netdev *netdev)
|
||||||
|
{
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only utilize the kernel's PMKSA cache for fullmac cards,
|
||||||
|
* so no need to flush if this is a softmac.
|
||||||
|
*/
|
||||||
|
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
|
||||||
|
return;
|
||||||
|
|
||||||
|
msg = l_genl_msg_new(NL80211_CMD_FLUSH_PMKSA);
|
||||||
|
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||||
|
|
||||||
|
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
|
||||||
|
l_error("Failed to flush PMKSA for %u", netdev->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_pmksa_driver_flush(void)
|
||||||
|
{
|
||||||
|
const struct l_queue_entry *e;
|
||||||
|
|
||||||
|
for (e = l_queue_get_entries(netdev_list); e; e = e->next) {
|
||||||
|
struct netdev *netdev = e->data;
|
||||||
|
|
||||||
|
netdev_flush_pmksa(netdev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void try_handshake_complete(struct netdev_handshake_state *nhs)
|
static void try_handshake_complete(struct netdev_handshake_state *nhs)
|
||||||
{
|
{
|
||||||
l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u",
|
l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u",
|
||||||
@ -3816,6 +3924,15 @@ static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
|
|||||||
static int netdev_cqm_rssi_update(struct netdev *netdev)
|
static int netdev_cqm_rssi_update(struct netdev *netdev)
|
||||||
{
|
{
|
||||||
struct l_genl_msg *msg;
|
struct l_genl_msg *msg;
|
||||||
|
struct netdev_handshake_state *nhs = l_container_of(netdev->handshake,
|
||||||
|
struct netdev_handshake_state, super);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fullmac cards handle roaming in firmware, there is no need to set
|
||||||
|
* CQM thresholds
|
||||||
|
*/
|
||||||
|
if (nhs->type == CONNECTION_TYPE_FULLMAC)
|
||||||
|
return 0;
|
||||||
|
|
||||||
l_debug("");
|
l_debug("");
|
||||||
|
|
||||||
@ -5504,23 +5621,18 @@ static void netdev_external_auth_event(struct l_genl_msg *msg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action == NL80211_EXTERNAL_AUTH_ABORT) {
|
if (action == NL80211_EXTERNAL_AUTH_ABORT) {
|
||||||
iwd_notice(IWD_NOTICE_CONNECT_INFO, "External Auth Aborted");
|
l_warn("External Auth Aborted");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
iwd_notice(IWD_NOTICE_CONNECT_INFO,
|
|
||||||
"External Auth to SSID: %s, bssid: "MAC,
|
|
||||||
util_ssid_to_utf8(ssid.iov_len, ssid.iov_base),
|
|
||||||
MAC_STR(bssid));
|
|
||||||
|
|
||||||
if (hs->ssid_len != ssid.iov_len ||
|
if (hs->ssid_len != ssid.iov_len ||
|
||||||
memcmp(hs->ssid, ssid.iov_base, hs->ssid_len)) {
|
memcmp(hs->ssid, ssid.iov_base, hs->ssid_len)) {
|
||||||
iwd_notice(IWD_NOTICE_CONNECT_INFO, "Target SSID mismatch");
|
l_warn("Target SSID mismatch");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(hs->aa, bssid, ETH_ALEN)) {
|
if (memcmp(hs->aa, bssid, ETH_ALEN)) {
|
||||||
iwd_notice(IWD_NOTICE_CONNECT_INFO, "Target BSSID mismatch");
|
l_warn("Target BSSID mismatch");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6529,6 +6641,16 @@ struct netdev *netdev_create_from_genl(struct l_genl_msg *msg,
|
|||||||
|
|
||||||
netdev_get_link(netdev);
|
netdev_get_link(netdev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the netdev-specific variant to flush only this devices PMKSA
|
||||||
|
* cache in the kernel. This will make IWD's cache and the kernel's
|
||||||
|
* cache consistent, i.e. no entries
|
||||||
|
*
|
||||||
|
* TODO: If we ever are storing PMKSA's on disk we would first need to
|
||||||
|
* flush, then add all the PMKSA entries at this time.
|
||||||
|
*/
|
||||||
|
netdev_flush_pmksa(netdev);
|
||||||
|
|
||||||
return netdev;
|
return netdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6644,6 +6766,10 @@ static int netdev_init(void)
|
|||||||
|
|
||||||
__ft_set_tx_frame_func(netdev_tx_ft_frame);
|
__ft_set_tx_frame_func(netdev_tx_ft_frame);
|
||||||
|
|
||||||
|
__pmksa_set_driver_callbacks(netdev_pmksa_driver_add,
|
||||||
|
netdev_pmksa_driver_remove,
|
||||||
|
netdev_pmksa_driver_flush);
|
||||||
|
|
||||||
unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
|
unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
|
||||||
netdev_unicast_notify,
|
netdev_unicast_notify,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
|
@ -158,6 +158,7 @@ const char *netdev_get_name(struct netdev *netdev);
|
|||||||
bool netdev_get_is_up(struct netdev *netdev);
|
bool netdev_get_is_up(struct netdev *netdev);
|
||||||
const char *netdev_get_path(struct netdev *netdev);
|
const char *netdev_get_path(struct netdev *netdev);
|
||||||
uint8_t netdev_get_rssi_level_idx(struct netdev *netdev);
|
uint8_t netdev_get_rssi_level_idx(struct netdev *netdev);
|
||||||
|
int netdev_get_low_signal_threshold(uint32_t frequency);
|
||||||
|
|
||||||
struct handshake_state *netdev_handshake_state_new(struct netdev *netdev);
|
struct handshake_state *netdev_handshake_state_new(struct netdev *netdev);
|
||||||
struct handshake_state *netdev_get_handshake(struct netdev *netdev);
|
struct handshake_state *netdev_get_handshake(struct netdev *netdev);
|
||||||
|
@ -1280,7 +1280,8 @@ struct scan_bss *network_bss_select(struct network *network,
|
|||||||
if (l_queue_find(network->blacklist, match_bss, bss))
|
if (l_queue_find(network->blacklist, match_bss, bss))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (blacklist_contains_bss(bss->addr))
|
if (blacklist_contains_bss(bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* OWE Transition BSS */
|
/* OWE Transition BSS */
|
||||||
|
38
src/pmksa.c
38
src/pmksa.c
@ -40,6 +40,9 @@
|
|||||||
|
|
||||||
static uint64_t dot11RSNAConfigPMKLifetime = 43200ULL * L_USEC_PER_SEC;
|
static uint64_t dot11RSNAConfigPMKLifetime = 43200ULL * L_USEC_PER_SEC;
|
||||||
static uint32_t pmksa_cache_capacity = 255;
|
static uint32_t pmksa_cache_capacity = 255;
|
||||||
|
static pmksa_cache_add_func_t driver_add;
|
||||||
|
static pmksa_cache_remove_func_t driver_remove;
|
||||||
|
static pmksa_cache_flush_func_t driver_flush;
|
||||||
|
|
||||||
struct min_heap {
|
struct min_heap {
|
||||||
struct pmksa **data;
|
struct pmksa **data;
|
||||||
@ -142,7 +145,7 @@ int pmksa_cache_put(struct pmksa *pmksa)
|
|||||||
l_debug("Adding entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
|
l_debug("Adding entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
|
||||||
|
|
||||||
if (cache.used == cache.capacity) {
|
if (cache.used == cache.capacity) {
|
||||||
l_free(cache.data[0]);
|
pmksa_cache_free(cache.data[0]);
|
||||||
cache.data[0] = pmksa;
|
cache.data[0] = pmksa;
|
||||||
__minheap_sift_down(cache.data, cache.used, 0, &ops);
|
__minheap_sift_down(cache.data, cache.used, 0, &ops);
|
||||||
return 0;
|
return 0;
|
||||||
@ -152,6 +155,9 @@ int pmksa_cache_put(struct pmksa *pmksa)
|
|||||||
__minheap_sift_up(cache.data, cache.used, &ops);
|
__minheap_sift_up(cache.data, cache.used, &ops);
|
||||||
cache.used += 1;
|
cache.used += 1;
|
||||||
|
|
||||||
|
if (driver_add)
|
||||||
|
driver_add(pmksa);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +173,7 @@ int pmksa_cache_expire(uint64_t cutoff)
|
|||||||
|
|
||||||
for (i = 0; i < used; i++) {
|
for (i = 0; i < used; i++) {
|
||||||
if (cache.data[i]->expiration <= cutoff) {
|
if (cache.data[i]->expiration <= cutoff) {
|
||||||
l_free(cache.data[i]);
|
pmksa_cache_free(cache.data[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,11 +196,30 @@ int pmksa_cache_flush(void)
|
|||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The driver flush operation is done via a single kernel API call which
|
||||||
|
* is why below we use l_free instead of pmksa_cache_free as to not
|
||||||
|
* induce a DEL_PMKSA kernel call for each entry.
|
||||||
|
*/
|
||||||
|
if (driver_flush)
|
||||||
|
driver_flush();
|
||||||
|
|
||||||
for (i = 0; i < cache.used; i++)
|
for (i = 0; i < cache.used; i++)
|
||||||
l_free(cache.data[i]);
|
l_free(cache.data[i]);
|
||||||
|
|
||||||
memset(cache.data, 0, cache.capacity * sizeof(struct pmksa *));
|
memset(cache.data, 0, cache.capacity * sizeof(struct pmksa *));
|
||||||
cache.used = 0;
|
cache.used = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pmksa_cache_free(struct pmksa *pmksa)
|
||||||
|
{
|
||||||
|
if (driver_remove)
|
||||||
|
driver_remove(pmksa);
|
||||||
|
|
||||||
|
l_free(pmksa);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +242,15 @@ void __pmksa_set_config(const struct l_settings *config)
|
|||||||
&pmksa_cache_capacity);
|
&pmksa_cache_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __pmksa_set_driver_callbacks(pmksa_cache_add_func_t add,
|
||||||
|
pmksa_cache_remove_func_t remove,
|
||||||
|
pmksa_cache_flush_func_t flush)
|
||||||
|
{
|
||||||
|
driver_add = add;
|
||||||
|
driver_remove = remove;
|
||||||
|
driver_flush = flush;
|
||||||
|
}
|
||||||
|
|
||||||
static int pmksa_init(void)
|
static int pmksa_init(void)
|
||||||
{
|
{
|
||||||
cache.capacity = pmksa_cache_capacity;
|
cache.capacity = pmksa_cache_capacity;
|
||||||
|
@ -32,6 +32,10 @@ struct pmksa {
|
|||||||
size_t pmk_len;
|
size_t pmk_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef void (*pmksa_cache_add_func_t)(const struct pmksa *pmksa);
|
||||||
|
typedef void (*pmksa_cache_remove_func_t)(const struct pmksa *pmksa);
|
||||||
|
typedef void (*pmksa_cache_flush_func_t)(void);
|
||||||
|
|
||||||
struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries);
|
struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries);
|
||||||
|
|
||||||
struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
|
struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
|
||||||
@ -41,6 +45,11 @@ struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
|
|||||||
int pmksa_cache_put(struct pmksa *pmksa);
|
int pmksa_cache_put(struct pmksa *pmksa);
|
||||||
int pmksa_cache_expire(uint64_t cutoff);
|
int pmksa_cache_expire(uint64_t cutoff);
|
||||||
int pmksa_cache_flush(void);
|
int pmksa_cache_flush(void);
|
||||||
|
int pmksa_cache_free(struct pmksa *pmksa);
|
||||||
|
|
||||||
uint64_t pmksa_lifetime(void);
|
uint64_t pmksa_lifetime(void);
|
||||||
void __pmksa_set_config(const struct l_settings *config);
|
void __pmksa_set_config(const struct l_settings *config);
|
||||||
|
|
||||||
|
void __pmksa_set_driver_callbacks(pmksa_cache_add_func_t add,
|
||||||
|
pmksa_cache_remove_func_t remove,
|
||||||
|
pmksa_cache_flush_func_t flush);
|
||||||
|
@ -143,9 +143,9 @@ struct scan_survey {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct scan_survey_results {
|
struct scan_survey_results {
|
||||||
struct scan_survey survey_2_4[14];
|
struct scan_survey survey_2_4[15];
|
||||||
struct scan_survey survey_5[196];
|
struct scan_survey survey_5[197];
|
||||||
struct scan_survey survey_6[233];
|
struct scan_survey survey_6[234];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct scan_results {
|
struct scan_results {
|
||||||
|
135
src/station.c
135
src/station.c
@ -155,6 +155,55 @@ struct anqp_entry {
|
|||||||
uint32_t pending;
|
uint32_t pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rather than sorting BSS's purely based on ranking a higher level grouping
|
||||||
|
* is used. The purpose of this higher order grouping is the consider the BSS's
|
||||||
|
* roam blacklist status. The roam blacklist is a "soft" blacklist in that we
|
||||||
|
* still should connect to these BSS's if they are the only "good" option.
|
||||||
|
* The question here is: what makes a BSS "good" vs "bad".
|
||||||
|
*
|
||||||
|
* For an initial (probably naive) approach here we can use the
|
||||||
|
* RoamThreshold[5G] which indicates the signal level that would not
|
||||||
|
* be of an acceptable connection quality. BSS can then be sorted either
|
||||||
|
* above or below this threshold. Within each of these groups a BSS may be
|
||||||
|
* blacklisted, meaning it should get sorted lower on the list compared to
|
||||||
|
* others within the same group.
|
||||||
|
*
|
||||||
|
* This sorting is achieved by extending rank to a uint32_t where the first 16
|
||||||
|
* bits are the standard rank calculated by scan.c. Above that bits can be
|
||||||
|
* reserved for this higher level grouping:
|
||||||
|
*
|
||||||
|
* Bit 16 indicates the BSS is not blacklisted
|
||||||
|
* Bit 17 indicates the BSS is above the critical signal threshold
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ABOVE_THRESHOLD_BIT 17
|
||||||
|
#define NOT_BLACKLISTED_BIT 16
|
||||||
|
|
||||||
|
static uint32_t evaluate_bss_group_rank(const uint8_t *addr, uint32_t freq,
|
||||||
|
int16_t signal_strength, uint16_t rank)
|
||||||
|
{
|
||||||
|
int signal = signal_strength / 100;
|
||||||
|
bool roam_blacklist;
|
||||||
|
bool good_signal;
|
||||||
|
uint32_t rank_out = (uint32_t) rank;
|
||||||
|
|
||||||
|
if (blacklist_contains_bss(addr, BLACKLIST_REASON_CONNECT_FAILED))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
roam_blacklist = blacklist_contains_bss(addr,
|
||||||
|
BLACKLIST_REASON_ROAM_REQUESTED);
|
||||||
|
good_signal = signal >= netdev_get_low_signal_threshold(freq);
|
||||||
|
|
||||||
|
if (good_signal)
|
||||||
|
set_bit(&rank_out, ABOVE_THRESHOLD_BIT);
|
||||||
|
|
||||||
|
if (!roam_blacklist)
|
||||||
|
set_bit(&rank_out, NOT_BLACKLISTED_BIT);
|
||||||
|
|
||||||
|
return rank_out;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used as entries for the roam list since holding scan_bss pointers directly
|
* Used as entries for the roam list since holding scan_bss pointers directly
|
||||||
* from station->bss_list is not 100% safe due to the possibility of the
|
* from station->bss_list is not 100% safe due to the possibility of the
|
||||||
@ -162,13 +211,13 @@ struct anqp_entry {
|
|||||||
*/
|
*/
|
||||||
struct roam_bss {
|
struct roam_bss {
|
||||||
uint8_t addr[6];
|
uint8_t addr[6];
|
||||||
uint16_t rank;
|
uint32_t rank;
|
||||||
int32_t signal_strength;
|
int32_t signal_strength;
|
||||||
bool ft_failed: 1;
|
bool ft_failed: 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct roam_bss *roam_bss_from_scan_bss(const struct scan_bss *bss,
|
static struct roam_bss *roam_bss_from_scan_bss(const struct scan_bss *bss,
|
||||||
uint16_t rank)
|
uint32_t rank)
|
||||||
{
|
{
|
||||||
struct roam_bss *rbss = l_new(struct roam_bss, 1);
|
struct roam_bss *rbss = l_new(struct roam_bss, 1);
|
||||||
|
|
||||||
@ -2805,7 +2854,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
struct handshake_state *hs = netdev_get_handshake(station->netdev);
|
struct handshake_state *hs = netdev_get_handshake(station->netdev);
|
||||||
struct scan_bss *current_bss = station->connected_bss;
|
struct scan_bss *current_bss = station->connected_bss;
|
||||||
struct scan_bss *bss;
|
struct scan_bss *bss;
|
||||||
double cur_bss_rank = 0.0;
|
uint32_t cur_bss_group_rank = 0;
|
||||||
static const double RANK_FT_FACTOR = 1.3;
|
static const double RANK_FT_FACTOR = 1.3;
|
||||||
uint16_t mdid;
|
uint16_t mdid;
|
||||||
enum security orig_security, security;
|
enum security orig_security, security;
|
||||||
@ -2834,10 +2883,15 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
*/
|
*/
|
||||||
bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr);
|
bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr);
|
||||||
if (bss && !station->ap_directed_roaming) {
|
if (bss && !station->ap_directed_roaming) {
|
||||||
cur_bss_rank = bss->rank;
|
double cur_bss_rank = bss->rank;
|
||||||
|
|
||||||
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
|
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
|
||||||
cur_bss_rank *= RANK_FT_FACTOR;
|
cur_bss_rank *= RANK_FT_FACTOR;
|
||||||
|
|
||||||
|
cur_bss_group_rank = evaluate_bss_group_rank(bss->addr,
|
||||||
|
bss->frequency,
|
||||||
|
bss->signal_strength,
|
||||||
|
(uint16_t) cur_bss_rank);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2859,6 +2913,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
while ((bss = l_queue_pop_head(bss_list))) {
|
while ((bss = l_queue_pop_head(bss_list))) {
|
||||||
double rank;
|
double rank;
|
||||||
struct roam_bss *rbss;
|
struct roam_bss *rbss;
|
||||||
|
uint32_t group_rank;
|
||||||
|
|
||||||
station_print_scan_bss(bss);
|
station_print_scan_bss(bss);
|
||||||
|
|
||||||
@ -2880,7 +2935,8 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
if (network_can_connect_bss(network, bss) < 0)
|
if (network_can_connect_bss(network, bss) < 0)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
if (blacklist_contains_bss(bss->addr))
|
if (blacklist_contains_bss(bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED))
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
rank = bss->rank;
|
rank = bss->rank;
|
||||||
@ -2888,7 +2944,11 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
|
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
|
||||||
rank *= RANK_FT_FACTOR;
|
rank *= RANK_FT_FACTOR;
|
||||||
|
|
||||||
if (rank <= cur_bss_rank)
|
group_rank = evaluate_bss_group_rank(bss->addr, bss->frequency,
|
||||||
|
bss->signal_strength,
|
||||||
|
(uint16_t) rank);
|
||||||
|
|
||||||
|
if (group_rank <= cur_bss_group_rank)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2897,7 +2957,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
*/
|
*/
|
||||||
station_update_roam_bss(station, bss);
|
station_update_roam_bss(station, bss);
|
||||||
|
|
||||||
rbss = roam_bss_from_scan_bss(bss, rank);
|
rbss = roam_bss_from_scan_bss(bss, group_rank);
|
||||||
|
|
||||||
l_queue_insert(station->roam_bss_list, rbss,
|
l_queue_insert(station->roam_bss_list, rbss,
|
||||||
roam_bss_rank_compare, NULL);
|
roam_bss_rank_compare, NULL);
|
||||||
@ -3165,12 +3225,10 @@ static void station_ap_directed_roam(struct station *station,
|
|||||||
uint8_t req_mode;
|
uint8_t req_mode;
|
||||||
uint16_t dtimer;
|
uint16_t dtimer;
|
||||||
uint8_t valid_interval;
|
uint8_t valid_interval;
|
||||||
|
bool can_roam = !station_cannot_roam(station);
|
||||||
|
|
||||||
l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
|
l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
|
||||||
|
|
||||||
if (station_cannot_roam(station))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (station->state != STATION_STATE_CONNECTED) {
|
if (station->state != STATION_STATE_CONNECTED) {
|
||||||
l_debug("roam: unexpected AP directed roam -- ignore");
|
l_debug("roam: unexpected AP directed roam -- ignore");
|
||||||
return;
|
return;
|
||||||
@ -3234,8 +3292,13 @@ static void station_ap_directed_roam(struct station *station,
|
|||||||
* disassociating us. If either of these bits are set, set the
|
* disassociating us. If either of these bits are set, set the
|
||||||
* ap_directed_roaming flag. Otherwise still try roaming but don't
|
* ap_directed_roaming flag. Otherwise still try roaming but don't
|
||||||
* treat it any different than a normal roam.
|
* treat it any different than a normal roam.
|
||||||
|
*
|
||||||
|
* The only exception here is if we are in the middle of roaming
|
||||||
|
* (can_roam == false) since we cannot reliably know if the roam scan
|
||||||
|
* included frequencies from potential candidates in this request,
|
||||||
|
* forcing a roam in this case might result in unintended behavior.
|
||||||
*/
|
*/
|
||||||
if (req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT |
|
if (can_roam && req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT |
|
||||||
WNM_REQUEST_MODE_TERMINATION_IMMINENT |
|
WNM_REQUEST_MODE_TERMINATION_IMMINENT |
|
||||||
WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT))
|
WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT))
|
||||||
station->ap_directed_roaming = true;
|
station->ap_directed_roaming = true;
|
||||||
@ -3262,6 +3325,19 @@ static void station_ap_directed_roam(struct station *station,
|
|||||||
pos += url_len;
|
pos += url_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blacklist_add_bss(station->connected_bss->addr,
|
||||||
|
BLACKLIST_REASON_ROAM_REQUESTED);
|
||||||
|
station_debug_event(station, "ap-roam-blacklist-added");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validating the frame and blacklisting should still be done even if
|
||||||
|
* we are mid-roam. Its important to track the BSS requesting the
|
||||||
|
* transition so when the current roam completes IWD will be less likely
|
||||||
|
* to roam back to the current BSS.
|
||||||
|
*/
|
||||||
|
if (!can_roam)
|
||||||
|
return;
|
||||||
|
|
||||||
station->preparing_roam = true;
|
station->preparing_roam = true;
|
||||||
|
|
||||||
l_timeout_remove(station->roam_trigger_timeout);
|
l_timeout_remove(station->roam_trigger_timeout);
|
||||||
@ -3400,7 +3476,15 @@ static bool station_retry_with_reason(struct station *station,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
blacklist_add_bss(station->connected_bss->addr);
|
blacklist_add_bss(station->connected_bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Network blacklist the BSS as well, since the timeout blacklist could
|
||||||
|
* be disabled
|
||||||
|
*/
|
||||||
|
network_blacklist_add(station->connected_network,
|
||||||
|
station->connected_bss);
|
||||||
|
|
||||||
try_next:
|
try_next:
|
||||||
return station_try_next_bss(station);
|
return station_try_next_bss(station);
|
||||||
@ -3449,6 +3533,10 @@ static bool station_pmksa_fallback(struct station *station, uint16_t status)
|
|||||||
static bool station_retry_with_status(struct station *station,
|
static bool station_retry_with_status(struct station *station,
|
||||||
uint16_t status_code)
|
uint16_t status_code)
|
||||||
{
|
{
|
||||||
|
/* If PMKSA failed don't blacklist so we can retry this BSS */
|
||||||
|
if (station_pmksa_fallback(station, status_code))
|
||||||
|
goto try_next;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Certain Auth/Assoc failures should not cause a timeout blacklist.
|
* Certain Auth/Assoc failures should not cause a timeout blacklist.
|
||||||
* In these cases we want to only temporarily blacklist the BSS until
|
* In these cases we want to only temporarily blacklist the BSS until
|
||||||
@ -3459,12 +3547,19 @@ static bool station_retry_with_status(struct station *station,
|
|||||||
* specific BSS on our next attempt. There is currently no way to
|
* specific BSS on our next attempt. There is currently no way to
|
||||||
* obtain that IE, but this should be done in the future.
|
* obtain that IE, but this should be done in the future.
|
||||||
*/
|
*/
|
||||||
if (IS_TEMPORARY_STATUS(status_code))
|
if (!IS_TEMPORARY_STATUS(status_code))
|
||||||
network_blacklist_add(station->connected_network,
|
blacklist_add_bss(station->connected_bss->addr,
|
||||||
station->connected_bss);
|
BLACKLIST_REASON_CONNECT_FAILED);
|
||||||
else if (!station_pmksa_fallback(station, status_code))
|
|
||||||
blacklist_add_bss(station->connected_bss->addr);
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unconditionally network blacklist the BSS if we are retrying. This
|
||||||
|
* will allow network_bss_select to traverse the BSS list and ignore
|
||||||
|
* BSS's which have previously failed
|
||||||
|
*/
|
||||||
|
network_blacklist_add(station->connected_network,
|
||||||
|
station->connected_bss);
|
||||||
|
|
||||||
|
try_next:
|
||||||
iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code);
|
iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code);
|
||||||
|
|
||||||
return station_try_next_bss(station);
|
return station_try_next_bss(station);
|
||||||
@ -3549,7 +3644,8 @@ static void station_connect_cb(struct netdev *netdev, enum netdev_result result,
|
|||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case NETDEV_RESULT_OK:
|
case NETDEV_RESULT_OK:
|
||||||
blacklist_remove_bss(station->connected_bss->addr);
|
blacklist_remove_bss(station->connected_bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED);
|
||||||
station_connect_ok(station);
|
station_connect_ok(station);
|
||||||
return;
|
return;
|
||||||
case NETDEV_RESULT_DISCONNECTED:
|
case NETDEV_RESULT_DISCONNECTED:
|
||||||
@ -4740,7 +4836,8 @@ static struct l_dbus_message *station_property_set_affinities(
|
|||||||
return dbus_error_invalid_args(message);
|
return dbus_error_invalid_args(message);
|
||||||
|
|
||||||
/* Get first entry, there should be only one */
|
/* Get first entry, there should be only one */
|
||||||
l_dbus_message_iter_next_entry(&array, &new_path);
|
if (!l_dbus_message_iter_next_entry(&array, &new_path))
|
||||||
|
return dbus_error_invalid_args(message);
|
||||||
|
|
||||||
if (l_dbus_message_iter_next_entry(&array, &new_path))
|
if (l_dbus_message_iter_next_entry(&array, &new_path))
|
||||||
return dbus_error_invalid_args(message);
|
return dbus_error_invalid_args(message);
|
||||||
|
@ -500,6 +500,13 @@ int __storage_decrypt(struct l_settings *settings, const char *ssid,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It should likely be far larger than this, but that will get caught
|
||||||
|
* later when reloading the decrypted data.
|
||||||
|
*/
|
||||||
|
if (elen < 16)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AES-SIV automatically verifies the IV (16 bytes) and returns only
|
* AES-SIV automatically verifies the IV (16 bytes) and returns only
|
||||||
* the decrypted data portion. We add one here for the NULL terminator
|
* the decrypted data portion. We add one here for the NULL terminator
|
||||||
|
@ -116,6 +116,29 @@ struct dpp_test_info bad_channels[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dpp_test_info duplicates[] = {
|
||||||
|
/* Duplicate key */
|
||||||
|
{
|
||||||
|
.uri = "DPP:K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;",
|
||||||
|
.expect_fail = true
|
||||||
|
},
|
||||||
|
/* Duplicate frequencies*/
|
||||||
|
{
|
||||||
|
.uri = "DPP:C:81/1,115/36;C:81/1,115/36;;",
|
||||||
|
.expect_fail = true
|
||||||
|
},
|
||||||
|
/* Duplicate MACs*/
|
||||||
|
{
|
||||||
|
.uri = "DPP:M:5254005828e5;M:5254005828e5;;",
|
||||||
|
.expect_fail = true
|
||||||
|
},
|
||||||
|
/* Duplicate versions */
|
||||||
|
{
|
||||||
|
.uri = "DPP:V:2;V:2;;",
|
||||||
|
.expect_fail = true
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static bool verify_info(const struct dpp_uri_info *parsed,
|
static bool verify_info(const struct dpp_uri_info *parsed,
|
||||||
const struct dpp_test_info *result)
|
const struct dpp_test_info *result)
|
||||||
{
|
{
|
||||||
@ -158,6 +181,14 @@ static void test_bad_channels(const void *data)
|
|||||||
test_uri_parse(&bad_channels[i]);
|
test_uri_parse(&bad_channels[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_duplicate_in_uri(const void *data)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < L_ARRAY_SIZE(duplicates); i++)
|
||||||
|
test_uri_parse(&duplicates[i]);
|
||||||
|
}
|
||||||
|
|
||||||
struct dpp_test_vector {
|
struct dpp_test_vector {
|
||||||
/* Initiator values */
|
/* Initiator values */
|
||||||
const char *i_proto_public;
|
const char *i_proto_public;
|
||||||
@ -576,6 +607,7 @@ int main(int argc, char *argv[])
|
|||||||
l_test_add("DPP URI bad key", test_uri_parse, &bad_key);
|
l_test_add("DPP URI bad key", test_uri_parse, &bad_key);
|
||||||
l_test_add("DPP URI bad channels", test_bad_channels, &bad_channels);
|
l_test_add("DPP URI bad channels", test_bad_channels, &bad_channels);
|
||||||
l_test_add("DPP URI unexpected ID", test_uri_parse, &unexpected_id);
|
l_test_add("DPP URI unexpected ID", test_uri_parse, &unexpected_id);
|
||||||
|
l_test_add("DPP URI duplicates", test_duplicate_in_uri, &duplicates);
|
||||||
|
|
||||||
return l_test_run();
|
return l_test_run();
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ static const struct p2p_probe_req_data p2p_probe_req_data_1 = {
|
|||||||
.group_caps = 0,
|
.group_caps = 0,
|
||||||
},
|
},
|
||||||
.listen_channel = {
|
.listen_channel = {
|
||||||
.country = "XX\x04",
|
.country = { 'X', 'X', '\x04' },
|
||||||
.oper_class = 81,
|
.oper_class = 81,
|
||||||
.channel_num = 1,
|
.channel_num = 1,
|
||||||
},
|
},
|
||||||
|
55
unit/test-storage.c
Normal file
55
unit/test-storage.c
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Wireless daemon for Linux
|
||||||
|
*
|
||||||
|
* Copyright (C) 2025 Locus Robotics. All rights reserved.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <ell/ell.h>
|
||||||
|
|
||||||
|
#include "src/storage.h"
|
||||||
|
|
||||||
|
static void test_short_encrypted_bytes(const void *data)
|
||||||
|
{
|
||||||
|
struct l_settings *settings = l_settings_new();
|
||||||
|
bool changed;
|
||||||
|
|
||||||
|
l_settings_set_string(settings, "Security", "EncryptedSecurity", "012345");
|
||||||
|
l_settings_set_string(settings, "Security", "EncryptedSalt", "012345");
|
||||||
|
|
||||||
|
assert(__storage_decrypt(settings, "mySSID", &changed) < 0);
|
||||||
|
l_settings_free(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
l_test_init(&argc, &argv);
|
||||||
|
|
||||||
|
storage_init((const uint8_t *)"abc123", 6);
|
||||||
|
|
||||||
|
l_test_add("/storage/profile encryption",
|
||||||
|
test_short_encrypted_bytes, NULL);
|
||||||
|
|
||||||
|
return l_test_run();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user