mirror of
				https://git.kernel.org/pub/scm/network/wireless/iwd.git
				synced 2025-11-04 08:57:29 +01:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "master" and "3.2" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -68,7 +68,6 @@ unit/test-dpp
 | 
			
		||||
unit/test-json
 | 
			
		||||
unit/test-nl80211util
 | 
			
		||||
unit/test-pmksa
 | 
			
		||||
unit/test-storage
 | 
			
		||||
unit/cert-*.pem
 | 
			
		||||
unit/cert-*.csr
 | 
			
		||||
unit/cert-*.srl
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										35
									
								
								ChangeLog
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								ChangeLog
									
									
									
									
									
								
							@ -1,38 +1,3 @@
 | 
			
		||||
ver 3.10:
 | 
			
		||||
	Fix issue with handling neighbor report on BSS TM request.
 | 
			
		||||
	Fix issue with handling deauth and FT association failure.
 | 
			
		||||
	Fix issue with handling roaming and old frequencies.
 | 
			
		||||
 | 
			
		||||
ver 3.9:
 | 
			
		||||
	Fix issue with Access Point mode and frequency unlocking.
 | 
			
		||||
	Fix issue with network configuration and BSS retry logic.
 | 
			
		||||
	Fix issue with handling busy notification from Access Point.
 | 
			
		||||
	Fix issue with handling P-192, P-224 and P-521 for SAE.
 | 
			
		||||
 | 
			
		||||
ver 3.8:
 | 
			
		||||
	Fix issue with handling unit tests and missing kernel features.
 | 
			
		||||
 | 
			
		||||
ver 3.7:
 | 
			
		||||
	Fix issue with handling length of EncryptedSecurity.
 | 
			
		||||
	Fix issue with handling empty affinities lists.
 | 
			
		||||
	Fix issue with handling survey scanning results.
 | 
			
		||||
	Fix issue with handling duplicate values in DPP URI.
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
	Add support for option to disable blacklist handling.
 | 
			
		||||
	Add support for option to disable SAE for broken drivers.
 | 
			
		||||
 | 
			
		||||
ver 3.4:
 | 
			
		||||
	Add support for the Test Anything Protocol.
 | 
			
		||||
 | 
			
		||||
ver 3.3:
 | 
			
		||||
	Fix issue with handling External Authentication.
 | 
			
		||||
 | 
			
		||||
ver 3.2:
 | 
			
		||||
	Fix issue with GCC 15 and -std=c23 build errors.
 | 
			
		||||
	Add support for using PMKSA over SAE if available.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								Makefile.am
									
									
									
									
									
								
							@ -71,9 +71,7 @@ ell_headers = ell/util.h \
 | 
			
		||||
ell_sources = ell/private.h \
 | 
			
		||||
			ell/missing.h \
 | 
			
		||||
			ell/util.c \
 | 
			
		||||
			ell/test-private.h \
 | 
			
		||||
			ell/test.c \
 | 
			
		||||
			ell/test-dbus.c \
 | 
			
		||||
			ell/strv.c \
 | 
			
		||||
			ell/utf8.c \
 | 
			
		||||
			ell/queue.c \
 | 
			
		||||
@ -274,8 +272,6 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h \
 | 
			
		||||
					src/dpp.c \
 | 
			
		||||
					src/udev.c \
 | 
			
		||||
					src/pmksa.h src/pmksa.c \
 | 
			
		||||
					src/vendor_quirks.h \
 | 
			
		||||
					src/vendor_quirks.c \
 | 
			
		||||
					$(eap_sources) \
 | 
			
		||||
					$(builtin_sources)
 | 
			
		||||
 | 
			
		||||
@ -443,7 +439,7 @@ unit_tests += unit/test-cmac-aes \
 | 
			
		||||
		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-dpp unit/test-json unit/test-nl80211util \
 | 
			
		||||
		unit/test-pmksa unit/test-storage
 | 
			
		||||
		unit/test-pmksa
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if CLIENT
 | 
			
		||||
@ -607,11 +603,6 @@ unit_test_nl80211util_LDADD = $(ell_ldadd)
 | 
			
		||||
unit_test_pmksa_SOURCES = unit/test-pmksa.c src/pmksa.c src/pmksa.h \
 | 
			
		||||
				src/module.h src/util.h
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
if CLIENT
 | 
			
		||||
@ -627,9 +618,6 @@ unit_test_client_SOURCES = unit/test-client.c \
 | 
			
		||||
unit_test_client_LDADD = $(ell_ldadd) $(client_ldadd)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \
 | 
			
		||||
			$(top_srcdir)/build-aux/tap-driver.sh
 | 
			
		||||
 | 
			
		||||
TESTS = $(unit_tests)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = src/genbuiltin src/iwd.service.in src/net.connman.iwd.service \
 | 
			
		||||
 | 
			
		||||
@ -1,156 +0,0 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
import sys
 | 
			
		||||
 | 
			
		||||
sys.path.append('../util')
 | 
			
		||||
import iwd
 | 
			
		||||
from iwd import IWD
 | 
			
		||||
from iwd import NetworkType
 | 
			
		||||
 | 
			
		||||
from hostapd import HostapdCLI
 | 
			
		||||
 | 
			
		||||
class Test(unittest.TestCase):
 | 
			
		||||
    def initial_connection(self):
 | 
			
		||||
        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(self.bss_hostapd[0].bssid)
 | 
			
		||||
 | 
			
		||||
        condition = 'obj.state == DeviceState.connected'
 | 
			
		||||
        self.wd.wait_for_object_condition(self.device, condition)
 | 
			
		||||
 | 
			
		||||
        self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED')
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.bss_hostapd[1].list_sta())
 | 
			
		||||
 | 
			
		||||
    def test_full_scan(self):
 | 
			
		||||
        """
 | 
			
		||||
            Tests that IWD first tries a limited scan, then a full scan after
 | 
			
		||||
            an AP directed roam. After the full scan yields no results IWD
 | 
			
		||||
            should stop trying to roam.
 | 
			
		||||
        """
 | 
			
		||||
        self.initial_connection()
 | 
			
		||||
 | 
			
		||||
        # Disable other APs, so the scans come up empty
 | 
			
		||||
        self.bss_hostapd[1].disable()
 | 
			
		||||
        self.bss_hostapd[2].disable()
 | 
			
		||||
 | 
			
		||||
        # Send a bad candidate list with the BSS TM request which contains a
 | 
			
		||||
        # channel with no AP operating on it.
 | 
			
		||||
        self.bss_hostapd[0].send_bss_transition(
 | 
			
		||||
            self.device.address,
 | 
			
		||||
            [(self.bss_hostapd[1].bssid, "8f0000005105060603000000")]
 | 
			
		||||
        )
 | 
			
		||||
        self.device.wait_for_event("roam-scan-triggered")
 | 
			
		||||
        self.device.wait_for_event("no-roam-candidates")
 | 
			
		||||
        # IWD should then trigger a full scan
 | 
			
		||||
        self.device.wait_for_event("full-roam-scan")
 | 
			
		||||
        self.device.wait_for_event("no-roam-candidates", timeout=30)
 | 
			
		||||
 | 
			
		||||
        # IWD should not trigger a roam again after the above 2 failures.
 | 
			
		||||
        with self.assertRaises(TimeoutError):
 | 
			
		||||
            self.device.wait_for_event("roam-scan-triggered", timeout=60)
 | 
			
		||||
 | 
			
		||||
    def test_bad_candidate_list(self):
 | 
			
		||||
        """
 | 
			
		||||
            Tests behavior when the AP sends a candidate list but the scan
 | 
			
		||||
            finds no BSS's. IWD should fall back to a full scan after.
 | 
			
		||||
        """
 | 
			
		||||
        self.initial_connection()
 | 
			
		||||
 | 
			
		||||
        # Send a bad candidate list with the BSS TM request which contains a
 | 
			
		||||
        # channel with no AP operating on it.
 | 
			
		||||
        self.bss_hostapd[0].send_bss_transition(
 | 
			
		||||
            self.device.address,
 | 
			
		||||
            [(self.bss_hostapd[1].bssid, "8f0000005105060603000000")]
 | 
			
		||||
        )
 | 
			
		||||
        self.device.wait_for_event("roam-scan-triggered")
 | 
			
		||||
        self.device.wait_for_event("no-roam-candidates")
 | 
			
		||||
        # IWD should then trigger a full scan
 | 
			
		||||
        self.device.wait_for_event("full-roam-scan")
 | 
			
		||||
        self.device.wait_for_event("roaming", timeout=30)
 | 
			
		||||
        self.device.wait_for_event("connected")
 | 
			
		||||
 | 
			
		||||
    def test_bad_neighbor_report(self):
 | 
			
		||||
        """
 | 
			
		||||
            Tests behavior when the AP sends no candidate list. IWD should
 | 
			
		||||
            request a neighbor report. If the limited scan yields no BSS's IWD
 | 
			
		||||
            should fall back to a full scan.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Set a bad neighbor (channel that no AP is on) to force the limited
 | 
			
		||||
        # roam scan to fail
 | 
			
		||||
        self.bss_hostapd[0].set_neighbor(
 | 
			
		||||
            self.bss_hostapd[1].bssid,
 | 
			
		||||
            "TestAPRoam",
 | 
			
		||||
            '%s8f000000%s%s060603000000' % (self.bss_hostapd[1].bssid.replace(':', ''), "51", "0b")
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        self.initial_connection()
 | 
			
		||||
 | 
			
		||||
        self.bss_hostapd[0].send_bss_transition(self.device.address, [])
 | 
			
		||||
        self.device.wait_for_event("roam-scan-triggered")
 | 
			
		||||
        # The AP will have sent a neighbor report with a single BSS but on
 | 
			
		||||
        # channel 11 which no AP is on. This should result in a limited scan
 | 
			
		||||
        # picking up no candidates.
 | 
			
		||||
        self.device.wait_for_event("no-roam-candidates", timeout=30)
 | 
			
		||||
        # IWD should then trigger a full scan
 | 
			
		||||
        self.device.wait_for_event("full-roam-scan")
 | 
			
		||||
        self.device.wait_for_event("roaming", timeout=30)
 | 
			
		||||
        self.device.wait_for_event("connected")
 | 
			
		||||
 | 
			
		||||
    def test_ignore_candidate_list_quirk(self):
 | 
			
		||||
        """
 | 
			
		||||
            Tests that IWD ignores the candidate list sent by the AP since its
 | 
			
		||||
            OUI indicates it should be ignored.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Set the OUI so the candidate list should be ignored
 | 
			
		||||
        for hapd in self.bss_hostapd:
 | 
			
		||||
            hapd.set_value('vendor_elements', 'dd0400180a01')
 | 
			
		||||
 | 
			
		||||
        self.initial_connection()
 | 
			
		||||
 | 
			
		||||
        # Send with a candidate list (should be ignored)
 | 
			
		||||
        self.bss_hostapd[0].send_bss_transition(
 | 
			
		||||
            self.device.address,
 | 
			
		||||
            [(self.bss_hostapd[1].bssid, "8f0000005105060603000000")]
 | 
			
		||||
        )
 | 
			
		||||
        # IWD should ignore the list and trigger a full scan since we have not
 | 
			
		||||
        # set any neighbors
 | 
			
		||||
        self.device.wait_for_event("full-roam-scan")
 | 
			
		||||
        self.device.wait_for_event("roaming", timeout=30)
 | 
			
		||||
        self.device.wait_for_event("connected")
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
        for hapd in self.bss_hostapd:
 | 
			
		||||
            hapd.reload()
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpClass(cls):
 | 
			
		||||
        IWD.copy_to_storage('TestAPRoam.psk')
 | 
			
		||||
 | 
			
		||||
        cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
 | 
			
		||||
                            HostapdCLI(config='ssid2.conf'),
 | 
			
		||||
                            HostapdCLI(config='ssid3.conf') ]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def tearDownClass(cls):
 | 
			
		||||
        IWD.clear_storage()
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    unittest.main(exit=True)
 | 
			
		||||
@ -11,58 +11,52 @@ from iwd import NetworkType
 | 
			
		||||
from hostapd import HostapdCLI
 | 
			
		||||
 | 
			
		||||
class Test(unittest.TestCase):
 | 
			
		||||
    def initial_connection(self):
 | 
			
		||||
        ordered_network = self.device.get_ordered_network('TestAPRoam')
 | 
			
		||||
 | 
			
		||||
    def validate(self, expect_roam=True):
 | 
			
		||||
        wd = IWD()
 | 
			
		||||
 | 
			
		||||
        devices = wd.list_devices(1)
 | 
			
		||||
        device = devices[0]
 | 
			
		||||
 | 
			
		||||
        ordered_network = 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)
 | 
			
		||||
        wd.wait_for_object_condition(ordered_network.network_object, condition)
 | 
			
		||||
 | 
			
		||||
        self.device.connect_bssid(self.bss_hostapd[0].bssid)
 | 
			
		||||
        device.connect_bssid(self.bss_hostapd[0].bssid)
 | 
			
		||||
 | 
			
		||||
        condition = 'obj.state == DeviceState.connected'
 | 
			
		||||
        self.wd.wait_for_object_condition(self.device, condition)
 | 
			
		||||
        wd.wait_for_object_condition(device, condition)
 | 
			
		||||
 | 
			
		||||
        self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED')
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.bss_hostapd[1].list_sta())
 | 
			
		||||
 | 
			
		||||
    def validate_roam(self, from_bss, to_bss, expect_roam=True):
 | 
			
		||||
        from_bss.send_bss_transition(self.device.address,
 | 
			
		||||
                self.neighbor_list,
 | 
			
		||||
        self.bss_hostapd[0].send_bss_transition(device.address,
 | 
			
		||||
                [(self.bss_hostapd[1].bssid, '8f0000005102060603000000')],
 | 
			
		||||
                disassoc_imminent=expect_roam)
 | 
			
		||||
 | 
			
		||||
        if expect_roam:
 | 
			
		||||
            from_condition = 'obj.state == DeviceState.roaming'
 | 
			
		||||
            to_condition = 'obj.state == DeviceState.connected'
 | 
			
		||||
            self.wd.wait_for_object_change(self.device, from_condition, to_condition)
 | 
			
		||||
            wd.wait_for_object_change(device, from_condition, to_condition)
 | 
			
		||||
 | 
			
		||||
            to_bss.wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
 | 
			
		||||
            self.bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % device.address)
 | 
			
		||||
        else:
 | 
			
		||||
            self.device.wait_for_event("no-roam-candidates")
 | 
			
		||||
            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):
 | 
			
		||||
        self.initial_connection()
 | 
			
		||||
        self.validate_roam(self.bss_hostapd[0], self.bss_hostapd[1])
 | 
			
		||||
        self.validate(expect_roam=True)
 | 
			
		||||
 | 
			
		||||
    def test_no_candidates(self):
 | 
			
		||||
        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
 | 
			
		||||
        self.validate(expect_roam=False)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpClass(cls):
 | 
			
		||||
@ -71,10 +65,6 @@ class Test(unittest.TestCase):
 | 
			
		||||
        cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
 | 
			
		||||
                            HostapdCLI(config='ssid2.conf'),
 | 
			
		||||
                            HostapdCLI(config='ssid3.conf') ]
 | 
			
		||||
        cls.neighbor_list = [
 | 
			
		||||
            (cls.bss_hostapd[0].bssid, "8f0000005101060603000000"),
 | 
			
		||||
            (cls.bss_hostapd[1].bssid, "8f0000005102060603000000"),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def tearDownClass(cls):
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,5 @@
 | 
			
		||||
[SETUP]
 | 
			
		||||
num_radios=4
 | 
			
		||||
hwsim_medium=true
 | 
			
		||||
start_iwd=false
 | 
			
		||||
 | 
			
		||||
[HOSTAPD]
 | 
			
		||||
rad0=ssid1.conf
 | 
			
		||||
 | 
			
		||||
@ -1,6 +0,0 @@
 | 
			
		||||
[General]
 | 
			
		||||
RoamThreshold=-72
 | 
			
		||||
CriticalRoamThreshold=-72
 | 
			
		||||
 | 
			
		||||
[Blacklist]
 | 
			
		||||
InitialAccessPointBusyTimeout=20
 | 
			
		||||
@ -1,183 +0,0 @@
 | 
			
		||||
#!/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)
 | 
			
		||||
@ -1,2 +0,0 @@
 | 
			
		||||
[Security]
 | 
			
		||||
Passphrase=secret123
 | 
			
		||||
@ -260,69 +260,12 @@ class Test(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        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):
 | 
			
		||||
        _, _, 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)
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        IWD.clear_storage()
 | 
			
		||||
        self.wd = None
 | 
			
		||||
        self.rule0.drop = False
 | 
			
		||||
        self.rule1.drop = False
 | 
			
		||||
        self.rule2.drop = False
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpClass(cls):
 | 
			
		||||
 | 
			
		||||
@ -1,2 +0,0 @@
 | 
			
		||||
[Blacklist]
 | 
			
		||||
InitialTimeout=0
 | 
			
		||||
@ -3,11 +3,7 @@ import sys
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
from scapy.layers.dot11 import *
 | 
			
		||||
from scapy.arch import str2mac
 | 
			
		||||
try:
 | 
			
		||||
    from scapy.arch import get_if_raw_hwaddr
 | 
			
		||||
except:
 | 
			
		||||
    from scapy.arch.unix import get_if_raw_hwaddr
 | 
			
		||||
from scapy.arch import str2mac, get_if_raw_hwaddr
 | 
			
		||||
from time import time, sleep
 | 
			
		||||
from threading import Thread
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -91,11 +91,16 @@ class Test(unittest.TestCase):
 | 
			
		||||
        # using the same static config.  The new client's ACD client should
 | 
			
		||||
        # detect an IP conflict and not allow the device to reach the
 | 
			
		||||
        # "connected" state although the DBus .Connect call will succeed.
 | 
			
		||||
        with self.assertRaises(iwd.FailedEx):
 | 
			
		||||
            ordered_network.network_object.connect(timeout=500)
 | 
			
		||||
 | 
			
		||||
        condition = 'obj.state == DeviceState.disconnected'
 | 
			
		||||
        iwd_ns0_1.wait_for_object_condition(dev2, condition, max_wait=21)
 | 
			
		||||
        ordered_network.network_object.connect()
 | 
			
		||||
        self.assertEqual(dev2.state, iwd.DeviceState.connecting)
 | 
			
		||||
        try:
 | 
			
		||||
            # We should either stay in "connecting" indefinitely or move to
 | 
			
		||||
            # "disconnecting"
 | 
			
		||||
            condition = 'obj.state != DeviceState.connecting'
 | 
			
		||||
            iwd_ns0_1.wait_for_object_condition(dev2, condition, max_wait=21)
 | 
			
		||||
            self.assertEqual(dev2.state, iwd.DeviceState.disconnecting)
 | 
			
		||||
        except TimeoutError:
 | 
			
		||||
            dev2.disconnect()
 | 
			
		||||
 | 
			
		||||
        iwd_ns0_1.unregister_psk_agent(psk_agent_ns0_1)
 | 
			
		||||
        del dev2
 | 
			
		||||
 | 
			
		||||
@ -8,8 +8,6 @@ from iwd import PSKAgent
 | 
			
		||||
from iwd import NetworkType
 | 
			
		||||
 | 
			
		||||
class Test(unittest.TestCase):
 | 
			
		||||
    def connect_failure(self, ex):
 | 
			
		||||
        self.failure_triggered = True
 | 
			
		||||
 | 
			
		||||
    def test_netconfig_timeout(self):
 | 
			
		||||
        IWD.copy_to_storage('autoconnect.psk', name='ap-ns1.psk')
 | 
			
		||||
@ -29,34 +27,23 @@ class Test(unittest.TestCase):
 | 
			
		||||
        condition = 'not obj.connected'
 | 
			
		||||
        wd.wait_for_object_condition(ordered_network.network_object, condition)
 | 
			
		||||
 | 
			
		||||
        self.failure_triggered = False
 | 
			
		||||
        ordered_network.network_object.connect()
 | 
			
		||||
 | 
			
		||||
        # Set our error handler here so we can check if it fails
 | 
			
		||||
        ordered_network.network_object.connect(
 | 
			
		||||
            wait=False,
 | 
			
		||||
            timeout=1000,
 | 
			
		||||
            error_handler=self.connect_failure
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # IWD should attempt to try both BSS's with both failing netconfig.
 | 
			
		||||
        # Then the autoconnect list should be exhausted, and IWD should
 | 
			
		||||
        # transition to a disconnected state, then proceed to full autoconnect.
 | 
			
		||||
        device.wait_for_event("netconfig-failed", timeout=1000)
 | 
			
		||||
        device.wait_for_event("netconfig-failed", timeout=1000)
 | 
			
		||||
        device.wait_for_event("disconnected")
 | 
			
		||||
 | 
			
		||||
        device.wait_for_event("autoconnect_full")
 | 
			
		||||
 | 
			
		||||
        # The connect call should have failed
 | 
			
		||||
        self.assertTrue(self.failure_triggered)
 | 
			
		||||
 | 
			
		||||
        condition = "obj.scanning"
 | 
			
		||||
        wd.wait_for_object_condition(device, condition)
 | 
			
		||||
        condition = "not obj.scanning"
 | 
			
		||||
        condition = 'obj.state == DeviceState.connecting'
 | 
			
		||||
        wd.wait_for_object_condition(device, condition)
 | 
			
		||||
 | 
			
		||||
        # IWD should attempt to connect, but it will of course fail again.
 | 
			
		||||
        device.wait_for_event("netconfig-failed", timeout=1000)
 | 
			
		||||
        device.wait_for_event("connecting (netconfig)")
 | 
			
		||||
 | 
			
		||||
        # Netconfig should fail, and IWD should disconnect
 | 
			
		||||
        from_condition = 'obj.state == DeviceState.connecting'
 | 
			
		||||
        to_condition = 'obj.state == DeviceState.disconnecting'
 | 
			
		||||
        wd.wait_for_object_change(device, from_condition, to_condition, max_wait=60)
 | 
			
		||||
 | 
			
		||||
        # Autoconnect should then try again
 | 
			
		||||
        condition = 'obj.state == DeviceState.connecting'
 | 
			
		||||
        wd.wait_for_object_condition(device, condition)
 | 
			
		||||
 | 
			
		||||
        device.wait_for_event("connecting (netconfig)")
 | 
			
		||||
 | 
			
		||||
        device.disconnect()
 | 
			
		||||
        condition = 'obj.state == DeviceState.disconnected'
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ class Test(unittest.TestCase):
 | 
			
		||||
 | 
			
		||||
        device = wd.list_devices(1)[0]
 | 
			
		||||
        device.get_ordered_network('TestFT', full_scan=True)
 | 
			
		||||
        device.connect_bssid(self.bss_hostapd[1].bssid, wait=False)
 | 
			
		||||
        device.connect_bssid(self.bss_hostapd[1].bssid)
 | 
			
		||||
 | 
			
		||||
        self.bss_hostapd[1].wait_for_event(f'AP-STA-CONNECTED {device.address}')
 | 
			
		||||
        device.wait_for_event("connecting (netconfig)")
 | 
			
		||||
 | 
			
		||||
@ -1,114 +0,0 @@
 | 
			
		||||
#! /usr/bin/python3
 | 
			
		||||
 | 
			
		||||
import unittest
 | 
			
		||||
import sys, os
 | 
			
		||||
 | 
			
		||||
sys.path.append('../util')
 | 
			
		||||
from iwd import IWD
 | 
			
		||||
from iwd import NetworkType
 | 
			
		||||
from hostapd import HostapdCLI
 | 
			
		||||
from packaging import version
 | 
			
		||||
from subprocess import run
 | 
			
		||||
import re
 | 
			
		||||
import testutil
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# The CSA handling was added in kernel 6.8, so for any earlier kernel this test
 | 
			
		||||
# won't pass.
 | 
			
		||||
#
 | 
			
		||||
def kernel_is_newer(min_version="6.8"):
 | 
			
		||||
    proc = run(["uname",  "-r"], capture_output=True)
 | 
			
		||||
 | 
			
		||||
    version_str = proc.stdout.decode("utf-8")
 | 
			
		||||
    match = re.match(r"(\d+\.\d+)", version_str)
 | 
			
		||||
    if not match:
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    return version.parse(match.group(1)) >= version.parse(min_version)
 | 
			
		||||
 | 
			
		||||
class Test(unittest.TestCase):
 | 
			
		||||
    def test_channel_switch_during_roam(self):
 | 
			
		||||
        wd = self.wd
 | 
			
		||||
 | 
			
		||||
        device = wd.list_devices(1)[0]
 | 
			
		||||
 | 
			
		||||
        ordered_network = device.get_ordered_network('TestFT', full_scan=True)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(ordered_network.type, NetworkType.psk)
 | 
			
		||||
 | 
			
		||||
        condition = 'not obj.connected'
 | 
			
		||||
        wd.wait_for_object_condition(ordered_network.network_object, condition)
 | 
			
		||||
 | 
			
		||||
        self.assertFalse(self.bss_hostapd[0].list_sta())
 | 
			
		||||
        self.assertFalse(self.bss_hostapd[1].list_sta())
 | 
			
		||||
 | 
			
		||||
        device.connect_bssid(self.bss_hostapd[0].bssid)
 | 
			
		||||
 | 
			
		||||
        condition = 'obj.state == DeviceState.connected'
 | 
			
		||||
        wd.wait_for_object_condition(device, condition)
 | 
			
		||||
 | 
			
		||||
        self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % device.address)
 | 
			
		||||
 | 
			
		||||
        testutil.test_iface_operstate(device.name)
 | 
			
		||||
        testutil.test_ifaces_connected(self.bss_hostapd[0].ifname, device.name)
 | 
			
		||||
        self.assertRaises(Exception, testutil.test_ifaces_connected,
 | 
			
		||||
                          (self.bss_hostapd[1].ifname, device.name, True, True))
 | 
			
		||||
 | 
			
		||||
        # Start a channel switch and wait for it to begin
 | 
			
		||||
        self.bss_hostapd[1].chan_switch(6, wait=False)
 | 
			
		||||
        self.bss_hostapd[1].wait_for_event("CTRL-EVENT-STARTED-CHANNEL-SWITCH")
 | 
			
		||||
        # Initiate a roam immediately which should get rejected by the kernel
 | 
			
		||||
        device.roam(self.bss_hostapd[1].bssid)
 | 
			
		||||
 | 
			
		||||
        # IWD should authenticate, then proceed to association
 | 
			
		||||
        device.wait_for_event("ft-authenticating")
 | 
			
		||||
        device.wait_for_event("ft-roaming")
 | 
			
		||||
 | 
			
		||||
        # The kernel should reject the association, which should trigger a
 | 
			
		||||
        # disconnect
 | 
			
		||||
        condition = 'obj.state == DeviceState.disconnected'
 | 
			
		||||
        wd.wait_for_object_condition(device, condition)
 | 
			
		||||
 | 
			
		||||
        condition = 'obj.state == DeviceState.connected'
 | 
			
		||||
        wd.wait_for_object_condition(device, condition)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def tearDown(self):
 | 
			
		||||
        os.system('ip link set "' + self.bss_hostapd[0].ifname + '" down')
 | 
			
		||||
        os.system('ip link set "' + self.bss_hostapd[1].ifname + '" down')
 | 
			
		||||
        os.system('ip link set "' + self.bss_hostapd[0].ifname + '" up')
 | 
			
		||||
        os.system('ip link set "' + self.bss_hostapd[1].ifname + '" up')
 | 
			
		||||
 | 
			
		||||
        for hapd in self.bss_hostapd:
 | 
			
		||||
            hapd.default()
 | 
			
		||||
 | 
			
		||||
        self.wd.stop()
 | 
			
		||||
        self.wd = None
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.wd = IWD(True)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def setUpClass(cls):
 | 
			
		||||
        if not kernel_is_newer():
 | 
			
		||||
            raise unittest.SkipTest()
 | 
			
		||||
 | 
			
		||||
        IWD.copy_to_storage('TestFT.psk')
 | 
			
		||||
 | 
			
		||||
        cls.bss_hostapd = [ HostapdCLI(config='ft-psk-ccmp-1.conf'),
 | 
			
		||||
                            HostapdCLI(config='ft-psk-ccmp-2.conf'),
 | 
			
		||||
                            HostapdCLI(config='ft-psk-ccmp-3.conf') ]
 | 
			
		||||
 | 
			
		||||
        unused = HostapdCLI(config='ft-psk-ccmp-3.conf')
 | 
			
		||||
        unused.disable()
 | 
			
		||||
 | 
			
		||||
        cls.bss_hostapd[0].set_address('12:00:00:00:00:01')
 | 
			
		||||
        cls.bss_hostapd[1].set_address('12:00:00:00:00:02')
 | 
			
		||||
        cls.bss_hostapd[2].set_address('12:00:00:00:00:03')
 | 
			
		||||
 | 
			
		||||
        HostapdCLI.group_neighbors(*cls.bss_hostapd)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def tearDownClass(cls):
 | 
			
		||||
        IWD.clear_storage()
 | 
			
		||||
        cls.bss_hostapd = None
 | 
			
		||||
@ -288,15 +288,13 @@ class HostapdCLI(object):
 | 
			
		||||
        cmd = 'RESEND_M3 %s' % address
 | 
			
		||||
        self.ctrl_sock.sendall(cmd.encode('utf-8'))
 | 
			
		||||
 | 
			
		||||
    def chan_switch(self, channel, wait=True):
 | 
			
		||||
    def chan_switch(self, channel):
 | 
			
		||||
        if channel > len(chan_freq_map):
 | 
			
		||||
            raise Exception("Only 2.4GHz channels supported for chan_switch")
 | 
			
		||||
 | 
			
		||||
        cmd = self.cmdline + ['chan_switch', '50', str(chan_freq_map[channel])]
 | 
			
		||||
        ctx.start_process(cmd).wait()
 | 
			
		||||
 | 
			
		||||
        if wait:
 | 
			
		||||
            self.wait_for_event('AP-CSA-FINISHED')
 | 
			
		||||
        self.wait_for_event('AP-CSA-FINISHED')
 | 
			
		||||
 | 
			
		||||
    def _get_status(self):
 | 
			
		||||
        ret = {}
 | 
			
		||||
 | 
			
		||||
@ -7,10 +7,7 @@ from weakref import WeakValueDictionary
 | 
			
		||||
from abc import ABCMeta, abstractmethod
 | 
			
		||||
from enum import Enum
 | 
			
		||||
from scapy.all import *
 | 
			
		||||
try:
 | 
			
		||||
    from scapy.contrib.wpa_eapol import WPA_key
 | 
			
		||||
except:
 | 
			
		||||
    from scapy.layers.eap import EAPOL_KEY
 | 
			
		||||
from scapy.contrib.wpa_eapol import WPA_key
 | 
			
		||||
 | 
			
		||||
import iwd
 | 
			
		||||
from config import ctx
 | 
			
		||||
@ -447,15 +444,9 @@ class Hwsim(iwd.AsyncOpAbstract):
 | 
			
		||||
 | 
			
		||||
        # NOTE: Expected key_info is 0x008a, with the install flag
 | 
			
		||||
        # this becomes 0x00ca.
 | 
			
		||||
        try:
 | 
			
		||||
            eapol = WPA_key( descriptor_type = 2,
 | 
			
		||||
                            key_info = 0x00ca, # Includes an invalid install flag!
 | 
			
		||||
                            replay_counter = struct.pack(">Q", 100))
 | 
			
		||||
        except:
 | 
			
		||||
            eapol = EAPOL_KEY( key_descriptor_type = 2,
 | 
			
		||||
                            install = 1,
 | 
			
		||||
                            key_ack = 1,
 | 
			
		||||
                            key_replay_counter = 1)
 | 
			
		||||
        eapol = WPA_key( descriptor_type = 2,
 | 
			
		||||
                        key_info = 0x00ca, # Includes an invalid install flag!
 | 
			
		||||
                        replay_counter = struct.pack(">Q", 100))
 | 
			
		||||
        frame /= LLC()/SNAP()/EAPOL(version="802.1X-2004", type="EAPOL-Key")
 | 
			
		||||
        frame /= eapol
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -112,8 +112,8 @@ class AsyncOpAbstract(object):
 | 
			
		||||
        self._is_completed = True
 | 
			
		||||
        self._exception = _convert_dbus_ex(ex)
 | 
			
		||||
 | 
			
		||||
    def _wait_for_async_op(self, timeout=50):
 | 
			
		||||
        ctx.non_block_wait(lambda s: s._is_completed, timeout, self, exception=None)
 | 
			
		||||
    def _wait_for_async_op(self):
 | 
			
		||||
        ctx.non_block_wait(lambda s: s._is_completed, 30, self, exception=None)
 | 
			
		||||
 | 
			
		||||
        self._is_completed = False
 | 
			
		||||
        if self._exception is not None:
 | 
			
		||||
@ -280,15 +280,8 @@ class StationDebug(IWDDBusAbstract):
 | 
			
		||||
    def autoconnect(self):
 | 
			
		||||
        return self._properties['AutoConnect']
 | 
			
		||||
 | 
			
		||||
    def connect_bssid(self, address, wait=True):
 | 
			
		||||
        self._iface.ConnectBssid(
 | 
			
		||||
            dbus.ByteArray.fromhex(address.replace(':', '')),
 | 
			
		||||
            reply_handler=self._success,
 | 
			
		||||
            error_handler=self._failure
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if wait:
 | 
			
		||||
            self._wait_for_async_op()
 | 
			
		||||
    def connect_bssid(self, address):
 | 
			
		||||
        self._iface.ConnectBssid(dbus.ByteArray.fromhex(address.replace(':', '')))
 | 
			
		||||
 | 
			
		||||
    def roam(self, address):
 | 
			
		||||
        self._iface.Roam(dbus.ByteArray.fromhex(address.replace(':', '')))
 | 
			
		||||
@ -877,8 +870,8 @@ class Device(IWDDBusAbstract):
 | 
			
		||||
    def stop_adhoc(self):
 | 
			
		||||
        self._prop_proxy.Set(IWD_DEVICE_INTERFACE, 'Mode', 'station')
 | 
			
		||||
 | 
			
		||||
    def connect_bssid(self, address, wait=True):
 | 
			
		||||
        self._station_debug.connect_bssid(address, wait=wait)
 | 
			
		||||
    def connect_bssid(self, address):
 | 
			
		||||
        self._station_debug.connect_bssid(address)
 | 
			
		||||
 | 
			
		||||
    def roam(self, address):
 | 
			
		||||
        self._station_debug.roam(address)
 | 
			
		||||
@ -1006,7 +999,7 @@ class Network(IWDDBusAbstract):
 | 
			
		||||
    def extended_service_set(self):
 | 
			
		||||
        return self._properties['ExtendedServiceSet']
 | 
			
		||||
 | 
			
		||||
    def connect(self, wait=True, timeout=50, reply_handler=None, error_handler=None):
 | 
			
		||||
    def connect(self, wait=True):
 | 
			
		||||
        '''
 | 
			
		||||
            Connect to the network. Request the device implied by the object
 | 
			
		||||
            path to connect to specified network.
 | 
			
		||||
@ -1021,19 +1014,12 @@ class Network(IWDDBusAbstract):
 | 
			
		||||
            @rtype: void
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
        if not reply_handler:
 | 
			
		||||
            reply_handler = self._success
 | 
			
		||||
 | 
			
		||||
        if not error_handler:
 | 
			
		||||
            error_handler = self._failure
 | 
			
		||||
 | 
			
		||||
        self._iface.Connect(dbus_interface=self._iface_name,
 | 
			
		||||
                            reply_handler=reply_handler,
 | 
			
		||||
                            error_handler=error_handler,
 | 
			
		||||
                            timeout=timeout)
 | 
			
		||||
                            reply_handler=self._success,
 | 
			
		||||
                            error_handler=self._failure)
 | 
			
		||||
 | 
			
		||||
        if wait:
 | 
			
		||||
            self._wait_for_async_op(timeout=timeout)
 | 
			
		||||
            self._wait_for_async_op()
 | 
			
		||||
 | 
			
		||||
    def __str__(self, prefix = ''):
 | 
			
		||||
        return prefix + 'Network:\n' \
 | 
			
		||||
 | 
			
		||||
@ -95,8 +95,6 @@ static const struct diagnostic_dict_mapping diagnostic_mapping[] = {
 | 
			
		||||
	{ "Frequency", 'u' },
 | 
			
		||||
	{ "Channel", 'q' },
 | 
			
		||||
	{ "Security", 's' },
 | 
			
		||||
	{ "InactiveTime", 'u', "ms" },
 | 
			
		||||
	{ "ConnectedTime", 'u', "s" },
 | 
			
		||||
	{ NULL }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,12 +1,10 @@
 | 
			
		||||
AC_PREREQ([2.69])
 | 
			
		||||
AC_INIT([iwd],[3.10])
 | 
			
		||||
AC_INIT([iwd],[3.2])
 | 
			
		||||
 | 
			
		||||
AC_CONFIG_HEADERS(config.h)
 | 
			
		||||
AC_CONFIG_AUX_DIR(build-aux)
 | 
			
		||||
AC_CONFIG_MACRO_DIR(build-aux)
 | 
			
		||||
 | 
			
		||||
AC_REQUIRE_AUX_FILE([tap-driver.sh])
 | 
			
		||||
 | 
			
		||||
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
 | 
			
		||||
					tar-pax no-dist-gzip dist-xz])
 | 
			
		||||
 | 
			
		||||
@ -31,7 +29,6 @@ AC_PROG_CC_GCOV
 | 
			
		||||
AC_PROG_INSTALL
 | 
			
		||||
AC_PROG_MKDIR_P
 | 
			
		||||
AC_PROG_LN_S
 | 
			
		||||
AC_PROG_AWK
 | 
			
		||||
 | 
			
		||||
AC_SYS_LARGEFILE
 | 
			
		||||
 | 
			
		||||
@ -300,7 +297,7 @@ if (test "${enable_external_ell}" = "yes"); then
 | 
			
		||||
			test "${enable_monitor}" != "no" ||
 | 
			
		||||
			test "${enable_wired}" = "yes" ||
 | 
			
		||||
			test "${enable_hwsim}" = "yes"); then
 | 
			
		||||
		ell_min_version="0.77"
 | 
			
		||||
		ell_min_version="0.69"
 | 
			
		||||
	else
 | 
			
		||||
		ell_min_version="0.5"
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
@ -31,12 +31,6 @@ Methods		array{dict} GetDiagnostics()
 | 
			
		||||
 | 
			
		||||
			TxMCS [optional] - Transmitting MCS index
 | 
			
		||||
 | 
			
		||||
			InactiveTime [optional] - Time duration (in ms) for which the STA
 | 
			
		||||
						  connected to this BSS is currently inactive.
 | 
			
		||||
 | 
			
		||||
			ConnectedTime [optional] - Time duration (in s) for which the STA
 | 
			
		||||
						   remains connected to this BSS.
 | 
			
		||||
 | 
			
		||||
			Possible errors: net.connman.iwd.Failed
 | 
			
		||||
					 net.connman.iwd.NotConnected
 | 
			
		||||
					 net.connman.iwd.NotFound
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,6 @@ Methods		void Connect()
 | 
			
		||||
			the object path to connect to specified network.
 | 
			
		||||
			Connecting to WEP networks is not supported.
 | 
			
		||||
 | 
			
		||||
			Note: When [General].EnableNetworkConfiguration is set
 | 
			
		||||
			to true a call to Connect() has the potential to take
 | 
			
		||||
			a significant amount of time. Specifically if DHCP is
 | 
			
		||||
			either slow, or is unable to complete. The timeout for
 | 
			
		||||
			DHCP is roughly 30 seconds per BSS.
 | 
			
		||||
 | 
			
		||||
			Possible errors: net.connman.iwd.Aborted
 | 
			
		||||
					 net.connman.iwd.Busy
 | 
			
		||||
					 net.connman.iwd.Failed
 | 
			
		||||
 | 
			
		||||
@ -53,12 +53,6 @@ Methods		dict GetDiagnostics()
 | 
			
		||||
					- GCMP-256
 | 
			
		||||
					- CCMP-256
 | 
			
		||||
 | 
			
		||||
			InactiveTime [optional] - Time duration (in ms) for which this STA
 | 
			
		||||
						  is currently inactive.
 | 
			
		||||
 | 
			
		||||
			ConnectedTime [optional] - Time Duration (in s) for which this STA
 | 
			
		||||
						   remains connected to the BSS.
 | 
			
		||||
 | 
			
		||||
			Possible errors: net.connman.iwd.Busy
 | 
			
		||||
					 net.connman.iwd.Failed
 | 
			
		||||
					 net.connman.iwd.NotConnected
 | 
			
		||||
 | 
			
		||||
@ -718,36 +718,29 @@ static void usage(void)
 | 
			
		||||
		"Usage:\n");
 | 
			
		||||
	printf("\tiwmon [options]\n");
 | 
			
		||||
	printf("Options:\n"
 | 
			
		||||
		"\t-r, --read <file>          Read netlink PCAP trace file\n"
 | 
			
		||||
		"\t-w, --write <file>         Write netlink PCAP trace file\n"
 | 
			
		||||
		"\t-a, --analyze <file>       Analyze netlink PCAP trace file\n"
 | 
			
		||||
		"\t-i, --interface <dev>      Use specified netlink monitor\n"
 | 
			
		||||
		"\t-n, --nortnl               Don't show RTNL output\n"
 | 
			
		||||
		"\t-y, --nowiphy              Don't show 'New Wiphy' output\n"
 | 
			
		||||
		"\t-s, --noscan               Don't show scan result output\n"
 | 
			
		||||
		"\t-e, --noies                Don't show IEs except SSID\n"
 | 
			
		||||
		"\t-t, --time-format <format> Time format to display. Either\n"
 | 
			
		||||
						"\t\t\t\t   'delta' or 'utc'.\n"
 | 
			
		||||
		"\t-W,--pcap-count            Maximum number of PCAP files\n"
 | 
			
		||||
		"\t-C,--pcap-size             Maximum size (MB) of PCAP files\n"
 | 
			
		||||
		"\t-h, --help                 Show help options\n");
 | 
			
		||||
		"\t-r, --read <file>      Read netlink PCAP trace file\n"
 | 
			
		||||
		"\t-w, --write <file>     Write netlink PCAP trace file\n"
 | 
			
		||||
		"\t-a, --analyze <file>   Analyze netlink PCAP trace file\n"
 | 
			
		||||
		"\t-i, --interface <dev>  Use specified netlink monitor\n"
 | 
			
		||||
		"\t-n, --nortnl           Don't show RTNL output\n"
 | 
			
		||||
		"\t-y, --nowiphy          Don't show 'New Wiphy' output\n"
 | 
			
		||||
		"\t-s, --noscan           Don't show scan result output\n"
 | 
			
		||||
		"\t-e, --noies            Don't show IEs except SSID\n"
 | 
			
		||||
		"\t-h, --help             Show help options\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct option main_options[] = {
 | 
			
		||||
	{ "read",        required_argument, NULL, 'r' },
 | 
			
		||||
	{ "write",       required_argument, NULL, 'w' },
 | 
			
		||||
	{ "analyze",     required_argument, NULL, 'a' },
 | 
			
		||||
	{ "nl80211",     required_argument, NULL, 'F' },
 | 
			
		||||
	{ "interface",   required_argument, NULL, 'i' },
 | 
			
		||||
	{ "nortnl",      no_argument,       NULL, 'n' },
 | 
			
		||||
	{ "nowiphy",     no_argument,       NULL, 'y' },
 | 
			
		||||
	{ "noscan",      no_argument,       NULL, 's' },
 | 
			
		||||
	{ "noies",       no_argument,       NULL, 'e' },
 | 
			
		||||
	{ "time-format", required_argument, NULL, 't' },
 | 
			
		||||
	{ "pcap-count",  required_argument, NULL, 'W' },
 | 
			
		||||
	{ "pcap-size",   required_argument, NULL, 'C' },
 | 
			
		||||
	{ "version",     no_argument,       NULL, 'v' },
 | 
			
		||||
	{ "help",        no_argument,       NULL, 'h' },
 | 
			
		||||
	{ "read",      required_argument, NULL, 'r' },
 | 
			
		||||
	{ "write",     required_argument, NULL, 'w' },
 | 
			
		||||
	{ "analyze",   required_argument, NULL, 'a' },
 | 
			
		||||
	{ "nl80211",   required_argument, NULL, 'F' },
 | 
			
		||||
	{ "interface", required_argument, NULL, 'i' },
 | 
			
		||||
	{ "nortnl",    no_argument,       NULL, 'n' },
 | 
			
		||||
	{ "nowiphy",   no_argument,       NULL, 'y' },
 | 
			
		||||
	{ "noscan",    no_argument,       NULL, 's' },
 | 
			
		||||
	{ "noies",     no_argument,       NULL, 'e' },
 | 
			
		||||
	{ "version",   no_argument,       NULL, 'v' },
 | 
			
		||||
	{ "help",      no_argument,       NULL, 'h' },
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -761,7 +754,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		int opt;
 | 
			
		||||
 | 
			
		||||
		opt = getopt_long(argc, argv, "r:w:a:i:t:W:C:nvhyse",
 | 
			
		||||
		opt = getopt_long(argc, argv, "r:w:a:i:nvhyse",
 | 
			
		||||
						main_options, NULL);
 | 
			
		||||
		if (opt < 0)
 | 
			
		||||
			break;
 | 
			
		||||
@ -791,35 +784,6 @@ int main(int argc, char *argv[])
 | 
			
		||||
			break;
 | 
			
		||||
		case 'e':
 | 
			
		||||
			config.noies = true;
 | 
			
		||||
			break;
 | 
			
		||||
		case 't':
 | 
			
		||||
			if (!strcmp(optarg, "delta"))
 | 
			
		||||
				config.time_format = TIME_FORMAT_DELTA;
 | 
			
		||||
			else if (!strcmp(optarg, "utc"))
 | 
			
		||||
				config.time_format = TIME_FORMAT_UTC;
 | 
			
		||||
			else {
 | 
			
		||||
				printf("Invalid time format '%s'", optarg);
 | 
			
		||||
				return EXIT_FAILURE;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		case 'W':
 | 
			
		||||
			if (l_safe_atou32(optarg,
 | 
			
		||||
					&config.pcap_file_count) < 0 ||
 | 
			
		||||
					config.pcap_file_count == 0) {
 | 
			
		||||
				printf("Invalid file count '%s'\n", optarg);
 | 
			
		||||
				return EXIT_FAILURE;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		case 'C':
 | 
			
		||||
			if (l_safe_atou32(optarg,
 | 
			
		||||
					&config.pcap_file_size) < 0 ||
 | 
			
		||||
					config.pcap_file_size == 0) {
 | 
			
		||||
				printf("Invalid file size '%s'\n", optarg);
 | 
			
		||||
				return EXIT_FAILURE;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		case 'v':
 | 
			
		||||
			printf("%s\n", VERSION);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										131
									
								
								monitor/nlmon.c
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								monitor/nlmon.c
									
									
									
									
									
								
							@ -29,7 +29,6 @@
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <sys/ioctl.h>
 | 
			
		||||
#include <sys/socket.h>
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
@ -43,7 +42,6 @@
 | 
			
		||||
#include <linux/genetlink.h>
 | 
			
		||||
#include <linux/rtnetlink.h>
 | 
			
		||||
#include <linux/filter.h>
 | 
			
		||||
#include <linux/limits.h>
 | 
			
		||||
#include <ell/ell.h>
 | 
			
		||||
 | 
			
		||||
#ifndef ARPHRD_NETLINK
 | 
			
		||||
@ -95,8 +93,6 @@
 | 
			
		||||
#define BSS_CAPABILITY_APSD		(1<<11)
 | 
			
		||||
#define BSS_CAPABILITY_DSSS_OFDM	(1<<13)
 | 
			
		||||
 | 
			
		||||
#define BYTES_PER_MB			1000000
 | 
			
		||||
 | 
			
		||||
struct nlmon *cur_nlmon;
 | 
			
		||||
 | 
			
		||||
enum msg_type {
 | 
			
		||||
@ -117,12 +113,6 @@ struct nlmon {
 | 
			
		||||
	bool noscan;
 | 
			
		||||
	bool noies;
 | 
			
		||||
	bool read;
 | 
			
		||||
	enum time_format time_format;
 | 
			
		||||
 | 
			
		||||
	char *file_prefix;
 | 
			
		||||
	unsigned int file_idx;
 | 
			
		||||
	unsigned int max_files;
 | 
			
		||||
	unsigned int max_size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct nlmon_req {
 | 
			
		||||
@ -195,15 +185,11 @@ static void nlmon_req_free(void *data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static time_t time_offset = ((time_t) -1);
 | 
			
		||||
static enum time_format time_format;
 | 
			
		||||
 | 
			
		||||
static inline void update_time_offset(const struct timeval *tv,
 | 
			
		||||
					enum time_format tf)
 | 
			
		||||
static inline void update_time_offset(const struct timeval *tv)
 | 
			
		||||
{
 | 
			
		||||
	if (tv && time_offset == ((time_t) -1)) {
 | 
			
		||||
	if (tv && time_offset == ((time_t) -1))
 | 
			
		||||
		time_offset = tv->tv_sec;
 | 
			
		||||
		time_format = tf;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
 | 
			
		||||
@ -239,38 +225,15 @@ static void print_packet(const struct timeval *tv, char ident,
 | 
			
		||||
	int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
 | 
			
		||||
 | 
			
		||||
	if (tv) {
 | 
			
		||||
		struct tm *tm;
 | 
			
		||||
 | 
			
		||||
		if (use_color()) {
 | 
			
		||||
			n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP);
 | 
			
		||||
			if (n > 0)
 | 
			
		||||
				ts_pos += n;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch (time_format) {
 | 
			
		||||
		case TIME_FORMAT_DELTA:
 | 
			
		||||
			n = sprintf(ts_str + ts_pos, " %" PRId64 ".%06" PRId64,
 | 
			
		||||
		n = sprintf(ts_str + ts_pos, " %" PRId64 ".%06" PRId64,
 | 
			
		||||
					(int64_t)tv->tv_sec - time_offset,
 | 
			
		||||
					(int64_t)tv->tv_usec);
 | 
			
		||||
			break;
 | 
			
		||||
		case TIME_FORMAT_UTC:
 | 
			
		||||
			tm = gmtime(&tv->tv_sec);
 | 
			
		||||
			if (!tm) {
 | 
			
		||||
				n = sprintf(ts_str + ts_pos, "%s",
 | 
			
		||||
						"Time error");
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			n = strftime(ts_str + ts_pos, sizeof(ts_str) - ts_pos,
 | 
			
		||||
					"%b %d %H:%M:%S", tm);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			/* Should never happen */
 | 
			
		||||
			printf("Unknown time format");
 | 
			
		||||
			l_main_quit();
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (n > 0) {
 | 
			
		||||
			ts_pos += n;
 | 
			
		||||
			ts_len += n;
 | 
			
		||||
@ -404,7 +367,6 @@ static const struct {
 | 
			
		||||
	{ { 0x00, 0x50, 0xf2 }, "Microsoft"		},
 | 
			
		||||
	{ { 0x00, 0x90, 0x4c }, "Epigram"		},
 | 
			
		||||
	{ { 0x50, 0x6f, 0x9a }, "Wi-Fi Alliance"	},
 | 
			
		||||
	{ { 0x00, 0x18, 0x0a }, "Cisco Meraki"		},
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1916,7 +1878,7 @@ static void print_ie_interworking(unsigned int level,
 | 
			
		||||
	size--;
 | 
			
		||||
	ptr++;
 | 
			
		||||
 | 
			
		||||
	if (size < 2)
 | 
			
		||||
	if (!size)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
@ -7402,64 +7364,6 @@ static bool nlmon_req_match(const void *a, const void *b)
 | 
			
		||||
	return (req->seq == match->seq && req->pid == match->pid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Ensures that PCAP names are zero padded when needed. This makes the files
 | 
			
		||||
 * sort correctly.
 | 
			
		||||
 */
 | 
			
		||||
static void next_pcap_name(char *buf, size_t size, const char *prefix,
 | 
			
		||||
				unsigned int idx, unsigned int max)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int ndigits = 1;
 | 
			
		||||
 | 
			
		||||
	while (max > 9) {
 | 
			
		||||
		max /= 10;
 | 
			
		||||
		ndigits++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, size, "%s%.*u", prefix, ndigits, idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool check_pcap(struct nlmon *nlmon, size_t next_size)
 | 
			
		||||
{
 | 
			
		||||
	char path[PATH_MAX];
 | 
			
		||||
 | 
			
		||||
	if (!nlmon->pcap)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (!nlmon->max_size)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	if (pcap_get_size(nlmon->pcap) + next_size <= nlmon->max_size)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	pcap_close(nlmon->pcap);
 | 
			
		||||
 | 
			
		||||
	/* Exhausted the single PCAP file */
 | 
			
		||||
	if (nlmon->max_files < 2) {
 | 
			
		||||
		printf("Reached maximum size of PCAP, exiting\n");
 | 
			
		||||
		nlmon->pcap = NULL;
 | 
			
		||||
		l_main_quit();
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	next_pcap_name(path, sizeof(path), nlmon->file_prefix,
 | 
			
		||||
			++nlmon->file_idx, nlmon->max_files);
 | 
			
		||||
 | 
			
		||||
	nlmon->pcap = pcap_create(path);
 | 
			
		||||
 | 
			
		||||
	if (nlmon->max_files > nlmon->file_idx)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	/* Remove oldest PCAP file */
 | 
			
		||||
	next_pcap_name(path, sizeof(path), nlmon->file_prefix,
 | 
			
		||||
		nlmon->file_idx - nlmon->max_files, nlmon->max_files);
 | 
			
		||||
 | 
			
		||||
	if (remove(path) < 0)
 | 
			
		||||
		printf("Failed to remove old PCAP file %s\n", path);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void store_packet(struct nlmon *nlmon, const struct timeval *tv,
 | 
			
		||||
					uint16_t pkt_type,
 | 
			
		||||
					uint16_t arphrd_type,
 | 
			
		||||
@ -7468,7 +7372,7 @@ static void store_packet(struct nlmon *nlmon, const struct timeval *tv,
 | 
			
		||||
{
 | 
			
		||||
	uint8_t sll_hdr[16], *buf = sll_hdr;
 | 
			
		||||
 | 
			
		||||
	if (!check_pcap(nlmon, sizeof(sll_hdr) + size))
 | 
			
		||||
	if (!nlmon->pcap)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	memset(sll_hdr, 0, sizeof(sll_hdr));
 | 
			
		||||
@ -7593,10 +7497,6 @@ struct nlmon *nlmon_create(uint16_t id, const struct nlmon_config *config)
 | 
			
		||||
	nlmon->noscan = config->noscan;
 | 
			
		||||
	nlmon->noies = config->noies;
 | 
			
		||||
	nlmon->read = config->read_only;
 | 
			
		||||
	nlmon->time_format = config->time_format;
 | 
			
		||||
	nlmon->max_files = config->pcap_file_count;
 | 
			
		||||
	/* Command line expects MB, but use bytes internally */
 | 
			
		||||
	nlmon->max_size = config->pcap_file_size * BYTES_PER_MB;
 | 
			
		||||
 | 
			
		||||
	return nlmon;
 | 
			
		||||
}
 | 
			
		||||
@ -8433,10 +8333,7 @@ void nlmon_print_rtnl(struct nlmon *nlmon, const struct timeval *tv,
 | 
			
		||||
	int64_t aligned_size = NLMSG_ALIGN(size);
 | 
			
		||||
	const struct nlmsghdr *nlmsg;
 | 
			
		||||
 | 
			
		||||
	if (nlmon->nortnl)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	update_time_offset(tv, nlmon->time_format);
 | 
			
		||||
	update_time_offset(tv);
 | 
			
		||||
 | 
			
		||||
	for (nlmsg = data; NLMSG_OK(nlmsg, aligned_size);
 | 
			
		||||
				nlmsg = NLMSG_NEXT(nlmsg, aligned_size)) {
 | 
			
		||||
@ -8474,7 +8371,7 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
 | 
			
		||||
{
 | 
			
		||||
	const struct nlmsghdr *nlmsg;
 | 
			
		||||
 | 
			
		||||
	update_time_offset(tv, nlmon->time_format);
 | 
			
		||||
	update_time_offset(tv);
 | 
			
		||||
 | 
			
		||||
	for (nlmsg = data; NLMSG_OK(nlmsg, size);
 | 
			
		||||
				nlmsg = NLMSG_NEXT(nlmsg, size)) {
 | 
			
		||||
@ -8497,7 +8394,7 @@ void nlmon_print_pae(struct nlmon *nlmon, const struct timeval *tv,
 | 
			
		||||
{
 | 
			
		||||
	char extra_str[16];
 | 
			
		||||
 | 
			
		||||
	update_time_offset(tv, nlmon->time_format);
 | 
			
		||||
	update_time_offset(tv);
 | 
			
		||||
 | 
			
		||||
	sprintf(extra_str, "len %u", size);
 | 
			
		||||
 | 
			
		||||
@ -8624,20 +8521,13 @@ struct nlmon *nlmon_open(uint16_t id, const char *pathname,
 | 
			
		||||
	struct nlmon *nlmon;
 | 
			
		||||
	struct l_io *pae_io;
 | 
			
		||||
	struct pcap *pcap;
 | 
			
		||||
	char path[PATH_MAX];
 | 
			
		||||
 | 
			
		||||
	pae_io = open_pae();
 | 
			
		||||
	if (!pae_io)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	if (pathname) {
 | 
			
		||||
		if (config->pcap_file_count > 1)
 | 
			
		||||
			next_pcap_name(path, sizeof(path), pathname,
 | 
			
		||||
					0, config->pcap_file_count);
 | 
			
		||||
		else
 | 
			
		||||
			snprintf(path, sizeof(path), "%s", pathname);
 | 
			
		||||
 | 
			
		||||
		pcap = pcap_create(path);
 | 
			
		||||
		pcap = pcap_create(pathname);
 | 
			
		||||
		if (!pcap) {
 | 
			
		||||
			l_io_destroy(pae_io);
 | 
			
		||||
			return NULL;
 | 
			
		||||
@ -8650,7 +8540,6 @@ struct nlmon *nlmon_open(uint16_t id, const char *pathname,
 | 
			
		||||
 | 
			
		||||
	nlmon->pae_io = pae_io;
 | 
			
		||||
	nlmon->pcap = pcap;
 | 
			
		||||
	nlmon->file_prefix = l_strdup(pathname);
 | 
			
		||||
 | 
			
		||||
	l_io_set_read_handler(nlmon->pae_io, pae_receive, nlmon, NULL);
 | 
			
		||||
 | 
			
		||||
@ -8673,7 +8562,5 @@ void nlmon_close(struct nlmon *nlmon)
 | 
			
		||||
	if (nlmon->pcap)
 | 
			
		||||
		pcap_close(nlmon->pcap);
 | 
			
		||||
 | 
			
		||||
	l_free(nlmon->file_prefix);
 | 
			
		||||
 | 
			
		||||
	l_free(nlmon);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -25,22 +25,12 @@
 | 
			
		||||
 | 
			
		||||
struct nlmon;
 | 
			
		||||
 | 
			
		||||
enum time_format {
 | 
			
		||||
	TIME_FORMAT_DELTA,
 | 
			
		||||
	TIME_FORMAT_UTC,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct nlmon_config {
 | 
			
		||||
	bool nortnl;
 | 
			
		||||
	bool nowiphy;
 | 
			
		||||
	bool noscan;
 | 
			
		||||
	bool noies;
 | 
			
		||||
	bool read_only;
 | 
			
		||||
	enum time_format time_format;
 | 
			
		||||
 | 
			
		||||
	/* File size in MB */
 | 
			
		||||
	uint32_t pcap_file_size;
 | 
			
		||||
	uint32_t pcap_file_count;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct nlmon *nlmon_open(uint16_t id, const char *pathname,
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,6 @@ struct pcap {
 | 
			
		||||
	bool closed;
 | 
			
		||||
	uint32_t type;
 | 
			
		||||
	uint32_t snaplen;
 | 
			
		||||
	size_t size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct pcap *pcap_open(const char *pathname)
 | 
			
		||||
@ -153,8 +152,6 @@ struct pcap *pcap_create(const char *pathname)
 | 
			
		||||
		goto failed;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pcap->size += len;
 | 
			
		||||
 | 
			
		||||
	return pcap;
 | 
			
		||||
 | 
			
		||||
failed:
 | 
			
		||||
@ -191,11 +188,6 @@ uint32_t pcap_get_snaplen(struct pcap *pcap)
 | 
			
		||||
	return pcap->snaplen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
size_t pcap_get_size(struct pcap *pcap)
 | 
			
		||||
{
 | 
			
		||||
	return pcap->size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool pcap_read(struct pcap *pcap, struct timeval *tv,
 | 
			
		||||
		void *data, uint32_t size, uint32_t *len, uint32_t *real_len)
 | 
			
		||||
{
 | 
			
		||||
@ -287,7 +279,5 @@ bool pcap_write(struct pcap *pcap, const struct timeval *tv,
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pcap->size += written;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,6 @@ void pcap_close(struct pcap *pcap);
 | 
			
		||||
 | 
			
		||||
uint32_t pcap_get_type(struct pcap *pcap);
 | 
			
		||||
uint32_t pcap_get_snaplen(struct pcap *pcap);
 | 
			
		||||
size_t pcap_get_size(struct pcap *pcap);
 | 
			
		||||
 | 
			
		||||
bool pcap_read(struct pcap *pcap, struct timeval *tv,
 | 
			
		||||
		void *data, uint32_t size, uint32_t *len, uint32_t *real_len);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										86
									
								
								src/ap.c
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								src/ap.c
									
									
									
									
									
								
							@ -109,8 +109,6 @@ struct ap_state {
 | 
			
		||||
	struct l_timeout *rekey_timeout;
 | 
			
		||||
	unsigned int rekey_time;
 | 
			
		||||
 | 
			
		||||
	uint32_t pre_scan_cmd_id;
 | 
			
		||||
 | 
			
		||||
	bool started : 1;
 | 
			
		||||
	bool gtk_set : 1;
 | 
			
		||||
	bool netconfig_set_addr4 : 1;
 | 
			
		||||
@ -356,12 +354,6 @@ static void ap_reset(struct ap_state *ap)
 | 
			
		||||
		l_timeout_remove(ap->rekey_timeout);
 | 
			
		||||
		ap->rekey_timeout = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ap->pre_scan_cmd_id) {
 | 
			
		||||
		scan_cancel(netdev_get_wdev_id(ap->netdev),
 | 
			
		||||
				ap->pre_scan_cmd_id);
 | 
			
		||||
		ap->pre_scan_cmd_id = 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool ap_event_done(struct ap_state *ap, bool prev_in_event)
 | 
			
		||||
@ -3860,70 +3852,6 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ap_pre_scan_trigger(int err, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ap_state *ap = user_data;
 | 
			
		||||
 | 
			
		||||
	if (err < 0) {
 | 
			
		||||
		l_error("AP pre-scan failed: %i", err);
 | 
			
		||||
		ap_start_failed(ap, err);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool ap_check_channel(struct ap_state *ap)
 | 
			
		||||
{
 | 
			
		||||
	const struct band_freq_attrs *freq_attr;
 | 
			
		||||
 | 
			
		||||
	freq_attr = wiphy_get_frequency_info(netdev_get_wiphy(ap->netdev),
 | 
			
		||||
				band_channel_to_freq(ap->channel, ap->band));
 | 
			
		||||
	if (L_WARN_ON(!freq_attr))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/* Check if disabled/no-IR */
 | 
			
		||||
	if (freq_attr->disabled || freq_attr->no_ir)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool ap_pre_scan_notify(int err, struct l_queue *bss_list,
 | 
			
		||||
				const struct scan_freq_set *freqs,
 | 
			
		||||
				void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ap_state *ap = user_data;
 | 
			
		||||
 | 
			
		||||
	if (!ap_check_channel(ap)) {
 | 
			
		||||
		l_error("Unable to channel %u even after pre-scan",
 | 
			
		||||
			ap->channel);
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ap_start_send(ap))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	ap_start_failed(ap, -ENOTSUP);
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void ap_pre_scan_destroy(void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct ap_state *ap = user_data;
 | 
			
		||||
 | 
			
		||||
	ap->pre_scan_cmd_id = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool ap_pre_scan(struct ap_state *ap)
 | 
			
		||||
{
 | 
			
		||||
	ap->pre_scan_cmd_id = scan_passive(netdev_get_wdev_id(ap->netdev),
 | 
			
		||||
						NULL, ap_pre_scan_trigger,
 | 
			
		||||
						ap_pre_scan_notify,
 | 
			
		||||
						ap, ap_pre_scan_destroy);
 | 
			
		||||
 | 
			
		||||
	return ap->pre_scan_cmd_id != 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Start a simple independent WPA2 AP on given netdev.
 | 
			
		||||
 *
 | 
			
		||||
@ -4034,20 +3962,6 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
 | 
			
		||||
		return ap;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!ap_check_channel(ap)) {
 | 
			
		||||
		l_debug("Channel %u is disabled/no-IR, pre-scanning",
 | 
			
		||||
			ap->channel);
 | 
			
		||||
 | 
			
		||||
		if (ap_pre_scan(ap)) {
 | 
			
		||||
			if (err_out)
 | 
			
		||||
				*err_out = 0;
 | 
			
		||||
 | 
			
		||||
			return ap;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ap_start_send(ap)) {
 | 
			
		||||
		if (err_out)
 | 
			
		||||
			*err_out = 0;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										128
									
								
								src/blacklist.c
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								src/blacklist.c
									
									
									
									
									
								
							@ -45,42 +45,22 @@
 | 
			
		||||
 | 
			
		||||
static uint64_t blacklist_multiplier;
 | 
			
		||||
static uint64_t blacklist_initial_timeout;
 | 
			
		||||
static uint64_t blacklist_ap_busy_initial_timeout;
 | 
			
		||||
static uint64_t blacklist_max_timeout;
 | 
			
		||||
 | 
			
		||||
struct blacklist_entry {
 | 
			
		||||
	uint8_t addr[6];
 | 
			
		||||
	uint64_t added_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 uint64_t get_reason_timeout(enum blacklist_reason reason)
 | 
			
		||||
{
 | 
			
		||||
	switch (reason) {
 | 
			
		||||
	case BLACKLIST_REASON_CONNECT_FAILED:
 | 
			
		||||
		return blacklist_initial_timeout;
 | 
			
		||||
	case BLACKLIST_REASON_AP_BUSY:
 | 
			
		||||
		return blacklist_ap_busy_initial_timeout;
 | 
			
		||||
	default:
 | 
			
		||||
		l_warn("Unhandled blacklist reason: %u", reason);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool check_if_expired(void *data, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct blacklist_entry *entry = data;
 | 
			
		||||
	uint64_t now = l_get_u64(user_data);
 | 
			
		||||
 | 
			
		||||
	if (l_time_after(now, entry->expire_time)) {
 | 
			
		||||
	if (l_time_diff(now, entry->added_time) > blacklist_max_timeout) {
 | 
			
		||||
		l_debug("Removing entry "MAC" on prune", MAC_STR(entry->addr));
 | 
			
		||||
		l_free(entry);
 | 
			
		||||
		return true;
 | 
			
		||||
@ -107,53 +87,17 @@ static bool match_addr(const void *a, const void *b)
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
void blacklist_add_bss(const uint8_t *addr)
 | 
			
		||||
{
 | 
			
		||||
	struct blacklist_entry *entry;
 | 
			
		||||
	uint64_t timeout;
 | 
			
		||||
 | 
			
		||||
	blacklist_prune();
 | 
			
		||||
 | 
			
		||||
	timeout = get_reason_timeout(reason);
 | 
			
		||||
	if (!timeout)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	entry = l_queue_find(blacklist, match_addr, addr);
 | 
			
		||||
 | 
			
		||||
	if (entry) {
 | 
			
		||||
		uint64_t offset;
 | 
			
		||||
 | 
			
		||||
		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);
 | 
			
		||||
		uint64_t offset = l_time_diff(entry->added_time,
 | 
			
		||||
							entry->expire_time);
 | 
			
		||||
 | 
			
		||||
		offset *= blacklist_multiplier;
 | 
			
		||||
 | 
			
		||||
@ -168,36 +112,40 @@ void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason)
 | 
			
		||||
	entry = l_new(struct blacklist_entry, 1);
 | 
			
		||||
 | 
			
		||||
	entry->added_time = l_time_now();
 | 
			
		||||
	entry->expire_time = l_time_offset(entry->added_time, timeout);
 | 
			
		||||
	entry->reason = reason;
 | 
			
		||||
	entry->expire_time = l_time_offset(entry->added_time,
 | 
			
		||||
						blacklist_initial_timeout);
 | 
			
		||||
	memcpy(entry->addr, addr, 6);
 | 
			
		||||
 | 
			
		||||
	l_queue_push_tail(blacklist, entry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason)
 | 
			
		||||
bool blacklist_contains_bss(const uint8_t *addr)
 | 
			
		||||
{
 | 
			
		||||
	struct blacklist_search search = {
 | 
			
		||||
		.addr = addr,
 | 
			
		||||
		.reason = reason
 | 
			
		||||
	};
 | 
			
		||||
	bool ret;
 | 
			
		||||
	uint64_t time_now;
 | 
			
		||||
	struct blacklist_entry *entry;
 | 
			
		||||
 | 
			
		||||
	blacklist_prune();
 | 
			
		||||
 | 
			
		||||
	return l_queue_find(blacklist, match_addr_and_reason, &search) != NULL;
 | 
			
		||||
	entry = l_queue_find(blacklist, match_addr, addr);
 | 
			
		||||
 | 
			
		||||
	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, enum blacklist_reason reason)
 | 
			
		||||
void blacklist_remove_bss(const uint8_t *addr)
 | 
			
		||||
{
 | 
			
		||||
	struct blacklist_entry *entry;
 | 
			
		||||
	struct blacklist_search search = {
 | 
			
		||||
		.addr = addr,
 | 
			
		||||
		.reason = reason
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	blacklist_prune();
 | 
			
		||||
 | 
			
		||||
	entry = l_queue_remove_if(blacklist, match_addr_and_reason, &search);
 | 
			
		||||
	entry = l_queue_remove_if(blacklist, match_addr, addr);
 | 
			
		||||
 | 
			
		||||
	if (!entry)
 | 
			
		||||
		return;
 | 
			
		||||
@ -214,47 +162,19 @@ static int blacklist_init(void)
 | 
			
		||||
		blacklist_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
	/* For easier user configuration the timeout values are in seconds */
 | 
			
		||||
	blacklist_initial_timeout *= L_USEC_PER_SEC;
 | 
			
		||||
 | 
			
		||||
	if (!l_settings_get_uint64(config, "Blacklist",
 | 
			
		||||
					"InitialRoamRequestedTimeout",
 | 
			
		||||
					&blacklist_ap_busy_initial_timeout))
 | 
			
		||||
		blacklist_ap_busy_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
 | 
			
		||||
	else
 | 
			
		||||
		l_warn("[Blacklist].InitialRoamRequestedTimeout is deprecated, "
 | 
			
		||||
			"use [Blacklist].InitialAccessPointBusyTimeout");
 | 
			
		||||
 | 
			
		||||
	if (!l_settings_get_uint64(config, "Blacklist",
 | 
			
		||||
					"InitialAccessPointBusyTimeout",
 | 
			
		||||
					&blacklist_ap_busy_initial_timeout))
 | 
			
		||||
		blacklist_ap_busy_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
	/* For easier user configuration the timeout values are in seconds */
 | 
			
		||||
	blacklist_ap_busy_initial_timeout *= L_USEC_PER_SEC;
 | 
			
		||||
	blacklist_initial_timeout *= 1000000;
 | 
			
		||||
 | 
			
		||||
	if (!l_settings_get_uint64(config, "Blacklist",
 | 
			
		||||
					"Multiplier",
 | 
			
		||||
					&blacklist_multiplier))
 | 
			
		||||
		blacklist_multiplier = BLACKLIST_DEFAULT_MULTIPLIER;
 | 
			
		||||
 | 
			
		||||
	if (blacklist_multiplier == 0) {
 | 
			
		||||
		l_warn("[Blacklist].Multiplier cannot be zero, setting to 1");
 | 
			
		||||
		blacklist_multiplier = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!l_settings_get_uint64(config, "Blacklist",
 | 
			
		||||
					"MaximumTimeout",
 | 
			
		||||
					&blacklist_max_timeout))
 | 
			
		||||
		blacklist_max_timeout = BLACKLIST_DEFAULT_MAX_TIMEOUT;
 | 
			
		||||
 | 
			
		||||
	blacklist_max_timeout *= L_USEC_PER_SEC;
 | 
			
		||||
 | 
			
		||||
	if (blacklist_initial_timeout > blacklist_max_timeout)
 | 
			
		||||
		l_warn("[Blacklist].InitialTimeout exceeded "
 | 
			
		||||
			"[Blacklist].MaximumTimeout!");
 | 
			
		||||
 | 
			
		||||
	if (!blacklist_initial_timeout)
 | 
			
		||||
		l_debug("initial timeout was zero, blacklist will be disabled");
 | 
			
		||||
	blacklist_max_timeout *= 1000000;
 | 
			
		||||
 | 
			
		||||
	blacklist = l_queue_new();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -20,23 +20,6 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
enum blacklist_reason {
 | 
			
		||||
	/*
 | 
			
		||||
	 * 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 an AP indicates that its unable
 | 
			
		||||
	 * to handle more connections. This is done via BSS-TM requests or
 | 
			
		||||
	 * denied authentications/associations with certain status codes.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Once this type of blacklist is applied to a BSS IWD will attempt to
 | 
			
		||||
	 * avoid roaming to it for a configured period of time.
 | 
			
		||||
	 */
 | 
			
		||||
	BLACKLIST_REASON_AP_BUSY,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
void blacklist_add_bss(const uint8_t *addr);
 | 
			
		||||
bool blacklist_contains_bss(const uint8_t *addr);
 | 
			
		||||
void blacklist_remove_bss(const uint8_t *addr);
 | 
			
		||||
 | 
			
		||||
@ -110,14 +110,6 @@ bool diagnostic_info_to_dict(const struct diagnostic_station_info *info,
 | 
			
		||||
		dbus_append_dict_basic(builder, "ExpectedThroughput", 'u',
 | 
			
		||||
					&info->expected_throughput);
 | 
			
		||||
 | 
			
		||||
	if (info->have_inactive_time)
 | 
			
		||||
		dbus_append_dict_basic(builder, "InactiveTime", 'u',
 | 
			
		||||
					&info->inactive_time);
 | 
			
		||||
 | 
			
		||||
	if (info->have_connected_time)
 | 
			
		||||
		dbus_append_dict_basic(builder, "ConnectedTime", 'u',
 | 
			
		||||
					&info->connected_time);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,9 +43,6 @@ struct diagnostic_station_info {
 | 
			
		||||
 | 
			
		||||
	uint32_t expected_throughput;
 | 
			
		||||
 | 
			
		||||
	uint32_t inactive_time;
 | 
			
		||||
	uint32_t connected_time;
 | 
			
		||||
 | 
			
		||||
	bool have_cur_rssi : 1;
 | 
			
		||||
	bool have_avg_rssi : 1;
 | 
			
		||||
	bool have_rx_mcs : 1;
 | 
			
		||||
@ -53,8 +50,6 @@ struct diagnostic_station_info {
 | 
			
		||||
	bool have_rx_bitrate : 1;
 | 
			
		||||
	bool have_tx_bitrate : 1;
 | 
			
		||||
	bool have_expected_throughput : 1;
 | 
			
		||||
	bool have_inactive_time : 1;
 | 
			
		||||
	bool have_connected_time : 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
bool diagnostic_info_to_dict(const struct diagnostic_station_info *info,
 | 
			
		||||
 | 
			
		||||
@ -1166,34 +1166,21 @@ struct dpp_uri_info *dpp_parse_uri(const char *uri)
 | 
			
		||||
 | 
			
		||||
		switch (*pos) {
 | 
			
		||||
		case 'C':
 | 
			
		||||
			if (L_WARN_ON(info->freqs))
 | 
			
		||||
				goto free_info;
 | 
			
		||||
 | 
			
		||||
			info->freqs = dpp_parse_class_and_channel(pos + 2, len);
 | 
			
		||||
			if (!info->freqs)
 | 
			
		||||
				goto free_info;
 | 
			
		||||
			break;
 | 
			
		||||
		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);
 | 
			
		||||
			if (ret < 0)
 | 
			
		||||
				goto free_info;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'V':
 | 
			
		||||
			if (L_WARN_ON(info->version != 0))
 | 
			
		||||
				goto free_info;
 | 
			
		||||
 | 
			
		||||
			ret = dpp_parse_version(pos + 2, len, &info->version);
 | 
			
		||||
			if (ret < 0)
 | 
			
		||||
				goto free_info;
 | 
			
		||||
			break;
 | 
			
		||||
		case 'K':
 | 
			
		||||
			if (L_WARN_ON(info->boot_public))
 | 
			
		||||
				goto free_info;
 | 
			
		||||
 | 
			
		||||
			info->boot_public = dpp_parse_key(pos + 2, len);
 | 
			
		||||
			if (!info->boot_public)
 | 
			
		||||
				goto free_info;
 | 
			
		||||
 | 
			
		||||
@ -544,8 +544,7 @@ static bool eap_mschapv2_load_settings(struct eap_state *eap,
 | 
			
		||||
	return true;
 | 
			
		||||
 | 
			
		||||
error:
 | 
			
		||||
	l_free(state->user);
 | 
			
		||||
	l_free(state);
 | 
			
		||||
	free(state);
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -425,8 +425,8 @@ static void eap_handle_response(struct eap_state *eap, const uint8_t *pkt,
 | 
			
		||||
				size_t len)
 | 
			
		||||
{
 | 
			
		||||
	enum eap_type type;
 | 
			
		||||
	uint32_t vendor_id = 0;
 | 
			
		||||
	uint32_t vendor_type = 0;
 | 
			
		||||
	uint32_t vendor_id;
 | 
			
		||||
	uint32_t vendor_type;
 | 
			
		||||
	enum eap_type our_type = eap->method->request_type;
 | 
			
		||||
	uint32_t our_vendor_id = (eap->method->vendor_id[0] << 16) |
 | 
			
		||||
				(eap->method->vendor_id[1] << 8) |
 | 
			
		||||
 | 
			
		||||
@ -1810,7 +1810,7 @@ static void eapol_handle_ptk_3_of_4(struct eapol_sm *sm,
 | 
			
		||||
 | 
			
		||||
	if ((rsne[1] != hs->authenticator_ie[1] ||
 | 
			
		||||
			memcmp(rsne + 2, hs->authenticator_ie + 2, rsne[1])) &&
 | 
			
		||||
			!handshake_util_ap_ie_matches(hs, &rsn_info,
 | 
			
		||||
			!handshake_util_ap_ie_matches(&rsn_info,
 | 
			
		||||
							hs->authenticator_ie,
 | 
			
		||||
							hs->wpa_ie))
 | 
			
		||||
		goto error_ie_different;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								src/ft.c
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/ft.c
									
									
									
									
									
								
							@ -223,8 +223,7 @@ static bool ft_parse_associate_resp_frame(const uint8_t *frame, size_t frame_len
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool ft_verify_rsne(struct handshake_state *hs,
 | 
			
		||||
				const uint8_t *rsne, const uint8_t *pmk_r0_name,
 | 
			
		||||
static bool ft_verify_rsne(const uint8_t *rsne, const uint8_t *pmk_r0_name,
 | 
			
		||||
				const uint8_t *authenticator_ie)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
@ -254,7 +253,7 @@ static bool ft_verify_rsne(struct handshake_state *hs,
 | 
			
		||||
				memcmp(msg2_rsne.pmkids, pmk_r0_name, 16))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (!handshake_util_ap_ie_matches(hs, &msg2_rsne, authenticator_ie, false))
 | 
			
		||||
	if (!handshake_util_ap_ie_matches(&msg2_rsne, authenticator_ie, false))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
@ -302,8 +301,7 @@ static int parse_ies(struct handshake_state *hs,
 | 
			
		||||
	is_rsn = hs->supplicant_ie != NULL;
 | 
			
		||||
 | 
			
		||||
	if (is_rsn) {
 | 
			
		||||
		if (!ft_verify_rsne(hs, rsne, hs->pmk_r0_name,
 | 
			
		||||
					authenticator_ie))
 | 
			
		||||
		if (!ft_verify_rsne(rsne, hs->pmk_r0_name, authenticator_ie))
 | 
			
		||||
			goto ft_error;
 | 
			
		||||
	} else if (rsne)
 | 
			
		||||
		goto ft_error;
 | 
			
		||||
@ -482,7 +480,7 @@ int __ft_rx_associate(uint32_t ifindex, const uint8_t *frame, size_t frame_len)
 | 
			
		||||
				memcmp(msg4_rsne.pmkids, hs->pmk_r1_name, 16))
 | 
			
		||||
			return -EBADMSG;
 | 
			
		||||
 | 
			
		||||
		if (!handshake_util_ap_ie_matches(hs, &msg4_rsne,
 | 
			
		||||
		if (!handshake_util_ap_ie_matches(&msg4_rsne,
 | 
			
		||||
							hs->authenticator_ie,
 | 
			
		||||
							false))
 | 
			
		||||
			return -EBADMSG;
 | 
			
		||||
 | 
			
		||||
@ -368,12 +368,6 @@ void handshake_state_set_vendor_ies(struct handshake_state *s,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handshake_state_set_vendor_quirks(struct handshake_state *s,
 | 
			
		||||
					struct vendor_quirk quirks)
 | 
			
		||||
{
 | 
			
		||||
	s->vendor_quirks = quirks;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handshake_state_set_kh_ids(struct handshake_state *s,
 | 
			
		||||
				const uint8_t *r0khid, size_t r0khid_len,
 | 
			
		||||
				const uint8_t *r1khid)
 | 
			
		||||
@ -883,8 +877,7 @@ void handshake_state_set_igtk(struct handshake_state *s, const uint8_t *key,
 | 
			
		||||
 * results vs the RSN/WPA IE obtained as part of the 4-way handshake.  If they
 | 
			
		||||
 * don't match, the EAPoL packet must be silently discarded.
 | 
			
		||||
 */
 | 
			
		||||
bool handshake_util_ap_ie_matches(struct handshake_state *s,
 | 
			
		||||
					const struct ie_rsn_info *msg_info,
 | 
			
		||||
bool handshake_util_ap_ie_matches(const struct ie_rsn_info *msg_info,
 | 
			
		||||
					const uint8_t *scan_ie, bool is_wpa)
 | 
			
		||||
{
 | 
			
		||||
	struct ie_rsn_info scan_info;
 | 
			
		||||
@ -914,15 +907,11 @@ bool handshake_util_ap_ie_matches(struct handshake_state *s,
 | 
			
		||||
	if (msg_info->no_pairwise != scan_info.no_pairwise)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (!(s->vendor_quirks.replay_counter_mismatch)) {
 | 
			
		||||
		if (msg_info->ptksa_replay_counter !=
 | 
			
		||||
					scan_info.ptksa_replay_counter)
 | 
			
		||||
			return false;
 | 
			
		||||
	if (msg_info->ptksa_replay_counter != scan_info.ptksa_replay_counter)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
		if (msg_info->gtksa_replay_counter !=
 | 
			
		||||
					scan_info.gtksa_replay_counter)
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
	if (msg_info->gtksa_replay_counter != scan_info.gtksa_replay_counter)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (msg_info->mfpr != scan_info.mfpr)
 | 
			
		||||
		return false;
 | 
			
		||||
@ -1250,7 +1239,7 @@ static struct pmksa *handshake_state_steal_pmksa(struct handshake_state *s)
 | 
			
		||||
		s->have_pmksa = false;
 | 
			
		||||
 | 
			
		||||
		if (l_time_after(now, pmksa->expiration)) {
 | 
			
		||||
			pmksa_cache_free(pmksa);
 | 
			
		||||
			l_free(pmksa);
 | 
			
		||||
			pmksa = NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -1283,15 +1272,13 @@ void handshake_state_cache_pmksa(struct handshake_state *s)
 | 
			
		||||
{
 | 
			
		||||
	struct pmksa *pmksa = handshake_state_steal_pmksa(s);
 | 
			
		||||
 | 
			
		||||
	if (!pmksa) {
 | 
			
		||||
		l_debug("No PMKSA for "MAC, MAC_STR(s->aa));
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	l_debug("%p", pmksa);
 | 
			
		||||
 | 
			
		||||
	l_debug("Caching PMKSA for "MAC, MAC_STR(s->aa));
 | 
			
		||||
	if (!pmksa)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (L_WARN_ON(pmksa_cache_put(pmksa) < 0))
 | 
			
		||||
		pmksa_cache_free(pmksa);
 | 
			
		||||
		l_free(pmksa);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool handshake_state_remove_pmksa(struct handshake_state *s)
 | 
			
		||||
@ -1305,7 +1292,7 @@ bool handshake_state_remove_pmksa(struct handshake_state *s)
 | 
			
		||||
	if (!pmksa)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	pmksa_cache_free(pmksa);
 | 
			
		||||
	l_free(pmksa);
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,8 +26,6 @@
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <ell/cleanup.h>
 | 
			
		||||
 | 
			
		||||
#include "src/vendor_quirks.h"
 | 
			
		||||
 | 
			
		||||
struct handshake_state;
 | 
			
		||||
enum crypto_cipher;
 | 
			
		||||
struct eapol_frame;
 | 
			
		||||
@ -109,7 +107,6 @@ struct handshake_state {
 | 
			
		||||
	uint8_t *authenticator_fte;
 | 
			
		||||
	uint8_t *supplicant_fte;
 | 
			
		||||
	uint8_t *vendor_ies;
 | 
			
		||||
	struct vendor_quirk vendor_quirks;
 | 
			
		||||
	size_t vendor_ies_len;
 | 
			
		||||
	enum ie_rsn_cipher_suite pairwise_cipher;
 | 
			
		||||
	enum ie_rsn_cipher_suite group_cipher;
 | 
			
		||||
@ -240,9 +237,6 @@ void handshake_state_set_vendor_ies(struct handshake_state *s,
 | 
			
		||||
					const struct iovec *iov,
 | 
			
		||||
					size_t n_iovs);
 | 
			
		||||
 | 
			
		||||
void handshake_state_set_vendor_quirks(struct handshake_state *s,
 | 
			
		||||
					struct vendor_quirk quirks);
 | 
			
		||||
 | 
			
		||||
void handshake_state_set_kh_ids(struct handshake_state *s,
 | 
			
		||||
				const uint8_t *r0khid, size_t r0khid_len,
 | 
			
		||||
				const uint8_t *r1khid);
 | 
			
		||||
@ -318,8 +312,7 @@ bool handshake_state_set_pmksa(struct handshake_state *s, struct pmksa *pmksa);
 | 
			
		||||
void handshake_state_cache_pmksa(struct handshake_state *s);
 | 
			
		||||
bool handshake_state_remove_pmksa(struct handshake_state *s);
 | 
			
		||||
 | 
			
		||||
bool handshake_util_ap_ie_matches(struct handshake_state *s,
 | 
			
		||||
					const struct ie_rsn_info *msg_info,
 | 
			
		||||
bool handshake_util_ap_ie_matches(const struct ie_rsn_info *msg_info,
 | 
			
		||||
					const uint8_t *scan_ie, bool is_wpa);
 | 
			
		||||
 | 
			
		||||
const uint8_t *handshake_util_find_kde(enum handshake_kde selector,
 | 
			
		||||
 | 
			
		||||
@ -290,27 +290,9 @@ control how long a misbehaved BSS spends on the blacklist.
 | 
			
		||||
   * - InitialTimeout
 | 
			
		||||
     - Values: uint64 value in seconds (default: **60**)
 | 
			
		||||
 | 
			
		||||
       The initial time that a BSS spends on the blacklist. Setting this to zero
 | 
			
		||||
       will disable blacklisting functionality in IWD.
 | 
			
		||||
   * - InitialRoamRequestedTimeout (**deprecated**)
 | 
			
		||||
     - Values: uint64 value in seconds (default: **30**)
 | 
			
		||||
 | 
			
		||||
       This setting is deprecated, please use
 | 
			
		||||
       [Blacklist].InitialAccessPointBusyTimeout instead.
 | 
			
		||||
   * - InitialAccessPointBusyTimeout
 | 
			
		||||
     - Values: uint64 value in seconds (default: **30**)
 | 
			
		||||
 | 
			
		||||
       The initial time that a BSS will be blacklisted after indicating it
 | 
			
		||||
       cannot handle more connections. This is triggered by either a BSS
 | 
			
		||||
       transition management request (telling IWD to roam elsewhere) or by a
 | 
			
		||||
       denied authentication or association with the NO_MORE_STAS status code.
 | 
			
		||||
 | 
			
		||||
       Once a BSS is blacklisted in this manor IWD will attempt to avoid it for
 | 
			
		||||
       the configured amount of time.
 | 
			
		||||
 | 
			
		||||
       The initial time that a BSS spends on the blacklist.
 | 
			
		||||
   * - Multiplier
 | 
			
		||||
     - Values: unsigned int value greater than zero, in seconds
 | 
			
		||||
       (default: **30**)
 | 
			
		||||
     - Values: unsigned int value in seconds (default: **30**)
 | 
			
		||||
 | 
			
		||||
       If the BSS was blacklisted previously and another connection attempt
 | 
			
		||||
       has failed after the initial timeout has expired, then the BSS blacklist
 | 
			
		||||
@ -483,14 +465,6 @@ are buggy or just don't behave similar enough to the majority of other drivers.
 | 
			
		||||
       If a driver in use matches one in this list, multicast RX will be
 | 
			
		||||
       disabled.
 | 
			
		||||
 | 
			
		||||
   * - SaeDisable
 | 
			
		||||
     - Values: comma-separated list of drivers or glob matches
 | 
			
		||||
 | 
			
		||||
       If a driver in use matches one in this list, SAE/WPA3 will be disabled
 | 
			
		||||
       for connections. This will prevent connections to WPA3-only networks, but
 | 
			
		||||
       will allow for connections to WPA3/WPA2 hybrid networks by utilizing
 | 
			
		||||
       WPA2.
 | 
			
		||||
 | 
			
		||||
SEE ALSO
 | 
			
		||||
========
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										222
									
								
								src/netdev.c
									
									
									
									
									
								
							
							
						
						
									
										222
									
								
								src/netdev.c
									
									
									
									
									
								
							@ -463,14 +463,6 @@ uint8_t netdev_get_rssi_level_idx(struct netdev *netdev)
 | 
			
		||||
	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,
 | 
			
		||||
					const void *data,
 | 
			
		||||
					uint32_t len, void *user_data)
 | 
			
		||||
@ -637,6 +629,7 @@ static bool netdev_parse_sta_info(struct l_genl_attr *attr,
 | 
			
		||||
				info->have_tx_mcs = true;
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		case NL80211_STA_INFO_EXPECTED_THROUGHPUT:
 | 
			
		||||
			if (len != 4)
 | 
			
		||||
				return false;
 | 
			
		||||
@ -644,22 +637,6 @@ static bool netdev_parse_sta_info(struct l_genl_attr *attr,
 | 
			
		||||
			info->expected_throughput = l_get_u32(data);
 | 
			
		||||
			info->have_expected_throughput = true;
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		case NL80211_STA_INFO_INACTIVE_TIME:
 | 
			
		||||
			if (len != 4)
 | 
			
		||||
				return false;
 | 
			
		||||
 | 
			
		||||
			info->inactive_time = l_get_u32(data);
 | 
			
		||||
			info->have_inactive_time = true;
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		case NL80211_STA_INFO_CONNECTED_TIME:
 | 
			
		||||
			if (len != 4)
 | 
			
		||||
				return false;
 | 
			
		||||
 | 
			
		||||
			info->connected_time = l_get_u32(data);
 | 
			
		||||
			info->have_connected_time = true;
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@ -1124,7 +1101,6 @@ static void netdev_free(void *data)
 | 
			
		||||
		l_timeout_remove(netdev->rssi_poll_timeout);
 | 
			
		||||
 | 
			
		||||
	scan_wdev_remove(netdev->wdev_id);
 | 
			
		||||
	frame_watch_wdev_remove(netdev->wdev_id);
 | 
			
		||||
 | 
			
		||||
	watchlist_destroy(&netdev->station_watches);
 | 
			
		||||
 | 
			
		||||
@ -1522,105 +1498,6 @@ static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u",
 | 
			
		||||
@ -3008,26 +2885,13 @@ static void netdev_cmd_ft_reassociate_cb(struct l_genl_msg *msg,
 | 
			
		||||
						void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct netdev *netdev = user_data;
 | 
			
		||||
	int err = l_genl_msg_get_error(msg);
 | 
			
		||||
 | 
			
		||||
	netdev->connect_cmd_id = 0;
 | 
			
		||||
 | 
			
		||||
	l_debug("%d", err);
 | 
			
		||||
 | 
			
		||||
	if (err >= 0)
 | 
			
		||||
	if (l_genl_msg_get_error(msg) >= 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * TODO: It is possible to not trigger a disconnect here and maintain
 | 
			
		||||
	 *       the current connection. The issue is that IWD has already
 | 
			
		||||
	 *       modified the handshake and we've lost all reference to the old
 | 
			
		||||
	 *       BSS keys.
 | 
			
		||||
	 *
 | 
			
		||||
	 *       This could be remedied in the future by creating an entirely
 | 
			
		||||
	 *       new handshake_state object for the association and only when
 | 
			
		||||
	 *       the ack indicates success do we clear out the old object.
 | 
			
		||||
	 */
 | 
			
		||||
	netdev_disconnect_and_fail_connection(netdev,
 | 
			
		||||
	netdev_deauth_and_fail_connection(netdev,
 | 
			
		||||
					NETDEV_RESULT_ASSOCIATION_FAILED,
 | 
			
		||||
					MMPDU_STATUS_CODE_UNSPECIFIED);
 | 
			
		||||
}
 | 
			
		||||
@ -3619,13 +3483,6 @@ static void netdev_external_auth_sae_tx_associate(void *user_data)
 | 
			
		||||
 | 
			
		||||
	netdev_send_external_auth(netdev, MMPDU_STATUS_CODE_SUCCESS);
 | 
			
		||||
	netdev_ensure_eapol_registered(netdev);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Free the auth proto now. With external auth there is no associate
 | 
			
		||||
	 * event which is where this normally gets cleaned up.
 | 
			
		||||
	 */
 | 
			
		||||
	auth_proto_free(netdev->ap);
 | 
			
		||||
	netdev->ap = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct rtnl_data {
 | 
			
		||||
@ -3952,15 +3809,6 @@ static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
 | 
			
		||||
static int netdev_cqm_rssi_update(struct netdev *netdev)
 | 
			
		||||
{
 | 
			
		||||
	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("");
 | 
			
		||||
 | 
			
		||||
@ -5430,9 +5278,6 @@ static void netdev_channel_switch_event(struct l_genl_msg *msg,
 | 
			
		||||
	if (netdev->type != NL80211_IFTYPE_STATION)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (L_WARN_ON(!netdev->connected))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	chandef = l_new(struct band_chandef, 1);
 | 
			
		||||
 | 
			
		||||
	if (nl80211_parse_chandef(msg, chandef) < 0) {
 | 
			
		||||
@ -5482,39 +5327,6 @@ static void netdev_michael_mic_failure(struct l_genl_msg *msg,
 | 
			
		||||
	l_debug("ifindex=%u key_idx=%u type=%u", netdev->index, idx, type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define MAX_COMEBACK_DELAY 1200
 | 
			
		||||
 | 
			
		||||
static void netdev_assoc_comeback(struct l_genl_msg *msg,
 | 
			
		||||
					struct netdev *netdev)
 | 
			
		||||
{
 | 
			
		||||
	const uint8_t *mac;
 | 
			
		||||
	uint32_t timeout;
 | 
			
		||||
 | 
			
		||||
	if (L_WARN_ON(!netdev->connected))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (nl80211_parse_attrs(msg, NL80211_ATTR_MAC, &mac,
 | 
			
		||||
				NL80211_ATTR_TIMEOUT, &timeout,
 | 
			
		||||
				NL80211_ATTR_UNSPEC) < 0)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (L_WARN_ON(memcmp(mac, netdev->handshake->aa, ETH_ALEN)))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (timeout <= MAX_COMEBACK_DELAY) {
 | 
			
		||||
		l_debug(MAC" requested an association comeback delay of %u TU",
 | 
			
		||||
			MAC_STR(netdev->handshake->aa), timeout);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_debug("Comeback delay of %u exceeded maximum of %u, deauthenticating",
 | 
			
		||||
		timeout, MAX_COMEBACK_DELAY);
 | 
			
		||||
 | 
			
		||||
	netdev_deauth_and_fail_connection(netdev,
 | 
			
		||||
					NETDEV_RESULT_ASSOCIATION_FAILED,
 | 
			
		||||
					MMPDU_STATUS_CODE_REFUSED_TEMPORARILY);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct netdev *netdev = NULL;
 | 
			
		||||
@ -5568,9 +5380,6 @@ static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data)
 | 
			
		||||
	case NL80211_CMD_MICHAEL_MIC_FAILURE:
 | 
			
		||||
		netdev_michael_mic_failure(msg, netdev);
 | 
			
		||||
		break;
 | 
			
		||||
	case NL80211_CMD_ASSOC_COMEBACK:
 | 
			
		||||
		netdev_assoc_comeback(msg, netdev);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5688,18 +5497,23 @@ static void netdev_external_auth_event(struct l_genl_msg *msg,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (action == NL80211_EXTERNAL_AUTH_ABORT) {
 | 
			
		||||
		l_warn("External Auth Aborted");
 | 
			
		||||
		iwd_notice(IWD_NOTICE_CONNECT_INFO, "External Auth Aborted");
 | 
			
		||||
		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 ||
 | 
			
		||||
			memcmp(hs->ssid, ssid.iov_base, hs->ssid_len)) {
 | 
			
		||||
		l_warn("Target SSID mismatch");
 | 
			
		||||
		iwd_notice(IWD_NOTICE_CONNECT_INFO, "Target SSID mismatch");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (memcmp(hs->aa, bssid, ETH_ALEN)) {
 | 
			
		||||
		l_warn("Target BSSID mismatch");
 | 
			
		||||
		iwd_notice(IWD_NOTICE_CONNECT_INFO, "Target BSSID mismatch");
 | 
			
		||||
		goto error;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -6708,16 +6522,6 @@ struct netdev *netdev_create_from_genl(struct l_genl_msg *msg,
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6833,10 +6637,6 @@ static int netdev_init(void)
 | 
			
		||||
 | 
			
		||||
	__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,
 | 
			
		||||
						netdev_unicast_notify,
 | 
			
		||||
						NULL, NULL);
 | 
			
		||||
 | 
			
		||||
@ -158,7 +158,6 @@ const char *netdev_get_name(struct netdev *netdev);
 | 
			
		||||
bool netdev_get_is_up(struct netdev *netdev);
 | 
			
		||||
const char *netdev_get_path(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_get_handshake(struct netdev *netdev);
 | 
			
		||||
 | 
			
		||||
@ -168,11 +168,6 @@ static bool network_secret_check_cacheable(void *data, void *user_data)
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void network_clear_blacklist(struct network *network)
 | 
			
		||||
{
 | 
			
		||||
	l_queue_clear(network->blacklist, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void network_connected(struct network *network)
 | 
			
		||||
{
 | 
			
		||||
	enum security security = network_get_security(network);
 | 
			
		||||
@ -203,6 +198,8 @@ void network_connected(struct network *network)
 | 
			
		||||
	l_queue_foreach_remove(network->secrets,
 | 
			
		||||
				network_secret_check_cacheable, network);
 | 
			
		||||
 | 
			
		||||
	l_queue_clear(network->blacklist, NULL);
 | 
			
		||||
 | 
			
		||||
	network->provisioning_hidden = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -210,7 +207,7 @@ void network_disconnected(struct network *network)
 | 
			
		||||
{
 | 
			
		||||
	network_settings_close(network);
 | 
			
		||||
 | 
			
		||||
	network_clear_blacklist(network);
 | 
			
		||||
	l_queue_clear(network->blacklist, NULL);
 | 
			
		||||
 | 
			
		||||
	if (network->provisioning_hidden)
 | 
			
		||||
		station_hide_network(network->station, network);
 | 
			
		||||
@ -1283,8 +1280,7 @@ struct scan_bss *network_bss_select(struct network *network,
 | 
			
		||||
		if (l_queue_find(network->blacklist, match_bss, bss))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (blacklist_contains_bss(bss->addr,
 | 
			
		||||
					BLACKLIST_REASON_CONNECT_FAILED))
 | 
			
		||||
		if (blacklist_contains_bss(bss->addr))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* OWE Transition BSS */
 | 
			
		||||
 | 
			
		||||
@ -74,7 +74,6 @@ bool network_bss_update(struct network *network, struct scan_bss *bss);
 | 
			
		||||
const char *network_bss_get_path(const struct network *network,
 | 
			
		||||
						const struct scan_bss *bss);
 | 
			
		||||
bool network_bss_list_isempty(struct network *network);
 | 
			
		||||
void network_clear_blacklist(struct network *network);
 | 
			
		||||
 | 
			
		||||
const char *__network_path_append_bss(const char *network_path,
 | 
			
		||||
					const struct scan_bss *bss);
 | 
			
		||||
 | 
			
		||||
@ -177,7 +177,6 @@ static const struct {
 | 
			
		||||
	{ NL80211_CMD_UNPROT_BEACON,		"Unprotected Beacon"	},
 | 
			
		||||
	{ NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS,
 | 
			
		||||
						"Control Port TX Status" },
 | 
			
		||||
	{ NL80211_CMD_ASSOC_COMEBACK,		"Association comeback delay" },
 | 
			
		||||
	{ }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -190,7 +190,6 @@ static attr_handler handler_for_nl80211(int type)
 | 
			
		||||
	case NL80211_ATTR_CENTER_FREQ2:
 | 
			
		||||
	case NL80211_ATTR_AKM_SUITES:
 | 
			
		||||
	case NL80211_ATTR_EXTERNAL_AUTH_ACTION:
 | 
			
		||||
	case NL80211_ATTR_TIMEOUT:
 | 
			
		||||
		return extract_uint32;
 | 
			
		||||
	case NL80211_ATTR_FRAME:
 | 
			
		||||
		return extract_iovec;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										38
									
								
								src/pmksa.c
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								src/pmksa.c
									
									
									
									
									
								
							@ -40,9 +40,6 @@
 | 
			
		||||
 | 
			
		||||
static uint64_t dot11RSNAConfigPMKLifetime = 43200ULL * L_USEC_PER_SEC;
 | 
			
		||||
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 pmksa **data;
 | 
			
		||||
@ -145,7 +142,7 @@ int pmksa_cache_put(struct pmksa *pmksa)
 | 
			
		||||
	l_debug("Adding entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
 | 
			
		||||
 | 
			
		||||
	if (cache.used == cache.capacity) {
 | 
			
		||||
		pmksa_cache_free(cache.data[0]);
 | 
			
		||||
		l_free(cache.data[0]);
 | 
			
		||||
		cache.data[0] = pmksa;
 | 
			
		||||
		__minheap_sift_down(cache.data, cache.used, 0, &ops);
 | 
			
		||||
		return 0;
 | 
			
		||||
@ -155,9 +152,6 @@ int pmksa_cache_put(struct pmksa *pmksa)
 | 
			
		||||
	__minheap_sift_up(cache.data, cache.used, &ops);
 | 
			
		||||
	cache.used += 1;
 | 
			
		||||
 | 
			
		||||
	if (driver_add)
 | 
			
		||||
		driver_add(pmksa);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -173,7 +167,7 @@ int pmksa_cache_expire(uint64_t cutoff)
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < used; i++) {
 | 
			
		||||
		if (cache.data[i]->expiration <= cutoff) {
 | 
			
		||||
			pmksa_cache_free(cache.data[i]);
 | 
			
		||||
			l_free(cache.data[i]);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -196,30 +190,11 @@ int pmksa_cache_flush(void)
 | 
			
		||||
{
 | 
			
		||||
	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++)
 | 
			
		||||
		l_free(cache.data[i]);
 | 
			
		||||
 | 
			
		||||
	memset(cache.data, 0, cache.capacity * sizeof(struct pmksa *));
 | 
			
		||||
	cache.used = 0;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int pmksa_cache_free(struct pmksa *pmksa)
 | 
			
		||||
{
 | 
			
		||||
	if (driver_remove)
 | 
			
		||||
		driver_remove(pmksa);
 | 
			
		||||
 | 
			
		||||
	l_free(pmksa);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -242,15 +217,6 @@ void __pmksa_set_config(const struct l_settings *config)
 | 
			
		||||
					&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)
 | 
			
		||||
{
 | 
			
		||||
	cache.capacity = pmksa_cache_capacity;
 | 
			
		||||
 | 
			
		||||
@ -32,10 +32,6 @@ struct pmksa {
 | 
			
		||||
	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(const uint8_t spa[static 6],
 | 
			
		||||
@ -45,11 +41,6 @@ struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
 | 
			
		||||
int pmksa_cache_put(struct pmksa *pmksa);
 | 
			
		||||
int pmksa_cache_expire(uint64_t cutoff);
 | 
			
		||||
int pmksa_cache_flush(void);
 | 
			
		||||
int pmksa_cache_free(struct pmksa *pmksa);
 | 
			
		||||
 | 
			
		||||
uint64_t pmksa_lifetime(void);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										22
									
								
								src/sae.c
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/sae.c
									
									
									
									
									
								
							@ -169,14 +169,6 @@ static int sae_choose_next_group(struct sae_sm *sm)
 | 
			
		||||
				!sm->handshake->ecc_sae_pts[sm->group_retry])
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * TODO: Groups for P192, P224 and P521 are currently
 | 
			
		||||
		 * non-functional with SAE. Until this is fixed we need to
 | 
			
		||||
		 * avoid these groups from being used.
 | 
			
		||||
		 */
 | 
			
		||||
		if (group == 21 || group == 25 || group == 26)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -1002,8 +994,7 @@ static int sae_process_anti_clogging(struct sae_sm *sm, const uint8_t *ptr,
 | 
			
		||||
	sm->token_len = len;
 | 
			
		||||
	sm->sync = 0;
 | 
			
		||||
 | 
			
		||||
	if (L_WARN_ON(!sae_send_commit(sm, true)))
 | 
			
		||||
		return -EPROTO;
 | 
			
		||||
	sae_send_commit(sm, true);
 | 
			
		||||
 | 
			
		||||
	return -EAGAIN;
 | 
			
		||||
}
 | 
			
		||||
@ -1083,9 +1074,7 @@ static int sae_verify_committed(struct sae_sm *sm, uint16_t transaction,
 | 
			
		||||
			return -ETIMEDOUT;
 | 
			
		||||
 | 
			
		||||
		sm->sync++;
 | 
			
		||||
 | 
			
		||||
		if (L_WARN_ON(!sae_send_commit(sm, true)))
 | 
			
		||||
			return -EPROTO;
 | 
			
		||||
		sae_send_commit(sm, true);
 | 
			
		||||
 | 
			
		||||
		return -EAGAIN;
 | 
			
		||||
	}
 | 
			
		||||
@ -1140,9 +1129,7 @@ static int sae_verify_committed(struct sae_sm *sm, uint16_t transaction,
 | 
			
		||||
				sm->group);
 | 
			
		||||
 | 
			
		||||
		sm->sync = 0;
 | 
			
		||||
 | 
			
		||||
		if (L_WARN_ON(!sae_send_commit(sm, false)))
 | 
			
		||||
			return -EPROTO;
 | 
			
		||||
		sae_send_commit(sm, false);
 | 
			
		||||
 | 
			
		||||
		return -EAGAIN;
 | 
			
		||||
	}
 | 
			
		||||
@ -1307,8 +1294,7 @@ static int sae_verify_confirmed(struct sae_sm *sm, uint16_t trans,
 | 
			
		||||
	sm->sync++;
 | 
			
		||||
	sm->sc++;
 | 
			
		||||
 | 
			
		||||
	if (L_WARN_ON(!sae_send_commit(sm, true)))
 | 
			
		||||
		return -EPROTO;
 | 
			
		||||
	sae_send_commit(sm, true);
 | 
			
		||||
 | 
			
		||||
	if (!sae_send_confirm(sm))
 | 
			
		||||
		return -EPROTO;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								src/scan.c
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/scan.c
									
									
									
									
									
								
							@ -51,7 +51,6 @@
 | 
			
		||||
#include "src/mpdu.h"
 | 
			
		||||
#include "src/band.h"
 | 
			
		||||
#include "src/scan.h"
 | 
			
		||||
#include "src/vendor_quirks.h"
 | 
			
		||||
 | 
			
		||||
/* User configurable options */
 | 
			
		||||
static double RANK_2G_FACTOR;
 | 
			
		||||
@ -144,9 +143,9 @@ struct scan_survey {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct scan_survey_results {
 | 
			
		||||
	struct scan_survey survey_2_4[15];
 | 
			
		||||
	struct scan_survey survey_5[197];
 | 
			
		||||
	struct scan_survey survey_6[234];
 | 
			
		||||
	struct scan_survey survey_2_4[14];
 | 
			
		||||
	struct scan_survey survey_5[196];
 | 
			
		||||
	struct scan_survey survey_6[233];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct scan_results {
 | 
			
		||||
@ -415,8 +414,7 @@ static struct l_genl_msg *scan_build_cmd(struct scan_context *sc,
 | 
			
		||||
	if (params->ap_scan)
 | 
			
		||||
		flags |= NL80211_SCAN_FLAG_AP;
 | 
			
		||||
 | 
			
		||||
	if (wiphy_supports_colocated_flag(sc->wiphy))
 | 
			
		||||
		flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
 | 
			
		||||
	flags |= NL80211_SCAN_FLAG_COLOCATED_6GHZ;
 | 
			
		||||
 | 
			
		||||
	if (flags)
 | 
			
		||||
		l_genl_msg_append_attr(msg, NL80211_ATTR_SCAN_FLAGS, 4, &flags);
 | 
			
		||||
@ -1222,11 +1220,6 @@ static void scan_parse_vendor_specific(struct scan_bss *bss, const void *data,
 | 
			
		||||
	uint16_t cost_flags;
 | 
			
		||||
	bool dgaf_disable;
 | 
			
		||||
 | 
			
		||||
	if (L_WARN_ON(len < 3))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	vendor_quirks_append_for_oui(data, &bss->vendor_quirks);
 | 
			
		||||
 | 
			
		||||
	if (!bss->wpa && is_ie_wpa_ie(data, len)) {
 | 
			
		||||
		bss->wpa = l_memdup(data - 2, len + 2);
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,6 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "src/defs.h"
 | 
			
		||||
#include "src/vendor_quirks.h"
 | 
			
		||||
 | 
			
		||||
struct scan_freq_set;
 | 
			
		||||
struct ie_rsn_info;
 | 
			
		||||
@ -80,7 +79,6 @@ struct scan_bss {
 | 
			
		||||
	uint8_t *wfd;		/* Concatenated WFD IEs */
 | 
			
		||||
	ssize_t wfd_size;	/* Size of Concatenated WFD IEs */
 | 
			
		||||
	int8_t snr;
 | 
			
		||||
	struct vendor_quirk vendor_quirks;
 | 
			
		||||
	bool mde_present : 1;
 | 
			
		||||
	bool cc_present : 1;
 | 
			
		||||
	bool cap_rm_neighbor_report : 1;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										320
									
								
								src/station.c
									
									
									
									
									
								
							
							
						
						
									
										320
									
								
								src/station.c
									
									
									
									
									
								
							@ -64,7 +64,6 @@
 | 
			
		||||
#include "src/eap-tls-common.h"
 | 
			
		||||
#include "src/storage.h"
 | 
			
		||||
#include "src/pmksa.h"
 | 
			
		||||
#include "src/vendor_quirks.h"
 | 
			
		||||
 | 
			
		||||
#define STATION_RECENT_NETWORK_LIMIT	5
 | 
			
		||||
#define STATION_RECENT_FREQS_LIMIT	5
 | 
			
		||||
@ -156,54 +155,6 @@ struct anqp_entry {
 | 
			
		||||
	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_AP_BUSY);
 | 
			
		||||
	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
 | 
			
		||||
 * from station->bss_list is not 100% safe due to the possibility of the
 | 
			
		||||
@ -211,13 +162,13 @@ static uint32_t evaluate_bss_group_rank(const uint8_t *addr, uint32_t freq,
 | 
			
		||||
 */
 | 
			
		||||
struct roam_bss {
 | 
			
		||||
	uint8_t addr[6];
 | 
			
		||||
	uint32_t rank;
 | 
			
		||||
	uint16_t rank;
 | 
			
		||||
	int32_t signal_strength;
 | 
			
		||||
	bool ft_failed: 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct roam_bss *roam_bss_from_scan_bss(const struct scan_bss *bss,
 | 
			
		||||
						uint32_t rank)
 | 
			
		||||
						uint16_t rank)
 | 
			
		||||
{
 | 
			
		||||
	struct roam_bss *rbss = l_new(struct roam_bss, 1);
 | 
			
		||||
 | 
			
		||||
@ -1447,8 +1398,6 @@ static struct handshake_state *station_handshake_setup(struct station *station,
 | 
			
		||||
	vendor_ies = network_info_get_extra_ies(info, bss, &iov_elems);
 | 
			
		||||
	handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
 | 
			
		||||
 | 
			
		||||
	handshake_state_set_vendor_quirks(hs, bss->vendor_quirks);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * It can't hurt to try the FILS IP Address Assignment independent of
 | 
			
		||||
	 * which auth-proto is actually used.
 | 
			
		||||
@ -1798,15 +1747,6 @@ static void station_enter_state(struct station *station,
 | 
			
		||||
		periodic_scan_stop(station);
 | 
			
		||||
		break;
 | 
			
		||||
	case STATION_STATE_CONNECTED:
 | 
			
		||||
		network_clear_blacklist(station->connected_network);
 | 
			
		||||
 | 
			
		||||
		if (station->connect_pending) {
 | 
			
		||||
			struct l_dbus_message *reply =
 | 
			
		||||
				l_dbus_message_new_method_return(
 | 
			
		||||
						station->connect_pending);
 | 
			
		||||
			dbus_pending_reply(&station->connect_pending, reply);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		l_dbus_object_add_interface(dbus,
 | 
			
		||||
					netdev_get_path(station->netdev),
 | 
			
		||||
					IWD_STATION_DIAGNOSTIC_INTERFACE,
 | 
			
		||||
@ -2226,26 +2166,6 @@ static void station_early_neighbor_report_cb(struct netdev *netdev, int err,
 | 
			
		||||
				&station->roam_freqs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool station_try_next_bss(struct station *station)
 | 
			
		||||
{
 | 
			
		||||
	struct scan_bss *next;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	next = network_bss_select(station->connected_network, false);
 | 
			
		||||
 | 
			
		||||
	if (!next)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	ret = __station_connect_network(station, station->connected_network,
 | 
			
		||||
						next, station->state);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	l_debug("Attempting to connect to next BSS "MAC, MAC_STR(next->addr));
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool station_can_fast_transition(struct station *station,
 | 
			
		||||
					struct handshake_state *hs,
 | 
			
		||||
					struct scan_bss *bss)
 | 
			
		||||
@ -2288,26 +2208,28 @@ static bool station_can_fast_transition(struct station *station,
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void station_disconnect_on_netconfig_failed(struct netdev *netdev,
 | 
			
		||||
							bool success,
 | 
			
		||||
							void *user_data)
 | 
			
		||||
static void station_disconnect_on_error_cb(struct netdev *netdev, bool success,
 | 
			
		||||
					void *user_data)
 | 
			
		||||
{
 | 
			
		||||
	struct station *station = user_data;
 | 
			
		||||
 | 
			
		||||
	if (station_try_next_bss(station))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (station->connect_pending) {
 | 
			
		||||
		struct l_dbus_message *reply = dbus_error_failed(
 | 
			
		||||
						station->connect_pending);
 | 
			
		||||
 | 
			
		||||
		dbus_pending_reply(&station->connect_pending, reply);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	station_reset_connection_state(station);
 | 
			
		||||
	bool continue_autoconnect;
 | 
			
		||||
 | 
			
		||||
	station_enter_state(station, STATION_STATE_DISCONNECTED);
 | 
			
		||||
	station_enter_state(station, STATION_STATE_AUTOCONNECT_FULL);
 | 
			
		||||
 | 
			
		||||
	continue_autoconnect = station->state == STATION_STATE_CONNECTING_AUTO;
 | 
			
		||||
 | 
			
		||||
	if (continue_autoconnect) {
 | 
			
		||||
		if (station_autoconnect_next(station) < 0) {
 | 
			
		||||
			l_debug("Nothing left on autoconnect list");
 | 
			
		||||
			station_enter_state(station,
 | 
			
		||||
					STATION_STATE_AUTOCONNECT_FULL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (station->autoconnect)
 | 
			
		||||
		station_enter_state(station, STATION_STATE_AUTOCONNECT_QUICK);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void station_netconfig_event_handler(enum netconfig_event event,
 | 
			
		||||
@ -2320,20 +2242,23 @@ static void station_netconfig_event_handler(enum netconfig_event event,
 | 
			
		||||
		station_enter_state(station, STATION_STATE_CONNECTED);
 | 
			
		||||
		break;
 | 
			
		||||
	case NETCONFIG_EVENT_FAILED:
 | 
			
		||||
		station_debug_event(station, "netconfig-failed");
 | 
			
		||||
		if (station->connect_pending) {
 | 
			
		||||
			struct l_dbus_message *reply = dbus_error_failed(
 | 
			
		||||
						station->connect_pending);
 | 
			
		||||
 | 
			
		||||
		netconfig_reset(station->netconfig);
 | 
			
		||||
			dbus_pending_reply(&station->connect_pending, reply);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (station->state == STATION_STATE_NETCONFIG)
 | 
			
		||||
			network_connect_failed(station->connected_network,
 | 
			
		||||
						false);
 | 
			
		||||
 | 
			
		||||
		network_blacklist_add(station->connected_network,
 | 
			
		||||
						station->connected_bss);
 | 
			
		||||
 | 
			
		||||
		netdev_disconnect(station->netdev,
 | 
			
		||||
					station_disconnect_on_netconfig_failed,
 | 
			
		||||
					station_disconnect_on_error_cb,
 | 
			
		||||
					station);
 | 
			
		||||
		station_reset_connection_state(station);
 | 
			
		||||
 | 
			
		||||
		station_enter_state(station, STATION_STATE_DISCONNECTING);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		l_error("station: Unsupported netconfig event: %d.", event);
 | 
			
		||||
@ -2407,11 +2332,6 @@ static void station_roam_retry(struct station *station)
 | 
			
		||||
	station->roam_scan_full = false;
 | 
			
		||||
	station->ap_directed_roaming = false;
 | 
			
		||||
 | 
			
		||||
	if (station->roam_freqs) {
 | 
			
		||||
		scan_freq_set_free(station->roam_freqs);
 | 
			
		||||
		station->roam_freqs = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (station->signal_low)
 | 
			
		||||
		station_roam_timeout_rearm(station, roam_retry_interval);
 | 
			
		||||
}
 | 
			
		||||
@ -2441,16 +2361,8 @@ static void station_roam_failed(struct station *station)
 | 
			
		||||
	 * We were told by the AP to roam, but failed.  Try ourselves or
 | 
			
		||||
	 * wait for the AP to tell us to roam again
 | 
			
		||||
	 */
 | 
			
		||||
	if (station->ap_directed_roaming) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * The candidate list from the AP (or neighbor report) found
 | 
			
		||||
		 * no BSS's. Force a full scan
 | 
			
		||||
		 */
 | 
			
		||||
		if (!station->roam_scan_full)
 | 
			
		||||
			goto full_scan;
 | 
			
		||||
 | 
			
		||||
	if (station->ap_directed_roaming)
 | 
			
		||||
		goto delayed_retry;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If we tried a limited scan, failed and the signal is still low,
 | 
			
		||||
@ -2462,7 +2374,6 @@ static void station_roam_failed(struct station *station)
 | 
			
		||||
		 * the scan here, so that the destroy callback is not called
 | 
			
		||||
		 * after the return of this function
 | 
			
		||||
		 */
 | 
			
		||||
full_scan:
 | 
			
		||||
		scan_cancel(netdev_get_wdev_id(station->netdev),
 | 
			
		||||
						station->roam_scan_id);
 | 
			
		||||
 | 
			
		||||
@ -2750,15 +2661,11 @@ static bool station_try_next_transition(struct station *station,
 | 
			
		||||
	enum security security = network_get_security(connected);
 | 
			
		||||
	struct handshake_state *new_hs;
 | 
			
		||||
	struct ie_rsn_info cur_rsne, target_rsne;
 | 
			
		||||
	const char *vendor_quirks = vendor_quirks_to_string(bss->vendor_quirks);
 | 
			
		||||
 | 
			
		||||
	iwd_notice(IWD_NOTICE_ROAM_INFO, "bss: "MAC", signal: %d, load: %d/255",
 | 
			
		||||
					MAC_STR(bss->addr),
 | 
			
		||||
					bss->signal_strength / 100,
 | 
			
		||||
					bss->utilization);
 | 
			
		||||
	if (vendor_quirks)
 | 
			
		||||
		l_debug("vendor quirks for "MAC": %s",
 | 
			
		||||
				MAC_STR(bss->addr), vendor_quirks);
 | 
			
		||||
 | 
			
		||||
	/* Reset AP roam flag, at this point the roaming behaves the same */
 | 
			
		||||
	station->ap_directed_roaming = false;
 | 
			
		||||
@ -2898,7 +2805,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 | 
			
		||||
	struct handshake_state *hs = netdev_get_handshake(station->netdev);
 | 
			
		||||
	struct scan_bss *current_bss = station->connected_bss;
 | 
			
		||||
	struct scan_bss *bss;
 | 
			
		||||
	uint32_t cur_bss_group_rank = 0;
 | 
			
		||||
	double cur_bss_rank = 0.0;
 | 
			
		||||
	static const double RANK_FT_FACTOR = 1.3;
 | 
			
		||||
	uint16_t mdid;
 | 
			
		||||
	enum security orig_security, security;
 | 
			
		||||
@ -2927,15 +2834,10 @@ 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);
 | 
			
		||||
	if (bss && !station->ap_directed_roaming) {
 | 
			
		||||
		double cur_bss_rank = bss->rank;
 | 
			
		||||
		cur_bss_rank = bss->rank;
 | 
			
		||||
 | 
			
		||||
		if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
 | 
			
		||||
			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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
@ -2957,7 +2859,6 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 | 
			
		||||
	while ((bss = l_queue_pop_head(bss_list))) {
 | 
			
		||||
		double rank;
 | 
			
		||||
		struct roam_bss *rbss;
 | 
			
		||||
		uint32_t group_rank;
 | 
			
		||||
 | 
			
		||||
		station_print_scan_bss(bss);
 | 
			
		||||
 | 
			
		||||
@ -2979,8 +2880,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 | 
			
		||||
		if (network_can_connect_bss(network, bss) < 0)
 | 
			
		||||
			goto next;
 | 
			
		||||
 | 
			
		||||
		if (blacklist_contains_bss(bss->addr,
 | 
			
		||||
					BLACKLIST_REASON_CONNECT_FAILED))
 | 
			
		||||
		if (blacklist_contains_bss(bss->addr))
 | 
			
		||||
			goto next;
 | 
			
		||||
 | 
			
		||||
		rank = bss->rank;
 | 
			
		||||
@ -2988,11 +2888,7 @@ 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)
 | 
			
		||||
			rank *= RANK_FT_FACTOR;
 | 
			
		||||
 | 
			
		||||
		group_rank = evaluate_bss_group_rank(bss->addr, bss->frequency,
 | 
			
		||||
						bss->signal_strength,
 | 
			
		||||
						(uint16_t) rank);
 | 
			
		||||
 | 
			
		||||
		if (group_rank <= cur_bss_group_rank)
 | 
			
		||||
		if (rank <= cur_bss_rank)
 | 
			
		||||
			goto next;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
@ -3001,7 +2897,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
 | 
			
		||||
		 */
 | 
			
		||||
		station_update_roam_bss(station, bss);
 | 
			
		||||
 | 
			
		||||
		rbss = roam_bss_from_scan_bss(bss, group_rank);
 | 
			
		||||
		rbss = roam_bss_from_scan_bss(bss, rank);
 | 
			
		||||
 | 
			
		||||
		l_queue_insert(station->roam_bss_list, rbss,
 | 
			
		||||
				roam_bss_rank_compare, NULL);
 | 
			
		||||
@ -3059,7 +2955,6 @@ static int station_roam_scan(struct station *station,
 | 
			
		||||
	if (!freq_set) {
 | 
			
		||||
		station->roam_scan_full = true;
 | 
			
		||||
		params.freqs = allowed;
 | 
			
		||||
		station_debug_event(station, "full-roam-scan");
 | 
			
		||||
	} else
 | 
			
		||||
		scan_freq_set_constrain(freq_set, allowed);
 | 
			
		||||
 | 
			
		||||
@ -3270,12 +3165,12 @@ static void station_ap_directed_roam(struct station *station,
 | 
			
		||||
	uint8_t req_mode;
 | 
			
		||||
	uint16_t dtimer;
 | 
			
		||||
	uint8_t valid_interval;
 | 
			
		||||
	bool can_roam = !station_cannot_roam(station);
 | 
			
		||||
	bool ignore_candidates =
 | 
			
		||||
		station->connected_bss->vendor_quirks.ignore_bss_tm_candidates;
 | 
			
		||||
 | 
			
		||||
	l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
 | 
			
		||||
 | 
			
		||||
	if (station_cannot_roam(station))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (station->state != STATION_STATE_CONNECTED) {
 | 
			
		||||
		l_debug("roam: unexpected AP directed roam -- ignore");
 | 
			
		||||
		return;
 | 
			
		||||
@ -3339,13 +3234,8 @@ static void station_ap_directed_roam(struct station *station,
 | 
			
		||||
	 * disassociating us. If either of these bits are set, set the
 | 
			
		||||
	 * ap_directed_roaming flag. Otherwise still try roaming but don't
 | 
			
		||||
	 * 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 (can_roam && req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT |
 | 
			
		||||
	if (req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT |
 | 
			
		||||
			WNM_REQUEST_MODE_TERMINATION_IMMINENT |
 | 
			
		||||
			WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT))
 | 
			
		||||
		station->ap_directed_roaming = true;
 | 
			
		||||
@ -3372,39 +3262,17 @@ static void station_ap_directed_roam(struct station *station,
 | 
			
		||||
		pos += url_len;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	blacklist_add_bss(station->connected_bss->addr,
 | 
			
		||||
				BLACKLIST_REASON_AP_BUSY);
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	l_timeout_remove(station->roam_trigger_timeout);
 | 
			
		||||
	station->roam_trigger_timeout = NULL;
 | 
			
		||||
 | 
			
		||||
	if ((req_mode & WNM_REQUEST_MODE_PREFERRED_CANDIDATE_LIST) &&
 | 
			
		||||
				!ignore_candidates) {
 | 
			
		||||
	if (req_mode & WNM_REQUEST_MODE_PREFERRED_CANDIDATE_LIST) {
 | 
			
		||||
		l_debug("roam: AP sent a preferred candidate list");
 | 
			
		||||
		station_neighbor_report_cb(station->netdev, 0, body + pos,
 | 
			
		||||
				body_len - pos, station);
 | 
			
		||||
	} else {
 | 
			
		||||
		if (station->connected_bss->cap_rm_neighbor_report) {
 | 
			
		||||
			if (!netdev_neighbor_report_req(station->netdev,
 | 
			
		||||
					station_neighbor_report_cb))
 | 
			
		||||
				return;
 | 
			
		||||
 | 
			
		||||
			l_warn("failed to request neighbor report!");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		l_debug("full scan after BSS transition request");
 | 
			
		||||
		l_debug("roam: AP did not include a preferred candidate list");
 | 
			
		||||
		if (station_roam_scan(station, NULL) < 0)
 | 
			
		||||
			station_roam_failed(station);
 | 
			
		||||
	}
 | 
			
		||||
@ -3466,6 +3334,26 @@ static void station_event_channel_switched(struct station *station,
 | 
			
		||||
	network_bss_update(network, station->connected_bss);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool station_try_next_bss(struct station *station)
 | 
			
		||||
{
 | 
			
		||||
	struct scan_bss *next;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	next = network_bss_select(station->connected_network, false);
 | 
			
		||||
 | 
			
		||||
	if (!next)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	ret = __station_connect_network(station, station->connected_network,
 | 
			
		||||
						next, station->state);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	l_debug("Attempting to connect to next BSS "MAC, MAC_STR(next->addr));
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool station_retry_owe_default_group(struct station *station)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
@ -3512,15 +3400,7 @@ static bool station_retry_with_reason(struct station *station,
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	blacklist_add_bss(station->connected_bss->addr);
 | 
			
		||||
 | 
			
		||||
try_next:
 | 
			
		||||
	return station_try_next_bss(station);
 | 
			
		||||
@ -3559,54 +3439,32 @@ static bool station_pmksa_fallback(struct station *station, uint16_t status)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* A bit more concise for trying to fit these into 80 characters */
 | 
			
		||||
#define IS_TEMPORARY_STATUS(code) \
 | 
			
		||||
	((code) == MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH || \
 | 
			
		||||
	(code) == MMPDU_STATUS_CODE_DENIED_POOR_CHAN_CONDITIONS || \
 | 
			
		||||
	(code) == MMPDU_STATUS_CODE_REJECTED_WITH_SUGG_BSS_TRANS || \
 | 
			
		||||
	(code) == MMPDU_STATUS_CODE_DENIED_NO_MORE_STAS)
 | 
			
		||||
 | 
			
		||||
static bool station_retry_with_status(struct station *station,
 | 
			
		||||
					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;
 | 
			
		||||
 | 
			
		||||
	switch (status_code) {
 | 
			
		||||
	/*
 | 
			
		||||
	 * Certain Auth/Assoc failures should not cause a timeout blacklist.
 | 
			
		||||
	 * In these cases we want to only temporarily blacklist the BSS until
 | 
			
		||||
	 * the connection is complete.
 | 
			
		||||
	 */
 | 
			
		||||
	case MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH:
 | 
			
		||||
	case MMPDU_STATUS_CODE_DENIED_POOR_CHAN_CONDITIONS:
 | 
			
		||||
	/*
 | 
			
		||||
	 *
 | 
			
		||||
	 * TODO: The WITH_SUGG_BSS_TRANS case should also include a neighbor
 | 
			
		||||
	 *       report IE in the frame. This would allow us to target a
 | 
			
		||||
	 *       specific BSS on our next attempt. There is currently no way to
 | 
			
		||||
	 *       obtain that IE, but this should be done in the future.
 | 
			
		||||
	*/
 | 
			
		||||
	case MMPDU_STATUS_CODE_REJECTED_WITH_SUGG_BSS_TRANS:
 | 
			
		||||
		break;
 | 
			
		||||
	/*
 | 
			
		||||
	 * If a BSS is indicating its unable to handle more connections we will
 | 
			
		||||
	 * blacklist this the same way we do for BSS's issuing BSS-TM requests
 | 
			
		||||
	 * thereby avoiding roams to this BSS for the configured timeout.
 | 
			
		||||
	 */
 | 
			
		||||
	case MMPDU_STATUS_CODE_DENIED_NO_MORE_STAS:
 | 
			
		||||
		blacklist_add_bss(station->connected_bss->addr,
 | 
			
		||||
					BLACKLIST_REASON_AP_BUSY);
 | 
			
		||||
		break;
 | 
			
		||||
	/* For any other status codes, blacklist the BSS */
 | 
			
		||||
	default:
 | 
			
		||||
		blacklist_add_bss(station->connected_bss->addr,
 | 
			
		||||
					BLACKLIST_REASON_CONNECT_FAILED);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	if (IS_TEMPORARY_STATUS(status_code))
 | 
			
		||||
		network_blacklist_add(station->connected_network,
 | 
			
		||||
						station->connected_bss);
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	return station_try_next_bss(station);
 | 
			
		||||
@ -3618,6 +3476,13 @@ static void station_connect_ok(struct station *station)
 | 
			
		||||
 | 
			
		||||
	l_debug("");
 | 
			
		||||
 | 
			
		||||
	if (station->connect_pending) {
 | 
			
		||||
		struct l_dbus_message *reply =
 | 
			
		||||
			l_dbus_message_new_method_return(
 | 
			
		||||
						station->connect_pending);
 | 
			
		||||
		dbus_pending_reply(&station->connect_pending, reply);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Get a neighbor report now so future roams can avoid waiting for
 | 
			
		||||
	 * a report at that time
 | 
			
		||||
@ -3684,8 +3549,7 @@ static void station_connect_cb(struct netdev *netdev, enum netdev_result result,
 | 
			
		||||
 | 
			
		||||
	switch (result) {
 | 
			
		||||
	case NETDEV_RESULT_OK:
 | 
			
		||||
		blacklist_remove_bss(station->connected_bss->addr,
 | 
			
		||||
					BLACKLIST_REASON_CONNECT_FAILED);
 | 
			
		||||
		blacklist_remove_bss(station->connected_bss->addr);
 | 
			
		||||
		station_connect_ok(station);
 | 
			
		||||
		return;
 | 
			
		||||
	case NETDEV_RESULT_DISCONNECTED:
 | 
			
		||||
@ -3917,7 +3781,6 @@ int __station_connect_network(struct station *station, struct network *network,
 | 
			
		||||
{
 | 
			
		||||
	struct handshake_state *hs;
 | 
			
		||||
	int r;
 | 
			
		||||
	const char *vendor_quirks = vendor_quirks_to_string(bss->vendor_quirks);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If we already have a handshake_state ref this is due to a retry,
 | 
			
		||||
@ -3952,10 +3815,6 @@ int __station_connect_network(struct station *station, struct network *network,
 | 
			
		||||
					bss->signal_strength / 100,
 | 
			
		||||
					bss->utilization);
 | 
			
		||||
 | 
			
		||||
	if (vendor_quirks)
 | 
			
		||||
		l_debug("vendor quirks for "MAC": %s",
 | 
			
		||||
				MAC_STR(bss->addr), vendor_quirks);
 | 
			
		||||
 | 
			
		||||
	station->connected_bss = bss;
 | 
			
		||||
	station->connected_network = network;
 | 
			
		||||
	station->hs = handshake_state_ref(hs);
 | 
			
		||||
@ -4860,7 +4719,7 @@ static struct l_dbus_message *station_property_set_affinities(
 | 
			
		||||
	struct l_dbus_message_iter array;
 | 
			
		||||
	const char *sender = l_dbus_message_get_sender(message);
 | 
			
		||||
	char *old_path = l_queue_peek_head(station->affinities);
 | 
			
		||||
	const char *new_path;
 | 
			
		||||
	const char *new_path = NULL;
 | 
			
		||||
	struct scan_bss *new_bss = NULL;
 | 
			
		||||
	struct scan_bss *old_bss = NULL;
 | 
			
		||||
	bool lower_threshold = false;
 | 
			
		||||
@ -4880,15 +4739,10 @@ static struct l_dbus_message *station_property_set_affinities(
 | 
			
		||||
	if (!l_dbus_message_iter_get_variant(new_value, "ao", &array))
 | 
			
		||||
		return dbus_error_invalid_args(message);
 | 
			
		||||
 | 
			
		||||
	/* Get first entry, or if an empty array set the path to NULL */
 | 
			
		||||
	if (!l_dbus_message_iter_next_entry(&array, &new_path))
 | 
			
		||||
		new_path = NULL;
 | 
			
		||||
	/* Get first entry, there should be only one */
 | 
			
		||||
	l_dbus_message_iter_next_entry(&array, &new_path);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Only allowing single values for now. If there is more than a single
 | 
			
		||||
	 * value, fail
 | 
			
		||||
	 */
 | 
			
		||||
	if (new_path && 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);
 | 
			
		||||
 | 
			
		||||
	old_path = l_queue_peek_head(station->affinities);
 | 
			
		||||
 | 
			
		||||
@ -500,13 +500,6 @@ int __storage_decrypt(struct l_settings *settings, const char *ssid,
 | 
			
		||||
		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
 | 
			
		||||
	 * the decrypted data portion. We add one here for the NULL terminator
 | 
			
		||||
 | 
			
		||||
@ -1,84 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  Wireless daemon for Linux
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2025  Locus Robotics Corporation. 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 <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <ell/ell.h>
 | 
			
		||||
 | 
			
		||||
#include "src/vendor_quirks.h"
 | 
			
		||||
 | 
			
		||||
static const struct {
 | 
			
		||||
	uint8_t oui[3];
 | 
			
		||||
	struct vendor_quirk quirks;
 | 
			
		||||
} oui_quirk_db[] = {
 | 
			
		||||
	{
 | 
			
		||||
		/* Cisco Meraki */
 | 
			
		||||
		{ 0x00, 0x18, 0x0a },
 | 
			
		||||
		{ .ignore_bss_tm_candidates = true },
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		/* Hewlett Packard, owns Aruba */
 | 
			
		||||
		{ 0x00, 0x0b, 0x86 },
 | 
			
		||||
		{ .replay_counter_mismatch = true },
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void vendor_quirks_append_for_oui(const uint8_t *oui,
 | 
			
		||||
					struct vendor_quirk *quirks)
 | 
			
		||||
{
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < L_ARRAY_SIZE(oui_quirk_db); i++) {
 | 
			
		||||
		const struct vendor_quirk *quirk = &oui_quirk_db[i].quirks;
 | 
			
		||||
 | 
			
		||||
		if (memcmp(oui_quirk_db[i].oui, oui, 3))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		quirks->ignore_bss_tm_candidates |=
 | 
			
		||||
				quirk->ignore_bss_tm_candidates;
 | 
			
		||||
		quirks->replay_counter_mismatch |=
 | 
			
		||||
				quirk->replay_counter_mismatch;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *vendor_quirks_to_string(struct vendor_quirk quirks)
 | 
			
		||||
{
 | 
			
		||||
	static char out[1024];
 | 
			
		||||
	char *pos = out;
 | 
			
		||||
	size_t s = 0;
 | 
			
		||||
 | 
			
		||||
	if (quirks.ignore_bss_tm_candidates)
 | 
			
		||||
		s += snprintf(pos, sizeof(out) - s, "IgnoreBssTmCandidateList");
 | 
			
		||||
 | 
			
		||||
	if (quirks.replay_counter_mismatch)
 | 
			
		||||
		s += snprintf(pos, sizeof(out) - s, "ReplayCounterMismatch");
 | 
			
		||||
 | 
			
		||||
	if (!s)
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	return out;
 | 
			
		||||
}
 | 
			
		||||
@ -1,39 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  Wireless daemon for Linux
 | 
			
		||||
 *
 | 
			
		||||
 *  Copyright (C) 2025  Locus Robotics Corporation. 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
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef __IWD_VENDOR_QUIRKS_H
 | 
			
		||||
#define __IWD_VENDOR_QUIRKS_H
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
struct vendor_quirk {
 | 
			
		||||
	bool ignore_bss_tm_candidates : 1;
 | 
			
		||||
	bool replay_counter_mismatch : 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void vendor_quirks_append_for_oui(const uint8_t *oui,
 | 
			
		||||
					struct vendor_quirk *quirks);
 | 
			
		||||
 | 
			
		||||
const char *vendor_quirks_to_string(struct vendor_quirk quirks);
 | 
			
		||||
 | 
			
		||||
#endif /* __IWD_VENDOR_QUIRKS_H */
 | 
			
		||||
							
								
								
									
										44
									
								
								src/wiphy.c
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								src/wiphy.c
									
									
									
									
									
								
							@ -69,26 +69,11 @@ static uint32_t work_ids;
 | 
			
		||||
static unsigned int wiphy_dump_id;
 | 
			
		||||
 | 
			
		||||
enum driver_flag {
 | 
			
		||||
	/* Force the use of the default interface created by the kernel */
 | 
			
		||||
	DEFAULT_IF = 0x1,
 | 
			
		||||
	/*
 | 
			
		||||
	 * Force the use of the PAE socket rather than control port, even if
 | 
			
		||||
	 * control port is supported
 | 
			
		||||
	 */
 | 
			
		||||
	FORCE_PAE = 0x2,
 | 
			
		||||
	/* Disable power save on the adapter during initialization */
 | 
			
		||||
	POWER_SAVE_DISABLE = 0x4,
 | 
			
		||||
	/* Don't use OWE when connecting to open networks */
 | 
			
		||||
	OWE_DISABLE = 0x8,
 | 
			
		||||
	/* Disables multicast RX frame registration */
 | 
			
		||||
	MULTICAST_RX_DISABLE = 0x10,
 | 
			
		||||
	/*
 | 
			
		||||
	 * Don't use SAE (WPA3) when connecting to hybrid networks. This will
 | 
			
		||||
	 * prevent IWD from connecting to WPA3-only networks
 | 
			
		||||
	 */
 | 
			
		||||
	SAE_DISABLE = 0x20,
 | 
			
		||||
	/* Disables use of the NL80211_SCAN_FLAG_COLOCATED_6GHZ flag in scans */
 | 
			
		||||
	COLOCATED_SCAN_DISABLE = 0x40,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct driver_flag_name {
 | 
			
		||||
@ -117,13 +102,11 @@ static const struct driver_info driver_infos[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct driver_flag_name driver_flag_names[] = {
 | 
			
		||||
	{ "DefaultInterface",     DEFAULT_IF },
 | 
			
		||||
	{ "ForcePae",             FORCE_PAE },
 | 
			
		||||
	{ "PowerSaveDisable",     POWER_SAVE_DISABLE },
 | 
			
		||||
	{ "OweDisable",           OWE_DISABLE },
 | 
			
		||||
	{ "MulticastRxDisable",   MULTICAST_RX_DISABLE },
 | 
			
		||||
	{ "SaeDisable",           SAE_DISABLE },
 | 
			
		||||
	{ "ColocatedScanDisable", COLOCATED_SCAN_DISABLE },
 | 
			
		||||
	{ "DefaultInterface",   DEFAULT_IF },
 | 
			
		||||
	{ "ForcePae",           FORCE_PAE },
 | 
			
		||||
	{ "PowerSaveDisable",   POWER_SAVE_DISABLE },
 | 
			
		||||
	{ "OweDisable",         OWE_DISABLE },
 | 
			
		||||
	{ "MulticastRxDisable", MULTICAST_RX_DISABLE }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct wiphy {
 | 
			
		||||
@ -219,9 +202,6 @@ uint16_t wiphy_get_supported_ciphers(struct wiphy *wiphy, uint16_t mask)
 | 
			
		||||
 | 
			
		||||
static bool wiphy_can_connect_sae(struct wiphy *wiphy)
 | 
			
		||||
{
 | 
			
		||||
	if (wiphy->driver_flags & SAE_DISABLE)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * WPA3 Specification version 3, Section 2.2:
 | 
			
		||||
	 * A STA shall not enable WEP and TKIP
 | 
			
		||||
@ -978,11 +958,6 @@ bool wiphy_supports_multicast_rx(const struct wiphy *wiphy)
 | 
			
		||||
				!(wiphy->driver_flags & MULTICAST_RX_DISABLE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool wiphy_supports_colocated_flag(const struct wiphy *wiphy)
 | 
			
		||||
{
 | 
			
		||||
	return !(wiphy->driver_flags & COLOCATED_SCAN_DISABLE);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy,
 | 
			
		||||
						enum band_freq band,
 | 
			
		||||
						size_t *size)
 | 
			
		||||
@ -1396,15 +1371,6 @@ static void wiphy_print_basic_info(struct wiphy *wiphy)
 | 
			
		||||
		if (wiphy->driver_flags & OWE_DISABLE)
 | 
			
		||||
			flags = l_strv_append(flags, "OweDisable");
 | 
			
		||||
 | 
			
		||||
		if (wiphy->driver_flags & MULTICAST_RX_DISABLE)
 | 
			
		||||
			flags = l_strv_append(flags, "MulticastRxDisable");
 | 
			
		||||
 | 
			
		||||
		if (wiphy->driver_flags & SAE_DISABLE)
 | 
			
		||||
			flags = l_strv_append(flags, "SaeDisable");
 | 
			
		||||
 | 
			
		||||
		if (wiphy->driver_flags & COLOCATED_SCAN_DISABLE)
 | 
			
		||||
			flags = l_strv_append(flags, "ColocatedScanDisable");
 | 
			
		||||
 | 
			
		||||
		joined = l_strjoinv(flags, ' ');
 | 
			
		||||
 | 
			
		||||
		l_info("\tDriver Flags: %s", joined);
 | 
			
		||||
 | 
			
		||||
@ -144,7 +144,6 @@ bool wiphy_country_is_unknown(struct wiphy *wiphy);
 | 
			
		||||
bool wiphy_supports_uapsd(const struct wiphy *wiphy);
 | 
			
		||||
bool wiphy_supports_cmd_offchannel(const struct wiphy *wiphy);
 | 
			
		||||
bool wiphy_supports_multicast_rx(const struct wiphy *wiphy);
 | 
			
		||||
bool wiphy_supports_colocated_flag(const struct wiphy *wiphy);
 | 
			
		||||
 | 
			
		||||
const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy,
 | 
			
		||||
						enum band_freq band,
 | 
			
		||||
 | 
			
		||||
@ -138,22 +138,20 @@ static const struct cmac_data example_4 = {
 | 
			
		||||
	.tag_len	= sizeof(tag_4),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_checksum_cmac_aes_supported();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
 | 
			
		||||
							func, test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("/cmac-aes/Example 1", cmac_test, &example_1);
 | 
			
		||||
	add_test("/cmac-aes/Example 2", cmac_test, &example_2);
 | 
			
		||||
	add_test("/cmac-aes/Example 3", cmac_test, &example_3);
 | 
			
		||||
	add_test("/cmac-aes/Example 4", cmac_test, &example_4);
 | 
			
		||||
	if (!l_checksum_cmac_aes_supported()) {
 | 
			
		||||
		printf("AES-CMAC support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("/cmac-aes/Example 1", cmac_test, &example_1);
 | 
			
		||||
	l_test_add("/cmac-aes/Example 2", cmac_test, &example_2);
 | 
			
		||||
	l_test_add("/cmac-aes/Example 3", cmac_test, &example_3);
 | 
			
		||||
	l_test_add("/cmac-aes/Example 4", cmac_test, &example_4);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -403,32 +403,40 @@ static void aes_siv_test(const void *data)
 | 
			
		||||
	assert(memcmp(decrypted, plaintext, sizeof(decrypted)) == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (l_cipher_is_supported(L_CIPHER_AES) &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_SHA1, true));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
 | 
			
		||||
							func, test_precheck, 0)
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("/Passphrase Generator/PSK Test Case 1",
 | 
			
		||||
						psk_test, &psk_test_case_1);
 | 
			
		||||
	add_test("/Passphrase Generator/PSK Test Case 2",
 | 
			
		||||
						psk_test, &psk_test_case_2);
 | 
			
		||||
	add_test("/Passphrase Generator/PSK Test Case 3",
 | 
			
		||||
						psk_test, &psk_test_case_3);
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_SHA1, true)) {
 | 
			
		||||
		printf("SHA1 support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	add_test("/PTK Derivation/PTK Test Case 1", ptk_test, &ptk_test_1);
 | 
			
		||||
	add_test("/PTK Derivation/PTK Test Case 2", ptk_test, &ptk_test_2);
 | 
			
		||||
	add_test("/PTK Derivation/PTK Test Case 3", ptk_test, &ptk_test_3);
 | 
			
		||||
	add_test("/PTK Derivation/PTK Test Case 4", ptk_test, &ptk_test_4);
 | 
			
		||||
	if (!l_cipher_is_supported(L_CIPHER_AES)) {
 | 
			
		||||
		printf("AES support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	add_test("/AES Key-wrap/Wrap & unwrap", aes_wrap_test, NULL);
 | 
			
		||||
	add_test("/AES-SIV", aes_siv_test, NULL);
 | 
			
		||||
	l_test_add("/Passphrase Generator/PSK Test Case 1",
 | 
			
		||||
			psk_test, &psk_test_case_1);
 | 
			
		||||
	l_test_add("/Passphrase Generator/PSK Test Case 2",
 | 
			
		||||
			psk_test, &psk_test_case_2);
 | 
			
		||||
	l_test_add("/Passphrase Generator/PSK Test Case 3",
 | 
			
		||||
			psk_test, &psk_test_case_3);
 | 
			
		||||
 | 
			
		||||
	l_test_add("/PTK Derivation/PTK Test Case 1",
 | 
			
		||||
			ptk_test, &ptk_test_1);
 | 
			
		||||
	l_test_add("/PTK Derivation/PTK Test Case 2",
 | 
			
		||||
			ptk_test, &ptk_test_2);
 | 
			
		||||
	l_test_add("/PTK Derivation/PTK Test Case 3",
 | 
			
		||||
			ptk_test, &ptk_test_3);
 | 
			
		||||
	l_test_add("/PTK Derivation/PTK Test Case 4",
 | 
			
		||||
			ptk_test, &ptk_test_4);
 | 
			
		||||
 | 
			
		||||
	l_test_add("/AES Key-wrap/Wrap & unwrap",
 | 
			
		||||
			aes_wrap_test, NULL);
 | 
			
		||||
	l_test_add("/AES-SIV", aes_siv_test, NULL);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -116,29 +116,6 @@ 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,
 | 
			
		||||
			const struct dpp_test_info *result)
 | 
			
		||||
{
 | 
			
		||||
@ -181,14 +158,6 @@ static void test_bad_channels(const void *data)
 | 
			
		||||
		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 {
 | 
			
		||||
	/* Initiator values */
 | 
			
		||||
	const char *i_proto_public;
 | 
			
		||||
@ -577,25 +546,22 @@ static void test_pkex_key_derivation(const void *user_data)
 | 
			
		||||
	CHECK_FROM_STR(vector->v, tmp, 32);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (l_getrandom_is_supported() &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_SHA256, true));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
 | 
			
		||||
							func, test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("DPP test responder-only key derivation", test_key_derivation,
 | 
			
		||||
							&responder_only_p256);
 | 
			
		||||
	add_test("DPP test mutual key derivation", test_key_derivation,
 | 
			
		||||
							&mutual_p256);
 | 
			
		||||
	add_test("DPP test PKEX key derivation", test_pkex_key_derivation,
 | 
			
		||||
							&pkex_vector);
 | 
			
		||||
	if (l_checksum_is_supported(L_CHECKSUM_SHA256, true) &&
 | 
			
		||||
						l_getrandom_is_supported()) {
 | 
			
		||||
		l_test_add("DPP test responder-only key derivation",
 | 
			
		||||
						test_key_derivation,
 | 
			
		||||
						&responder_only_p256);
 | 
			
		||||
		l_test_add("DPP test mutual key derivation",
 | 
			
		||||
						test_key_derivation,
 | 
			
		||||
						&mutual_p256);
 | 
			
		||||
		l_test_add("DPP test PKEX key derivation",
 | 
			
		||||
						test_pkex_key_derivation,
 | 
			
		||||
						&pkex_vector);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("DPP URI parse", test_uri_parse, &all_values);
 | 
			
		||||
	l_test_add("DPP URI no type", test_uri_parse, &no_type);
 | 
			
		||||
@ -610,7 +576,6 @@ int main(int argc, char *argv[])
 | 
			
		||||
	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 unexpected ID", test_uri_parse, &unexpected_id);
 | 
			
		||||
	l_test_add("DPP URI duplicates", test_duplicate_in_uri, &duplicates);
 | 
			
		||||
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -131,23 +131,27 @@ static void test_get_asym_key(const void *data)
 | 
			
		||||
	assert(!memcmp(msk, m_session_key, sizeof(m_session_key)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_checksum_is_supported(L_CHECKSUM_MD4, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func) l_test_add_func_precheck(name, func, \
 | 
			
		||||
							test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("MSHAPv2 nt_password-hash", test_nt_password_hash);
 | 
			
		||||
	add_test("MSHAPv2 generate_nt_response", test_generate_nt_response);
 | 
			
		||||
	add_test("MSHAPv2 get_master_key", test_get_master_key);
 | 
			
		||||
	add_test("MSHAPv2 get_asym_state_key", test_get_asym_key);
 | 
			
		||||
	add_test("MSHAPv2 authenticator_response", test_authenticator_response);
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_MD4, false)) {
 | 
			
		||||
		printf("MD4 support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("MSHAPv2 nt_password-hash",
 | 
			
		||||
			test_nt_password_hash, NULL);
 | 
			
		||||
	l_test_add("MSHAPv2 generate_nt_response",
 | 
			
		||||
			test_generate_nt_response, NULL);
 | 
			
		||||
	l_test_add("MSHAPv2 get_master_key",
 | 
			
		||||
			test_get_master_key, NULL);
 | 
			
		||||
	l_test_add("MSHAPv2 get_asym_state_key",
 | 
			
		||||
			test_get_asym_key, NULL);
 | 
			
		||||
	l_test_add("MSHAPv2 authenticator_response",
 | 
			
		||||
			test_authenticator_response, NULL);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3908,38 +3908,6 @@ static void eapol_ap_sta_handshake_ip_alloc_no_req_test(const void *data)
 | 
			
		||||
#define _IS_ENABLED2(one_or_two_args) _IS_ENABLED3(one_or_two_args true, false)
 | 
			
		||||
#define _IS_ENABLED3(ignore_this, val, ...) val
 | 
			
		||||
 | 
			
		||||
static bool hash_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (l_checksum_is_supported(L_CHECKSUM_MD5, true) &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_SHA1, true));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool aes_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (l_cipher_is_supported(L_CIPHER_AES) &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_MD5, true) &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_SHA1, true));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool pkcs8_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (IS_ENABLED(HAVE_PKCS8_SUPPORT) &&
 | 
			
		||||
		l_cipher_is_supported(L_CIPHER_AES) &&
 | 
			
		||||
		l_cipher_is_supported(L_CIPHER_AES_CBC) &&
 | 
			
		||||
		l_cipher_is_supported(L_CIPHER_DES3_EDE_CBC) &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_MD5, true) &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_SHA1, true) &&
 | 
			
		||||
		l_key_is_supported(L_KEY_FEATURE_CRYPTO) &&
 | 
			
		||||
		l_key_is_supported(L_KEY_FEATURE_RESTRICT));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_hash_test(name, func, data) l_test_add_data_func_precheck(name, \
 | 
			
		||||
						data, func, hash_precheck, 0)
 | 
			
		||||
#define add_aes_test(name, func, data) l_test_add_data_func_precheck(name, \
 | 
			
		||||
						data, func, aes_precheck, 0)
 | 
			
		||||
#define add_pkcs8_test(name, func, data) l_test_add_data_func_precheck(name, \
 | 
			
		||||
						data, func, pkcs8_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
@ -4009,55 +3977,80 @@ int main(int argc, char *argv[])
 | 
			
		||||
	l_test_add("/EAPoL Key/Key Frame 32",
 | 
			
		||||
			eapol_key_test, &eapol_key_test_32);
 | 
			
		||||
 | 
			
		||||
	add_hash_test("/EAPoL Key/MIC Test 1",
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_MD5, true) ||
 | 
			
		||||
			!l_checksum_is_supported(L_CHECKSUM_SHA1, true))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	l_test_add("/EAPoL Key/MIC Test 1",
 | 
			
		||||
			eapol_key_mic_test, &eapol_key_mic_test_1);
 | 
			
		||||
	add_hash_test("/EAPoL Key/MIC Test 2",
 | 
			
		||||
	l_test_add("/EAPoL Key/MIC Test 2",
 | 
			
		||||
			eapol_key_mic_test, &eapol_key_mic_test_2);
 | 
			
		||||
	add_hash_test("/EAPoL Key/Calculate MIC Test 1",
 | 
			
		||||
 | 
			
		||||
	l_test_add("/EAPoL Key/Calculate MIC Test 1",
 | 
			
		||||
			eapol_calculate_mic_test, &eapol_calculate_mic_test_1);
 | 
			
		||||
 | 
			
		||||
	add_aes_test("EAPoL/WPA2 4-Way Handshake",
 | 
			
		||||
				eapol_4way_test, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/WPA2 4-Way & GTK Handshake",
 | 
			
		||||
				eapol_wpa2_handshake_test, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/WPA 4-Way & GTK Handshake",
 | 
			
		||||
				eapol_wpa_handshake_test, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/WPA2 PTK State Machine",
 | 
			
		||||
				eapol_sm_test_ptk, NULL);
 | 
			
		||||
	add_aes_test("EAPoL IGTK & 4-Way Handshake",
 | 
			
		||||
				eapol_sm_test_igtk, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/WPA2 PTK & GTK State Machine",
 | 
			
		||||
				eapol_sm_test_wpa2_ptk_gtk, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/WPA PTK & GTK State Machine Test 1",
 | 
			
		||||
				eapol_sm_test_wpa_ptk_gtk, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/WPA PTK & GTK State Machine Test 2",
 | 
			
		||||
				eapol_sm_test_wpa_ptk_gtk_2, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/WPA2 Retransmit Test",
 | 
			
		||||
				eapol_sm_wpa2_retransmit_test, NULL);
 | 
			
		||||
	if (!l_cipher_is_supported(L_CIPHER_AES))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	add_pkcs8_test("EAPoL/8021x EAP-TLS & 4-Way Handshake",
 | 
			
		||||
				eapol_sm_test_eap_tls, NULL);
 | 
			
		||||
	add_pkcs8_test("EAPoL/8021x EAP-TTLS+EAP-MD5 & 4-Way Handshake",
 | 
			
		||||
				eapol_sm_test_eap_ttls_md5, NULL);
 | 
			
		||||
	add_pkcs8_test("EAPoL/8021x EAP NAK",
 | 
			
		||||
				eapol_sm_test_eap_nak, NULL);
 | 
			
		||||
	add_pkcs8_test("EAPoL/8021x EAP-TLS subject name match",
 | 
			
		||||
				eapol_sm_test_eap_tls_subject_good, NULL);
 | 
			
		||||
	add_pkcs8_test("EAPoL/8021x EAP-TLS subject name mismatch",
 | 
			
		||||
				eapol_sm_test_eap_tls_subject_bad, NULL);
 | 
			
		||||
	add_pkcs8_test("EAPoL/8021x EAP-TLS embedded certs",
 | 
			
		||||
				eapol_sm_test_eap_tls_embedded, NULL);
 | 
			
		||||
	l_test_add("EAPoL/WPA2 4-Way Handshake",
 | 
			
		||||
			&eapol_4way_test, NULL);
 | 
			
		||||
 | 
			
		||||
	add_aes_test("EAPoL/FT-Using-PSK 4-Way Handshake",
 | 
			
		||||
			eapol_ft_handshake_test, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/Supplicant+Authenticator 4-Way Handshake",
 | 
			
		||||
			eapol_ap_sta_handshake_test, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/Supplicant+Authenticator 4-Way Handshake Bad PSK",
 | 
			
		||||
			eapol_ap_sta_handshake_bad_psk_test, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/Supplicant+Authenticator IP Allocation OK",
 | 
			
		||||
			eapol_ap_sta_handshake_ip_alloc_ok_test, NULL);
 | 
			
		||||
	add_aes_test("EAPoL/Supplicant+Authenticator IP Allocation no request",
 | 
			
		||||
			eapol_ap_sta_handshake_ip_alloc_no_req_test, NULL);
 | 
			
		||||
	l_test_add("EAPoL/WPA2 4-Way & GTK Handshake",
 | 
			
		||||
			&eapol_wpa2_handshake_test, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL/WPA 4-Way & GTK Handshake",
 | 
			
		||||
			&eapol_wpa_handshake_test, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL/WPA2 PTK State Machine", &eapol_sm_test_ptk, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL IGTK & 4-Way Handshake",
 | 
			
		||||
			&eapol_sm_test_igtk, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL/WPA2 PTK & GTK State Machine",
 | 
			
		||||
			&eapol_sm_test_wpa2_ptk_gtk, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL/WPA PTK & GTK State Machine Test 1",
 | 
			
		||||
			&eapol_sm_test_wpa_ptk_gtk, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL/WPA PTK & GTK State Machine Test 2",
 | 
			
		||||
			&eapol_sm_test_wpa_ptk_gtk_2, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL/WPA2 Retransmit Test",
 | 
			
		||||
			&eapol_sm_wpa2_retransmit_test, NULL);
 | 
			
		||||
 | 
			
		||||
	if (IS_ENABLED(HAVE_PKCS8_SUPPORT) &&
 | 
			
		||||
			l_cipher_is_supported(L_CIPHER_DES3_EDE_CBC) &&
 | 
			
		||||
			l_cipher_is_supported(L_CIPHER_AES_CBC) &&
 | 
			
		||||
			l_key_is_supported(L_KEY_FEATURE_RESTRICT |
 | 
			
		||||
						L_KEY_FEATURE_CRYPTO)) {
 | 
			
		||||
		l_test_add("EAPoL/8021x EAP-TLS & 4-Way Handshake",
 | 
			
		||||
					&eapol_sm_test_eap_tls, NULL);
 | 
			
		||||
 | 
			
		||||
		l_test_add("EAPoL/8021x EAP-TTLS+EAP-MD5 & 4-Way Handshake",
 | 
			
		||||
					&eapol_sm_test_eap_ttls_md5, NULL);
 | 
			
		||||
		l_test_add("EAPoL/8021x EAP NAK",
 | 
			
		||||
				&eapol_sm_test_eap_nak, NULL);
 | 
			
		||||
 | 
			
		||||
		l_test_add("EAPoL/8021x EAP-TLS subject name match",
 | 
			
		||||
				&eapol_sm_test_eap_tls_subject_good, NULL);
 | 
			
		||||
		l_test_add("EAPoL/8021x EAP-TLS subject name mismatch",
 | 
			
		||||
				&eapol_sm_test_eap_tls_subject_bad, NULL);
 | 
			
		||||
		l_test_add("EAPoL/8021x EAP-TLS embedded certs",
 | 
			
		||||
				&eapol_sm_test_eap_tls_embedded, NULL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL/FT-Using-PSK 4-Way Handshake",
 | 
			
		||||
			&eapol_ft_handshake_test, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("EAPoL/Supplicant+Authenticator 4-Way Handshake",
 | 
			
		||||
			&eapol_ap_sta_handshake_test, NULL);
 | 
			
		||||
	l_test_add("EAPoL/Supplicant+Authenticator 4-Way Handshake Bad PSK",
 | 
			
		||||
			&eapol_ap_sta_handshake_bad_psk_test, NULL);
 | 
			
		||||
	l_test_add("EAPoL/Supplicant+Authenticator IP Allocation OK",
 | 
			
		||||
			&eapol_ap_sta_handshake_ip_alloc_ok_test, NULL);
 | 
			
		||||
	l_test_add("EAPoL/Supplicant+Authenticator IP Allocation no request",
 | 
			
		||||
			&eapol_ap_sta_handshake_ip_alloc_no_req_test, NULL);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -81,20 +81,18 @@ static const struct hmac_data test_case_2 = {
 | 
			
		||||
	.hmac		= "80070713463e7749b90c2dc24911e275",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_checksum_is_supported(L_CHECKSUM_MD5, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
 | 
			
		||||
							func, test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("/hmac-md5/Test case 1", hmac_test, &test_case_1);
 | 
			
		||||
	add_test("/hmac-md5/Test case 2", hmac_test, &test_case_2);
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_MD5, true)) {
 | 
			
		||||
		printf("MD5 support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("/hmac-md5/Test case 1", hmac_test, &test_case_1);
 | 
			
		||||
	l_test_add("/hmac-md5/Test case 2", hmac_test, &test_case_2);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -81,20 +81,18 @@ static const struct hmac_data test_case_2 = {
 | 
			
		||||
	.hmac		= "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_checksum_is_supported(L_CHECKSUM_SHA1, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
 | 
			
		||||
							func, test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("/hmac-sha1/Test case 1", hmac_test, &test_case_1);
 | 
			
		||||
	add_test("/hmac-sha1/Test case 2", hmac_test, &test_case_2);
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_SHA1, true)) {
 | 
			
		||||
		printf("SHA1 support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("/hmac-sha1/Test case 1", hmac_test, &test_case_1);
 | 
			
		||||
	l_test_add("/hmac-sha1/Test case 2", hmac_test, &test_case_2);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -83,20 +83,18 @@ static const struct hmac_data test_case_2 = {
 | 
			
		||||
			  "ef4d59a14946175997479dbc2d1a3cd8",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_checksum_is_supported(L_CHECKSUM_SHA256, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
 | 
			
		||||
							func, test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("/hmac-sha256/Test case 1", hmac_test, &test_case_1);
 | 
			
		||||
	add_test("/hmac-sha256/Test case 2", hmac_test, &test_case_2);
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_SHA256, true)) {
 | 
			
		||||
		printf("SHA256 support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("/hmac-sha256/Test case 1", hmac_test, &test_case_1);
 | 
			
		||||
	l_test_add("/hmac-sha256/Test case 2", hmac_test, &test_case_2);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -81,19 +81,17 @@ static const struct kdf_data test_case_1 = {
 | 
			
		||||
			  "84f7d2291143d4d4",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_checksum_is_supported(L_CHECKSUM_SHA256, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
 | 
			
		||||
							func, test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("/kdf-sha256/Test case 1", kdf_test, &test_case_1);
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_SHA256, true)) {
 | 
			
		||||
		printf("SHA256 support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("/kdf-sha256/Test case 1", kdf_test, &test_case_1);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -341,7 +341,7 @@ static const struct p2p_probe_req_data p2p_probe_req_data_1 = {
 | 
			
		||||
			.group_caps = 0,
 | 
			
		||||
		},
 | 
			
		||||
		.listen_channel = {
 | 
			
		||||
			.country = { 'X', 'X', '\x04' },
 | 
			
		||||
			.country = "XX\x04",
 | 
			
		||||
			.oper_class = 81,
 | 
			
		||||
			.channel_num = 1,
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
@ -113,21 +113,19 @@ static const struct prf_data test_case_3 = {
 | 
			
		||||
			  "f7b4abd43d87f0a68f1cbd9e2b6f7607",
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_checksum_is_supported(L_CHECKSUM_SHA1, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
 | 
			
		||||
							func, test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("/prf-sha1/Test case 1", prf_test, &test_case_1);
 | 
			
		||||
	add_test("/prf-sha1/Test case 2", prf_test, &test_case_2);
 | 
			
		||||
	add_test("/prf-sha1/Test case 3", prf_test, &test_case_3);
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_SHA1, true)) {
 | 
			
		||||
		printf("SHA1 support missing, skipping...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("/prf-sha1/Test case 1", prf_test, &test_case_1);
 | 
			
		||||
	l_test_add("/prf-sha1/Test case 2", prf_test, &test_case_2);
 | 
			
		||||
	l_test_add("/prf-sha1/Test case 3", prf_test, &test_case_3);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -871,29 +871,32 @@ static void test_pt_pwe(const void *data)
 | 
			
		||||
	l_ecc_point_free(pt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool test_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (l_getrandom_is_supported() &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_SHA256, true));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_test(name, func) l_test_add_func_precheck(name, func, \
 | 
			
		||||
							test_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	add_test("SAE anti-clogging", test_clogging);
 | 
			
		||||
	add_test("SAE early confirm", test_early_confirm);
 | 
			
		||||
	add_test("SAE reflection", test_reflection);
 | 
			
		||||
	add_test("SAE malformed commit", test_malformed_commit);
 | 
			
		||||
	add_test("SAE malformed confirm", test_malformed_confirm);
 | 
			
		||||
	add_test("SAE bad group", test_bad_group);
 | 
			
		||||
	add_test("SAE bad confirm", test_bad_confirm);
 | 
			
		||||
	add_test("SAE confirm after accept", test_confirm_after_accept);
 | 
			
		||||
	add_test("SAE end-to-end", test_end_to_end);
 | 
			
		||||
	add_test("SAE pt-pwe", test_pt_pwe);
 | 
			
		||||
	if (!l_getrandom_is_supported()) {
 | 
			
		||||
		l_info("l_getrandom not supported, skipping...");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_SHA256, true)) {
 | 
			
		||||
		l_info("SHA256/HMAC_SHA256 not supported, skipping...");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l_test_add("SAE anti-clogging", test_clogging, NULL);
 | 
			
		||||
	l_test_add("SAE early confirm", test_early_confirm, NULL);
 | 
			
		||||
	l_test_add("SAE reflection", test_reflection, NULL);
 | 
			
		||||
	l_test_add("SAE malformed commit", test_malformed_commit, NULL);
 | 
			
		||||
	l_test_add("SAE malformed confirm", test_malformed_confirm, NULL);
 | 
			
		||||
	l_test_add("SAE bad group", test_bad_group, NULL);
 | 
			
		||||
	l_test_add("SAE bad confirm", test_bad_confirm, NULL);
 | 
			
		||||
	l_test_add("SAE confirm after accept", test_confirm_after_accept, NULL);
 | 
			
		||||
	l_test_add("SAE end-to-end", test_end_to_end, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("SAE pt-pwe", test_pt_pwe, NULL);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,66 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
 *  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 bool aes_ctr_supported(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_cipher_is_supported(L_CIPHER_AES_CTR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void test_short_encrypted_bytes(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	struct l_settings *settings = l_settings_new();
 | 
			
		||||
	bool changed;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	storage_init((const uint8_t *)"abc123", 6);
 | 
			
		||||
 | 
			
		||||
	l_settings_set_string(settings, "Security", "EncryptedSecurity", "012345");
 | 
			
		||||
	l_settings_set_string(settings, "Security", "EncryptedSalt", "012345");
 | 
			
		||||
 | 
			
		||||
	err = __storage_decrypt(settings, "mySSID", &changed);
 | 
			
		||||
	assert(err == -EBADMSG);
 | 
			
		||||
 | 
			
		||||
	l_settings_free(settings);
 | 
			
		||||
 | 
			
		||||
	storage_exit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
 | 
			
		||||
	l_test_add_func_precheck("/storage/profile encryption",
 | 
			
		||||
						test_short_encrypted_bytes,
 | 
			
		||||
						aes_ctr_supported, 0);
 | 
			
		||||
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										153
									
								
								unit/test-wsc.c
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								unit/test-wsc.c
									
									
									
									
									
								
							@ -2574,40 +2574,6 @@ struct wsc_credential wsc_r_test_open_cred = {
 | 
			
		||||
	.encryption_type = WSC_ENCRYPTION_TYPE_NONE,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static bool getrandom_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return l_getrandom_is_supported();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool aes_cbc_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (l_key_is_supported(L_KEY_FEATURE_DH) &&
 | 
			
		||||
		l_cipher_is_supported(L_CIPHER_AES_CBC));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool key_crypto_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (l_key_is_supported(L_KEY_FEATURE_CRYPTO) &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_SHA256, true));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool key_dh_precheck(const void *data)
 | 
			
		||||
{
 | 
			
		||||
	return (l_key_is_supported(L_KEY_FEATURE_DH) &&
 | 
			
		||||
		l_key_is_supported(L_KEY_FEATURE_CRYPTO) &&
 | 
			
		||||
		l_checksum_is_supported(L_CHECKSUM_SHA256, true));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define add_aes_cbc_test(name, func, data) l_test_add_data_func_precheck(name, \
 | 
			
		||||
							data, func, \
 | 
			
		||||
							aes_cbc_precheck, 0)
 | 
			
		||||
#define add_crypto_test(name, func, data) l_test_add_data_func_precheck(name, \
 | 
			
		||||
							data, func, \
 | 
			
		||||
							key_crypto_precheck, 0)
 | 
			
		||||
#define add_dh_test(name, func, data) l_test_add_data_func_precheck(name, \
 | 
			
		||||
							data, func, \
 | 
			
		||||
							key_dh_precheck, 0)
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	l_test_init(&argc, &argv);
 | 
			
		||||
@ -2629,8 +2595,8 @@ int main(int argc, char *argv[])
 | 
			
		||||
	l_test_add("/wsc/pin/valid pin", wsc_test_pin_valid, NULL);
 | 
			
		||||
	l_test_add("/wsc/pin/valid checksum", wsc_test_pin_checksum, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add_func_precheck("/wsc/pin/generate", wsc_test_pin_generate,
 | 
			
		||||
							getrandom_precheck, 0);
 | 
			
		||||
	if (l_getrandom_is_supported())
 | 
			
		||||
		l_test_add("/wsc/pin/generate", wsc_test_pin_generate, NULL);
 | 
			
		||||
 | 
			
		||||
	l_test_add("/wsc/gen_uuid/1", wsc_test_uuid_from_addr,
 | 
			
		||||
					&uuid_from_addr_data_1);
 | 
			
		||||
@ -2641,77 +2607,96 @@ int main(int argc, char *argv[])
 | 
			
		||||
	l_test_add("/wsc/build/m1 1", wsc_test_build_m1, &m1_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m1 2", wsc_test_build_m1, &m1_data_2);
 | 
			
		||||
 | 
			
		||||
	add_dh_test("/wsc/parse/m2 1", wsc_test_parse_m2, &m2_data_1);
 | 
			
		||||
	add_dh_test("/wsc/parse/m2 2", wsc_test_parse_m2, &m2_data_2);
 | 
			
		||||
	if (!l_checksum_is_supported(L_CHECKSUM_SHA256, true)) {
 | 
			
		||||
		printf("SHA256 support missing, skipping other tests...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/build/m2 1", wsc_test_build_m2, &m2_data_1);
 | 
			
		||||
	if (!l_key_is_supported(L_KEY_FEATURE_CRYPTO)) {
 | 
			
		||||
		printf("Key crypto not supported, skipping other tests...\n");
 | 
			
		||||
		goto done;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m3 1", wsc_test_parse_m3, &m3_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m3 1", wsc_test_build_m3, &m3_data_1);
 | 
			
		||||
	if (l_key_is_supported(L_KEY_FEATURE_DH)) {
 | 
			
		||||
		l_test_add("/wsc/parse/m2 1", wsc_test_parse_m2, &m2_data_1);
 | 
			
		||||
		l_test_add("/wsc/parse/m2 2", wsc_test_parse_m2, &m2_data_2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m4 1", wsc_test_parse_m4, &m4_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m4 1", wsc_test_build_m4, &m4_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m2 1", wsc_test_build_m2, &m2_data_1);
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m4 encrypted settings 1",
 | 
			
		||||
					wsc_test_parse_m4_encrypted_settings,
 | 
			
		||||
					&m4_encrypted_settings_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m4 encrypted settings 1",
 | 
			
		||||
					wsc_test_build_m4_encrypted_settings,
 | 
			
		||||
					&m4_encrypted_settings_data_1);
 | 
			
		||||
	l_test_add("/wsc/parse/m3 1", wsc_test_parse_m3, &m3_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m3 1", wsc_test_build_m3, &m3_data_1);
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m5 1", wsc_test_parse_m5, &m5_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m5 1", wsc_test_build_m5, &m5_data_1);
 | 
			
		||||
	l_test_add("/wsc/parse/m4 1", wsc_test_parse_m4, &m4_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m4 1", wsc_test_build_m4, &m4_data_1);
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m6 1", wsc_test_parse_m6, &m6_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m6 1", wsc_test_build_m6, &m6_data_1);
 | 
			
		||||
	l_test_add("/wsc/parse/m4 encrypted settings 1",
 | 
			
		||||
			wsc_test_parse_m4_encrypted_settings,
 | 
			
		||||
			&m4_encrypted_settings_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m4 encrypted settings 1",
 | 
			
		||||
			wsc_test_build_m4_encrypted_settings,
 | 
			
		||||
			&m4_encrypted_settings_data_1);
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m6 encrypted settings 1",
 | 
			
		||||
					wsc_test_parse_m6_encrypted_settings,
 | 
			
		||||
					&m6_encrypted_settings_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m6 encrypted settings 1",
 | 
			
		||||
					wsc_test_build_m6_encrypted_settings,
 | 
			
		||||
					&m6_encrypted_settings_data_1);
 | 
			
		||||
	l_test_add("/wsc/parse/m5 1", wsc_test_parse_m5, &m5_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m5 1", wsc_test_build_m5, &m5_data_1);
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m7 1", wsc_test_parse_m7, &m7_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m7 1", wsc_test_build_m7, &m7_data_1);
 | 
			
		||||
	l_test_add("/wsc/parse/m6 1", wsc_test_parse_m6, &m6_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m6 1", wsc_test_build_m6, &m6_data_1);
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m8 1", wsc_test_parse_m8, &m8_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m8 1", wsc_test_build_m8, &m8_data_1);
 | 
			
		||||
	l_test_add("/wsc/parse/m6 encrypted settings 1",
 | 
			
		||||
			wsc_test_parse_m6_encrypted_settings,
 | 
			
		||||
			&m6_encrypted_settings_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m6 encrypted settings 1",
 | 
			
		||||
			wsc_test_build_m6_encrypted_settings,
 | 
			
		||||
			&m6_encrypted_settings_data_1);
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/m8 encrypted settings 1",
 | 
			
		||||
					wsc_test_parse_m8_encrypted_settings,
 | 
			
		||||
					&m8_encrypted_settings_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/m8 encrypted settings 1",
 | 
			
		||||
					wsc_test_build_m8_encrypted_settings,
 | 
			
		||||
					&m8_encrypted_settings_data_1);
 | 
			
		||||
	l_test_add("/wsc/parse/m7 1", wsc_test_parse_m7, &m7_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m7 1", wsc_test_build_m7, &m7_data_1);
 | 
			
		||||
 | 
			
		||||
	add_crypto_test("/wsc/parse/wsc_done 1", wsc_test_parse_wsc_done,
 | 
			
		||||
	l_test_add("/wsc/parse/m8 1", wsc_test_parse_m8, &m8_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m8 1", wsc_test_build_m8, &m8_data_1);
 | 
			
		||||
 | 
			
		||||
	l_test_add("/wsc/parse/m8 encrypted settings 1",
 | 
			
		||||
			wsc_test_parse_m8_encrypted_settings,
 | 
			
		||||
			&m8_encrypted_settings_data_1);
 | 
			
		||||
	l_test_add("/wsc/build/m8 encrypted settings 1",
 | 
			
		||||
			wsc_test_build_m8_encrypted_settings,
 | 
			
		||||
			&m8_encrypted_settings_data_1);
 | 
			
		||||
 | 
			
		||||
	l_test_add("/wsc/parse/wsc_done 1", wsc_test_parse_wsc_done,
 | 
			
		||||
							&wsc_done_data_1);
 | 
			
		||||
	add_crypto_test("/wsc/build/wsc_done 1", wsc_test_build_wsc_done,
 | 
			
		||||
	l_test_add("/wsc/build/wsc_done 1", wsc_test_build_wsc_done,
 | 
			
		||||
							&wsc_done_data_1);
 | 
			
		||||
 | 
			
		||||
	add_dh_test("/wsc/diffie-hellman/generate pubkey 1",
 | 
			
		||||
	if (!l_key_is_supported(L_KEY_FEATURE_DH))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	l_test_add("/wsc/diffie-hellman/generate pubkey 1",
 | 
			
		||||
					wsc_test_dh_generate_pubkey,
 | 
			
		||||
					&dh_generate_pubkey_test_data_1);
 | 
			
		||||
	add_dh_test("/wsc/diffie-hellman/generate pubkey 2",
 | 
			
		||||
	l_test_add("/wsc/diffie-hellman/generate pubkey 2",
 | 
			
		||||
					wsc_test_dh_generate_pubkey,
 | 
			
		||||
					&dh_generate_pubkey_test_data_2);
 | 
			
		||||
 | 
			
		||||
	add_aes_cbc_test("/wsc/handshake/PBC Handshake Test",
 | 
			
		||||
	if (!l_cipher_is_supported(L_CIPHER_AES_CBC))
 | 
			
		||||
		goto done;
 | 
			
		||||
 | 
			
		||||
	l_test_add("/wsc/handshake/PBC Handshake Test",
 | 
			
		||||
						wsc_test_pbc_handshake, NULL);
 | 
			
		||||
 | 
			
		||||
	add_aes_cbc_test("/wsc/retransmission/no fragmentation",
 | 
			
		||||
	l_test_add("/wsc/retransmission/no fragmentation",
 | 
			
		||||
				wsc_test_retransmission_no_fragmentation, NULL);
 | 
			
		||||
 | 
			
		||||
	add_aes_cbc_test("/wsc-r/handshake/PBC Handshake WPA2 passphrase test",
 | 
			
		||||
					wsc_r_test_pbc_handshake,
 | 
			
		||||
					&wsc_r_test_wpa2_cred_passphrase);
 | 
			
		||||
	add_aes_cbc_test("/wsc-r/handshake/PBC Handshake WPA2 PSK test",
 | 
			
		||||
					wsc_r_test_pbc_handshake,
 | 
			
		||||
					&wsc_r_test_wpa2_cred_psk);
 | 
			
		||||
	add_aes_cbc_test("/wsc-r/handshake/PBC Handshake Open test",
 | 
			
		||||
					wsc_r_test_pbc_handshake,
 | 
			
		||||
					&wsc_r_test_open_cred);
 | 
			
		||||
	l_test_add("/wsc-r/handshake/PBC Handshake WPA2 passphrase test",
 | 
			
		||||
				wsc_r_test_pbc_handshake,
 | 
			
		||||
				&wsc_r_test_wpa2_cred_passphrase);
 | 
			
		||||
	l_test_add("/wsc-r/handshake/PBC Handshake WPA2 PSK test",
 | 
			
		||||
				wsc_r_test_pbc_handshake,
 | 
			
		||||
				&wsc_r_test_wpa2_cred_psk);
 | 
			
		||||
	l_test_add("/wsc-r/handshake/PBC Handshake Open test",
 | 
			
		||||
				wsc_r_test_pbc_handshake,
 | 
			
		||||
				&wsc_r_test_open_cred);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	return l_test_run();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user