mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2025-04-20 19:37:58 +02:00
Compare commits
124 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4ded663e68 | ||
![]() |
c00bc3a065 | ||
![]() |
f469db8a95 | ||
![]() |
c3a27354ff | ||
![]() |
d927fd07c1 | ||
![]() |
8dff156eb6 | ||
![]() |
e5c41a8024 | ||
![]() |
603d6b2881 | ||
![]() |
d1aa4009bc | ||
![]() |
3c5081c7a6 | ||
![]() |
7d5bcd738b | ||
![]() |
0a93c55552 | ||
![]() |
7f9ea7640d | ||
![]() |
c52d913f20 | ||
![]() |
651b647570 | ||
![]() |
8cf9734d2b | ||
![]() |
d70fbade44 | ||
![]() |
f0e515b6ff | ||
![]() |
47ef40d645 | ||
![]() |
9e10efbef5 | ||
![]() |
224afbb9ca | ||
![]() |
bf69e6210c | ||
![]() |
258482d509 | ||
![]() |
1caad4ca88 | ||
![]() |
59464a0ca4 | ||
![]() |
93b25c87d6 | ||
![]() |
e971ef71d5 | ||
![]() |
bff5006b38 | ||
![]() |
ea571861d6 | ||
![]() |
f3e4263f51 | ||
![]() |
77639d2d45 | ||
![]() |
1662707f22 | ||
![]() |
5f4bf2a5e5 | ||
![]() |
40af18f96a | ||
![]() |
ab4fa30c7e | ||
![]() |
43f73823ec | ||
![]() |
f4439fd2b6 | ||
![]() |
bf82aff039 | ||
![]() |
4b535cee1f | ||
![]() |
7144741537 | ||
![]() |
64b872f363 | ||
![]() |
83a2457550 | ||
![]() |
43f895142c | ||
![]() |
c352b35bf1 | ||
![]() |
d4bba5c838 | ||
![]() |
1dd9f94713 | ||
![]() |
c458e6612d | ||
![]() |
45db339dcd | ||
![]() |
887d8c8fe8 | ||
![]() |
c6932efa30 | ||
![]() |
f3ba82b0e1 | ||
![]() |
a26fcd8f2d | ||
![]() |
ab49b404fd | ||
![]() |
9bc71b2853 | ||
![]() |
5b104967ce | ||
![]() |
4680c0c13b | ||
![]() |
c36358cc7c | ||
![]() |
235f6e5f14 | ||
![]() |
980e132f48 | ||
![]() |
900aa5810e | ||
![]() |
8cf83d6620 | ||
![]() |
ee52bc60ff | ||
![]() |
3f4a29651e | ||
![]() |
f58cad8cd9 | ||
![]() |
b9c3feb198 | ||
![]() |
94ebc9d90b | ||
![]() |
b0759ebbb2 | ||
![]() |
f2ac45eb52 | ||
![]() |
7c5b40ff6b | ||
![]() |
7465abe5f8 | ||
![]() |
a910a21beb | ||
![]() |
c40e665094 | ||
![]() |
bb57d61add | ||
![]() |
fc2965649c | ||
![]() |
fa25de4ad1 | ||
![]() |
901305dcdd | ||
![]() |
b4a4495537 | ||
![]() |
ccd91fe556 | ||
![]() |
e89e4d692c | ||
![]() |
0868418ad1 | ||
![]() |
d81de65533 | ||
![]() |
65073ffcfa | ||
![]() |
d0b9fc84b5 | ||
![]() |
e0727bfeb6 | ||
![]() |
8e10e00904 | ||
![]() |
a2b2f66c4c | ||
![]() |
ca9b7ccaf6 | ||
![]() |
354bce64dd | ||
![]() |
ff4edacb42 | ||
![]() |
a6edf6f31e | ||
![]() |
31787e3788 | ||
![]() |
e98a76aefb | ||
![]() |
4a04d41409 | ||
![]() |
8bb22a722b | ||
![]() |
c459dc75c0 | ||
![]() |
1a554a300d | ||
![]() |
6cc6a8a2cb | ||
![]() |
b0a011d8f4 | ||
![]() |
a27b7823df | ||
![]() |
14b9291490 | ||
![]() |
354200f9da | ||
![]() |
acc5daf0e2 | ||
![]() |
5c22ab6621 | ||
![]() |
af99fbb6c0 | ||
![]() |
4b2c6de45c | ||
![]() |
30cc3ecf7b | ||
![]() |
87a8884f25 | ||
![]() |
4f2bf0b0a6 | ||
![]() |
184c3efcb3 | ||
![]() |
3f06d0128a | ||
![]() |
3bc8b90c0e | ||
![]() |
f6cfcb8ca2 | ||
![]() |
163c2ebd37 | ||
![]() |
154a29be05 | ||
![]() |
23cf6107c6 | ||
![]() |
c66438e34f | ||
![]() |
c778ddf0c2 | ||
![]() |
2ad9561069 | ||
![]() |
f4ec1ee509 | ||
![]() |
b98bc30c23 | ||
![]() |
4c3cbdc8d3 | ||
![]() |
61cba6bd28 | ||
![]() |
574b0d80dc | ||
![]() |
0c228f4465 |
3
.codespellrc
Normal file
3
.codespellrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[codespell]
|
||||||
|
ignore-words-list = fils, FILS, SME, assertIn, OCE, clen, aci, ELL, sav, ths
|
||||||
|
skip = build-aux, linux, autom4te.cache, aclocal.m4, AUTHORS, libtool, configure*, *.5, ell
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -67,6 +67,7 @@ unit/test-band
|
|||||||
unit/test-dpp
|
unit/test-dpp
|
||||||
unit/test-json
|
unit/test-json
|
||||||
unit/test-nl80211util
|
unit/test-nl80211util
|
||||||
|
unit/test-pmksa
|
||||||
unit/cert-*.pem
|
unit/cert-*.pem
|
||||||
unit/cert-*.csr
|
unit/cert-*.csr
|
||||||
unit/cert-*.srl
|
unit/cert-*.srl
|
||||||
|
44
ChangeLog
44
ChangeLog
@ -1,3 +1,47 @@
|
|||||||
|
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.
|
||||||
|
Add support for HighUtilization/StationCount thresholds.
|
||||||
|
Add support for disabling Multicast RX option.
|
||||||
|
|
||||||
|
ver 3.1:
|
||||||
|
Fix issue with handling OWE transition BSS selection.
|
||||||
|
Fix issue with handling oper class 136 starting frequency.
|
||||||
|
|
||||||
|
ver 3.0:
|
||||||
|
Fix issue with handling alpha2 code for United Kingdom.
|
||||||
|
Fix issue with handling empty TX/RX bitrate attributes.
|
||||||
|
Fix issue with handling RSSI polling fallback workaround.
|
||||||
|
Fix issue with handling harmless cloned information elements.
|
||||||
|
Add experimental support for External Authentication feature.
|
||||||
|
|
||||||
|
ver 2.22:
|
||||||
|
Fix issue with handling the Affinities property.
|
||||||
|
Fix issue with handling ConnectedAccessPoint signal when roaming.
|
||||||
|
|
||||||
|
ver 2.21:
|
||||||
|
Fix issue with pending scan requests after regdom update.
|
||||||
|
Fix issue with handling the rearming of the roaming timeout.
|
||||||
|
Fix issue with survey request and externally triggered scans.
|
||||||
|
Fix issue with RSSI fallback when setting CQM threshold fails.
|
||||||
|
Fix issue with FT-over-Air without offchannel support.
|
||||||
|
Add support for per station Affinities property.
|
||||||
|
|
||||||
ver 2.20:
|
ver 2.20:
|
||||||
Fix issue with PKEX timeout and number of frequencies used.
|
Fix issue with PKEX timeout and number of frequencies used.
|
||||||
Fix issue with handling logic for handshake failures.
|
Fix issue with handling logic for handshake failures.
|
||||||
|
28
Makefile.am
28
Makefile.am
@ -65,12 +65,15 @@ ell_headers = ell/util.h \
|
|||||||
ell/cleanup.h \
|
ell/cleanup.h \
|
||||||
ell/netconfig.h \
|
ell/netconfig.h \
|
||||||
ell/sysctl.h \
|
ell/sysctl.h \
|
||||||
ell/notifylist.h
|
ell/notifylist.h \
|
||||||
|
ell/minheap.h
|
||||||
|
|
||||||
ell_sources = ell/private.h \
|
ell_sources = ell/private.h \
|
||||||
ell/missing.h \
|
ell/missing.h \
|
||||||
ell/util.c \
|
ell/util.c \
|
||||||
|
ell/test-private.h \
|
||||||
ell/test.c \
|
ell/test.c \
|
||||||
|
ell/test-dbus.c \
|
||||||
ell/strv.c \
|
ell/strv.c \
|
||||||
ell/utf8.c \
|
ell/utf8.c \
|
||||||
ell/queue.c \
|
ell/queue.c \
|
||||||
@ -147,7 +150,8 @@ ell_sources = ell/private.h \
|
|||||||
ell/acd.c \
|
ell/acd.c \
|
||||||
ell/netconfig.c \
|
ell/netconfig.c \
|
||||||
ell/sysctl.c \
|
ell/sysctl.c \
|
||||||
ell/notifylist.c
|
ell/notifylist.c \
|
||||||
|
ell/minheap.c
|
||||||
|
|
||||||
ell_shared = ell/useful.h ell/asn1-private.h
|
ell_shared = ell/useful.h ell/asn1-private.h
|
||||||
|
|
||||||
@ -269,6 +273,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h \
|
|||||||
src/json.h src/json.c \
|
src/json.h src/json.c \
|
||||||
src/dpp.c \
|
src/dpp.c \
|
||||||
src/udev.c \
|
src/udev.c \
|
||||||
|
src/pmksa.h src/pmksa.c \
|
||||||
$(eap_sources) \
|
$(eap_sources) \
|
||||||
$(builtin_sources)
|
$(builtin_sources)
|
||||||
|
|
||||||
@ -435,7 +440,8 @@ unit_tests += unit/test-cmac-aes \
|
|||||||
unit/test-ie unit/test-util unit/test-ssid-security \
|
unit/test-ie unit/test-util unit/test-ssid-security \
|
||||||
unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \
|
unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \
|
||||||
unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \
|
unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \
|
||||||
unit/test-dpp unit/test-json unit/test-nl80211util
|
unit/test-dpp unit/test-json unit/test-nl80211util \
|
||||||
|
unit/test-pmksa unit/test-storage
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if CLIENT
|
if CLIENT
|
||||||
@ -454,6 +460,7 @@ unit_test_eap_sim_SOURCES = unit/test-eap-sim.c \
|
|||||||
src/eapol.h src/eapol.c \
|
src/eapol.h src/eapol.c \
|
||||||
src/eapolutil.h src/eapolutil.c \
|
src/eapolutil.h src/eapolutil.c \
|
||||||
src/handshake.h src/handshake.c \
|
src/handshake.h src/handshake.c \
|
||||||
|
src/pmksa.h src/pmksa.c \
|
||||||
src/eap.h src/eap.c src/eap-private.h \
|
src/eap.h src/eap.c src/eap-private.h \
|
||||||
src/util.h src/util.c \
|
src/util.h src/util.c \
|
||||||
src/simauth.h src/simauth.c \
|
src/simauth.h src/simauth.c \
|
||||||
@ -513,6 +520,7 @@ unit_test_eapol_SOURCES = unit/test-eapol.c \
|
|||||||
src/eapol.h src/eapol.c \
|
src/eapol.h src/eapol.c \
|
||||||
src/eapolutil.h src/eapolutil.c \
|
src/eapolutil.h src/eapolutil.c \
|
||||||
src/handshake.h src/handshake.c \
|
src/handshake.h src/handshake.c \
|
||||||
|
src/pmksa.h src/pmksa.c \
|
||||||
src/eap.h src/eap.c src/eap-private.h \
|
src/eap.h src/eap.c src/eap-private.h \
|
||||||
src/eap-tls.c src/eap-ttls.c \
|
src/eap-tls.c src/eap-ttls.c \
|
||||||
src/eap-md5.c src/util.c \
|
src/eap-md5.c src/util.c \
|
||||||
@ -543,6 +551,7 @@ unit_test_wsc_SOURCES = unit/test-wsc.c src/wscutil.h src/wscutil.c \
|
|||||||
src/eapol.h src/eapol.c \
|
src/eapol.h src/eapol.c \
|
||||||
src/eapolutil.h src/eapolutil.c \
|
src/eapolutil.h src/eapolutil.c \
|
||||||
src/handshake.h src/handshake.c \
|
src/handshake.h src/handshake.c \
|
||||||
|
src/pmksa.h src/pmksa.c \
|
||||||
src/eap.h src/eap.c src/eap-private.h \
|
src/eap.h src/eap.c src/eap-private.h \
|
||||||
src/util.h src/util.c \
|
src/util.h src/util.c \
|
||||||
src/erp.h src/erp.c \
|
src/erp.h src/erp.c \
|
||||||
@ -561,6 +570,7 @@ unit_test_sae_SOURCES = unit/test-sae.c \
|
|||||||
src/crypto.h src/crypto.c \
|
src/crypto.h src/crypto.c \
|
||||||
src/ie.h src/ie.c \
|
src/ie.h src/ie.c \
|
||||||
src/handshake.h src/handshake.c \
|
src/handshake.h src/handshake.c \
|
||||||
|
src/pmksa.h src/pmksa.c \
|
||||||
src/erp.h src/erp.c \
|
src/erp.h src/erp.c \
|
||||||
src/band.h src/band.c \
|
src/band.h src/band.c \
|
||||||
src/util.h src/util.c \
|
src/util.h src/util.c \
|
||||||
@ -591,6 +601,15 @@ unit_test_nl80211util_SOURCES = unit/test-nl80211util.c \
|
|||||||
src/ie.h src/ie.c \
|
src/ie.h src/ie.c \
|
||||||
src/util.h src/util.c
|
src/util.h src/util.c
|
||||||
unit_test_nl80211util_LDADD = $(ell_ldadd)
|
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
|
endif
|
||||||
|
|
||||||
if CLIENT
|
if CLIENT
|
||||||
@ -606,6 +625,9 @@ unit_test_client_SOURCES = unit/test-client.c \
|
|||||||
unit_test_client_LDADD = $(ell_ldadd) $(client_ldadd)
|
unit_test_client_LDADD = $(ell_ldadd) $(client_ldadd)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \
|
||||||
|
$(top_srcdir)/build-aux/tap-driver.sh
|
||||||
|
|
||||||
TESTS = $(unit_tests)
|
TESTS = $(unit_tests)
|
||||||
|
|
||||||
EXTRA_DIST = src/genbuiltin src/iwd.service.in src/net.connman.iwd.service \
|
EXTRA_DIST = src/genbuiltin src/iwd.service.in src/net.connman.iwd.service \
|
||||||
|
4
TODO
4
TODO
@ -110,7 +110,7 @@ Wireless monitor
|
|||||||
|
|
||||||
- Subscribe to all nl80211 multicast groups at startup
|
- Subscribe to all nl80211 multicast groups at startup
|
||||||
|
|
||||||
It seems the nlmon packets are limited to actual subscribed mutlicast
|
It seems the nlmon packets are limited to actual subscribed multicast
|
||||||
groups. To get a complete picture of all the nl80211 commands and
|
groups. To get a complete picture of all the nl80211 commands and
|
||||||
events, it is required that iwmon adds membership to all multicast
|
events, it is required that iwmon adds membership to all multicast
|
||||||
groups that the nl80211 lists.
|
groups that the nl80211 lists.
|
||||||
@ -234,7 +234,7 @@ Wireless daemon
|
|||||||
|
|
||||||
- Implement Enrollee Session Overlap Detection after WSC Protocol Run
|
- Implement Enrollee Session Overlap Detection after WSC Protocol Run
|
||||||
|
|
||||||
WSC Best Practices v2.0.1, Section 3.15 describes an enhacement to detect
|
WSC Best Practices v2.0.1, Section 3.15 describes an enhancement to detect
|
||||||
PBC session overlaps. The Enrollee is asked to perform an extra scan without
|
PBC session overlaps. The Enrollee is asked to perform an extra scan without
|
||||||
the PBC request in the ProbeRequest frames after EAP-WSC completes
|
the PBC request in the ProbeRequest frames after EAP-WSC completes
|
||||||
successfully. If another AP in PBC mode is found, then a SessionOverlap
|
successfully. If another AP in PBC mode is found, then a SessionOverlap
|
||||||
|
@ -11,52 +11,58 @@ from iwd import NetworkType
|
|||||||
from hostapd import HostapdCLI
|
from hostapd import HostapdCLI
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
|
def initial_connection(self):
|
||||||
def validate(self, expect_roam=True):
|
ordered_network = self.device.get_ordered_network('TestAPRoam')
|
||||||
wd = IWD()
|
|
||||||
|
|
||||||
devices = wd.list_devices(1)
|
|
||||||
device = devices[0]
|
|
||||||
|
|
||||||
ordered_network = device.get_ordered_network('TestAPRoam')
|
|
||||||
|
|
||||||
self.assertEqual(ordered_network.type, NetworkType.psk)
|
self.assertEqual(ordered_network.type, NetworkType.psk)
|
||||||
|
|
||||||
condition = 'not obj.connected'
|
condition = 'not obj.connected'
|
||||||
wd.wait_for_object_condition(ordered_network.network_object, condition)
|
self.wd.wait_for_object_condition(ordered_network.network_object, condition)
|
||||||
|
|
||||||
device.connect_bssid(self.bss_hostapd[0].bssid)
|
self.device.connect_bssid(self.bss_hostapd[0].bssid)
|
||||||
|
|
||||||
condition = 'obj.state == DeviceState.connected'
|
condition = 'obj.state == DeviceState.connected'
|
||||||
wd.wait_for_object_condition(device, condition)
|
self.wd.wait_for_object_condition(self.device, condition)
|
||||||
|
|
||||||
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED')
|
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED')
|
||||||
|
|
||||||
self.assertFalse(self.bss_hostapd[1].list_sta())
|
self.assertFalse(self.bss_hostapd[1].list_sta())
|
||||||
|
|
||||||
self.bss_hostapd[0].send_bss_transition(device.address,
|
def validate_roam(self, from_bss, to_bss, expect_roam=True):
|
||||||
[(self.bss_hostapd[1].bssid, '8f0000005102060603000000')],
|
from_bss.send_bss_transition(self.device.address,
|
||||||
|
self.neighbor_list,
|
||||||
disassoc_imminent=expect_roam)
|
disassoc_imminent=expect_roam)
|
||||||
|
|
||||||
if expect_roam:
|
if expect_roam:
|
||||||
from_condition = 'obj.state == DeviceState.roaming'
|
from_condition = 'obj.state == DeviceState.roaming'
|
||||||
to_condition = 'obj.state == DeviceState.connected'
|
to_condition = 'obj.state == DeviceState.connected'
|
||||||
wd.wait_for_object_change(device, from_condition, to_condition)
|
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
|
||||||
|
|
||||||
self.bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % device.address)
|
to_bss.wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
|
||||||
else:
|
else:
|
||||||
device.wait_for_event("no-roam-candidates")
|
self.device.wait_for_event("no-roam-candidates")
|
||||||
|
|
||||||
device.disconnect()
|
|
||||||
|
|
||||||
condition = 'not obj.connected'
|
|
||||||
wd.wait_for_object_condition(ordered_network.network_object, condition)
|
|
||||||
|
|
||||||
def test_disassoc_imminent(self):
|
def test_disassoc_imminent(self):
|
||||||
self.validate(expect_roam=True)
|
self.initial_connection()
|
||||||
|
self.validate_roam(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
|
||||||
def test_no_candidates(self):
|
def test_no_candidates(self):
|
||||||
self.validate(expect_roam=False)
|
self.initial_connection()
|
||||||
|
# We now have BSS0 roam blacklisted
|
||||||
|
self.validate_roam(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
# Try and trigger another roam back, which shouldn't happen since now
|
||||||
|
# both BSS's are roam blacklisted
|
||||||
|
self.validate_roam(self.bss_hostapd[1], self.bss_hostapd[0], expect_roam=False)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.wd = IWD(True)
|
||||||
|
|
||||||
|
devices = self.wd.list_devices(1)
|
||||||
|
self.device = devices[0]
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.wd = None
|
||||||
|
self.device = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
@ -65,6 +71,10 @@ class Test(unittest.TestCase):
|
|||||||
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
|
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
|
||||||
HostapdCLI(config='ssid2.conf'),
|
HostapdCLI(config='ssid2.conf'),
|
||||||
HostapdCLI(config='ssid3.conf') ]
|
HostapdCLI(config='ssid3.conf') ]
|
||||||
|
cls.neighbor_list = [
|
||||||
|
(cls.bss_hostapd[0].bssid, "8f0000005101060603000000"),
|
||||||
|
(cls.bss_hostapd[1].bssid, "8f0000005102060603000000"),
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
[SETUP]
|
[SETUP]
|
||||||
num_radios=4
|
num_radios=4
|
||||||
|
hwsim_medium=true
|
||||||
|
start_iwd=false
|
||||||
|
|
||||||
[HOSTAPD]
|
[HOSTAPD]
|
||||||
rad0=ssid1.conf
|
rad0=ssid1.conf
|
||||||
|
6
autotests/testAPRoam/main.conf.roaming
Normal file
6
autotests/testAPRoam/main.conf.roaming
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[General]
|
||||||
|
RoamThreshold=-72
|
||||||
|
CriticalRoamThreshold=-72
|
||||||
|
|
||||||
|
[Blacklist]
|
||||||
|
InitialRoamRequestedTimeout=20
|
183
autotests/testAPRoam/roam_blacklist_test.py
Normal file
183
autotests/testAPRoam/roam_blacklist_test.py
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append('../util')
|
||||||
|
import iwd
|
||||||
|
from iwd import IWD, IWD_CONFIG_DIR
|
||||||
|
from iwd import NetworkType
|
||||||
|
|
||||||
|
from hostapd import HostapdCLI
|
||||||
|
from hwsim import Hwsim
|
||||||
|
|
||||||
|
class Test(unittest.TestCase):
|
||||||
|
def validate_connected(self, hostapd):
|
||||||
|
ordered_network = self.device.get_ordered_network('TestAPRoam')
|
||||||
|
|
||||||
|
self.assertEqual(ordered_network.type, NetworkType.psk)
|
||||||
|
|
||||||
|
condition = 'not obj.connected'
|
||||||
|
self.wd.wait_for_object_condition(ordered_network.network_object, condition)
|
||||||
|
|
||||||
|
self.device.connect_bssid(hostapd.bssid)
|
||||||
|
|
||||||
|
condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_condition(self.device, condition)
|
||||||
|
|
||||||
|
hostapd.wait_for_event('AP-STA-CONNECTED')
|
||||||
|
|
||||||
|
def validate_ap_roamed(self, from_hostapd, to_hostapd):
|
||||||
|
from_hostapd.send_bss_transition(
|
||||||
|
self.device.address, self.neighbor_list, disassoc_imminent=True
|
||||||
|
)
|
||||||
|
|
||||||
|
from_condition = 'obj.state == DeviceState.roaming'
|
||||||
|
to_condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
|
||||||
|
|
||||||
|
to_hostapd.wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
|
||||||
|
|
||||||
|
self.device.wait_for_event("ap-roam-blacklist-added")
|
||||||
|
|
||||||
|
def test_roam_to_optimal_candidates(self):
|
||||||
|
# In this test IWD will naturally transition down the list after each
|
||||||
|
# BSS gets roam blacklisted. All BSS's are above the RSSI thresholds.
|
||||||
|
self.rule_ssid1.signal = -5000
|
||||||
|
self.rule_ssid2.signal = -6500
|
||||||
|
self.rule_ssid3.signal = -6900
|
||||||
|
|
||||||
|
# Connect to BSS0
|
||||||
|
self.validate_connected(self.bss_hostapd[0])
|
||||||
|
|
||||||
|
# AP directed roam to BSS1
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
|
||||||
|
# AP directed roam to BSS2
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[1], self.bss_hostapd[2])
|
||||||
|
|
||||||
|
def test_avoiding_under_threshold_bss(self):
|
||||||
|
# In this test IWD will blacklist BSS0, then roam the BSS1. BSS1 will
|
||||||
|
# then tell IWD to roam, but it should go back to BSS0 since the only
|
||||||
|
# non-blacklisted BSS is under the roam threshold.
|
||||||
|
self.rule_ssid1.signal = -5000
|
||||||
|
self.rule_ssid2.signal = -6500
|
||||||
|
self.rule_ssid3.signal = -7300
|
||||||
|
|
||||||
|
# Connect to BSS0
|
||||||
|
self.validate_connected(self.bss_hostapd[0])
|
||||||
|
|
||||||
|
# AP directed roam to BSS1
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
|
||||||
|
# AP directed roam, but IWD should choose BSS0 since BSS2 is -73dB
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[1], self.bss_hostapd[0])
|
||||||
|
|
||||||
|
def test_connect_to_roam_blacklisted_bss(self):
|
||||||
|
# In this test a BSS will be roam blacklisted, but all other options are
|
||||||
|
# below the RSSI threshold so IWD should roam back to the blacklisted
|
||||||
|
# BSS.
|
||||||
|
self.rule_ssid1.signal = -5000
|
||||||
|
self.rule_ssid2.signal = -8000
|
||||||
|
self.rule_ssid3.signal = -8500
|
||||||
|
|
||||||
|
# Connect to BSS0
|
||||||
|
self.validate_connected(self.bss_hostapd[0])
|
||||||
|
|
||||||
|
# AP directed roam, should connect to BSS1 as its the next best
|
||||||
|
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
|
||||||
|
|
||||||
|
# Connected to BSS1, but the signal is bad, so IWD should try to roam
|
||||||
|
# again. BSS0 is still blacklisted, but its the only reasonable option
|
||||||
|
# since both BSS1 and BSS2 are below the set RSSI threshold (-72dB)
|
||||||
|
|
||||||
|
from_condition = 'obj.state == DeviceState.roaming'
|
||||||
|
to_condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
|
||||||
|
|
||||||
|
# IWD should have connected to BSS0, even though its roam blacklisted
|
||||||
|
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
|
||||||
|
|
||||||
|
def test_blacklist_during_roam_scan(self):
|
||||||
|
# Tests that an AP roam request mid-roam results in the AP still being
|
||||||
|
# blacklisted even though the request itself doesn't directly trigger
|
||||||
|
# a roam.
|
||||||
|
self.rule_ssid1.signal = -7300
|
||||||
|
self.rule_ssid2.signal = -7500
|
||||||
|
self.rule_ssid3.signal = -8500
|
||||||
|
|
||||||
|
# Connect to BSS0 under the roam threshold so IWD will immediately try
|
||||||
|
# roaming elsewhere
|
||||||
|
self.validate_connected(self.bss_hostapd[0])
|
||||||
|
|
||||||
|
self.device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
self.bss_hostapd[0].send_bss_transition(
|
||||||
|
self.device.address, self.neighbor_list, disassoc_imminent=True
|
||||||
|
)
|
||||||
|
self.device.wait_for_event("ap-roam-blacklist-added")
|
||||||
|
|
||||||
|
# BSS0 should have gotten blacklisted even though IWD was mid-roam,
|
||||||
|
# causing IWD to choose BSS1 when it gets is results.
|
||||||
|
|
||||||
|
from_condition = 'obj.state == DeviceState.roaming'
|
||||||
|
to_condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
|
||||||
|
|
||||||
|
self.bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.wd = IWD(True)
|
||||||
|
|
||||||
|
devices = self.wd.list_devices(1)
|
||||||
|
self.device = devices[0]
|
||||||
|
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.wd = None
|
||||||
|
self.device = None
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
IWD.copy_to_storage("main.conf.roaming", IWD_CONFIG_DIR, "main.conf")
|
||||||
|
IWD.copy_to_storage('TestAPRoam.psk')
|
||||||
|
hwsim = Hwsim()
|
||||||
|
|
||||||
|
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
|
||||||
|
HostapdCLI(config='ssid2.conf'),
|
||||||
|
HostapdCLI(config='ssid3.conf') ]
|
||||||
|
HostapdCLI.group_neighbors(*cls.bss_hostapd)
|
||||||
|
|
||||||
|
rad0 = hwsim.get_radio('rad0')
|
||||||
|
rad1 = hwsim.get_radio('rad1')
|
||||||
|
rad2 = hwsim.get_radio('rad2')
|
||||||
|
|
||||||
|
cls.neighbor_list = [
|
||||||
|
(cls.bss_hostapd[0].bssid, "8f0000005101060603000000"),
|
||||||
|
(cls.bss_hostapd[1].bssid, "8f0000005102060603000000"),
|
||||||
|
(cls.bss_hostapd[2].bssid, "8f0000005103060603000000"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
cls.rule_ssid1 = hwsim.rules.create()
|
||||||
|
cls.rule_ssid1.source = rad0.addresses[0]
|
||||||
|
cls.rule_ssid1.bidirectional = True
|
||||||
|
cls.rule_ssid1.enabled = True
|
||||||
|
|
||||||
|
cls.rule_ssid2 = hwsim.rules.create()
|
||||||
|
cls.rule_ssid2.source = rad1.addresses[0]
|
||||||
|
cls.rule_ssid2.bidirectional = True
|
||||||
|
cls.rule_ssid2.enabled = True
|
||||||
|
|
||||||
|
cls.rule_ssid3 = hwsim.rules.create()
|
||||||
|
cls.rule_ssid3.source = rad2.addresses[0]
|
||||||
|
cls.rule_ssid3.bidirectional = True
|
||||||
|
cls.rule_ssid3.enabled = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
IWD.clear_storage()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(exit=True)
|
2
autotests/testAffinity/TestFT.psk
Normal file
2
autotests/testAffinity/TestFT.psk
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[Security]
|
||||||
|
Passphrase=EasilyGuessedPassword
|
41
autotests/testAffinity/ft-psk-ccmp-1.conf
Normal file
41
autotests/testAffinity/ft-psk-ccmp-1.conf
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
hw_mode=g
|
||||||
|
channel=1
|
||||||
|
ssid=TestFT
|
||||||
|
utf8_ssid=1
|
||||||
|
ctrl_interface=/var/run/hostapd
|
||||||
|
|
||||||
|
r1_key_holder=120000000001
|
||||||
|
nas_identifier=dummy1
|
||||||
|
|
||||||
|
wpa=2
|
||||||
|
# Can support WPA-PSK and FT-PSK (space separated list) and/or EAP at the same
|
||||||
|
# time but we want to force FT
|
||||||
|
wpa_key_mgmt=FT-PSK
|
||||||
|
wpa_pairwise=CCMP
|
||||||
|
wpa_passphrase=EasilyGuessedPassword
|
||||||
|
ieee80211w=0
|
||||||
|
rsn_preauth=1
|
||||||
|
rsn_preauth_interfaces=lo
|
||||||
|
disable_pmksa_caching=0
|
||||||
|
# Allow PMK cache to be shared opportunistically among configured interfaces
|
||||||
|
# and BSSes (i.e., all configurations within a single hostapd process).
|
||||||
|
okc=1
|
||||||
|
mobility_domain=1234
|
||||||
|
reassociation_deadline=60000
|
||||||
|
r0kh=12:00:00:00:00:01 dummy1 000102030405060708090a0b0c0d0e0f
|
||||||
|
r0kh=12:00:00:00:00:02 dummy2 000102030405060708090a0b0c0d0e0f
|
||||||
|
r1kh=12:00:00:00:00:01 00:00:00:00:00:01 000102030405060708090a0b0c0d0e0f
|
||||||
|
r1kh=12:00:00:00:00:02 00:00:00:00:00:02 000102030405060708090a0b0c0d0e0f
|
||||||
|
# Push mode only needed for 8021x, not PSK mode since msk already known
|
||||||
|
pmk_r1_push=0
|
||||||
|
# Allow locally generated FT response so we don't have to configure push/pull
|
||||||
|
# between BSSes running as separate hostapd processes as in the test-runner
|
||||||
|
# case. Only works with FT-PSK, otherwise brctl needs to be installed and
|
||||||
|
# CONFIG_BRIDGE enabled in the kernel.
|
||||||
|
ft_psk_generate_local=1
|
||||||
|
rkh_pull_timeout=50
|
||||||
|
ft_over_ds=0
|
||||||
|
ap_table_expiration_time=36000
|
||||||
|
ap_table_max_size=10
|
||||||
|
rrm_neighbor_report=1
|
||||||
|
ocv=1
|
41
autotests/testAffinity/ft-psk-ccmp-2.conf
Normal file
41
autotests/testAffinity/ft-psk-ccmp-2.conf
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
hw_mode=g
|
||||||
|
channel=2
|
||||||
|
ssid=TestFT
|
||||||
|
utf8_ssid=1
|
||||||
|
ctrl_interface=/var/run/hostapd
|
||||||
|
|
||||||
|
r1_key_holder=120000000002
|
||||||
|
nas_identifier=dummy2
|
||||||
|
|
||||||
|
wpa=2
|
||||||
|
# Can support WPA-PSK and FT-PSK (space separated list) and/or EAP at the same
|
||||||
|
# time but we want to force FT
|
||||||
|
wpa_key_mgmt=FT-PSK
|
||||||
|
wpa_pairwise=CCMP
|
||||||
|
wpa_passphrase=EasilyGuessedPassword
|
||||||
|
ieee80211w=0
|
||||||
|
rsn_preauth=1
|
||||||
|
rsn_preauth_interfaces=lo
|
||||||
|
disable_pmksa_caching=0
|
||||||
|
# Allow PMK cache to be shared opportunistically among configured interfaces
|
||||||
|
# and BSSes (i.e., all configurations within a single hostapd process).
|
||||||
|
okc=1
|
||||||
|
mobility_domain=1234
|
||||||
|
reassociation_deadline=60000
|
||||||
|
r0kh=12:00:00:00:00:01 dummy1 000102030405060708090a0b0c0d0e0f
|
||||||
|
r0kh=12:00:00:00:00:02 dummy2 000102030405060708090a0b0c0d0e0f
|
||||||
|
r1kh=12:00:00:00:00:01 00:00:00:00:00:01 000102030405060708090a0b0c0d0e0f
|
||||||
|
r1kh=12:00:00:00:00:02 00:00:00:00:00:02 000102030405060708090a0b0c0d0e0f
|
||||||
|
# Push mode only needed for 8021x, not PSK mode since msk already known
|
||||||
|
pmk_r1_push=0
|
||||||
|
# Allow locally generated FT response so we don't have to configure push/pull
|
||||||
|
# between BSSes running as separate hostapd processes as in the test-runner
|
||||||
|
# case. Only works with FT-PSK, otherwise brctl needs to be installed and
|
||||||
|
# CONFIG_BRIDGE enabled in the kernel.
|
||||||
|
ft_psk_generate_local=1
|
||||||
|
rkh_pull_timeout=50
|
||||||
|
ft_over_ds=0
|
||||||
|
ap_table_expiration_time=36000
|
||||||
|
ap_table_max_size=10
|
||||||
|
rrm_neighbor_report=1
|
||||||
|
ocv=1
|
8
autotests/testAffinity/hw.conf
Normal file
8
autotests/testAffinity/hw.conf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[SETUP]
|
||||||
|
num_radios=3
|
||||||
|
start_iwd=0
|
||||||
|
hwsim_medium=yes
|
||||||
|
|
||||||
|
[HOSTAPD]
|
||||||
|
rad0=ft-psk-ccmp-1.conf
|
||||||
|
rad1=ft-psk-ccmp-2.conf
|
5
autotests/testAffinity/main.conf
Normal file
5
autotests/testAffinity/main.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[Scan]
|
||||||
|
DisableMacAddressRandomization=true
|
||||||
|
|
||||||
|
[General]
|
||||||
|
RoamRetryInterval=1
|
216
autotests/testAffinity/test_set_affinity.py
Normal file
216
autotests/testAffinity/test_set_affinity.py
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#! /usr/bin/python3
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys, os
|
||||||
|
import dbus
|
||||||
|
|
||||||
|
sys.path.append('../util')
|
||||||
|
from config import ctx
|
||||||
|
import iwd
|
||||||
|
from iwd import IWD, IWDDBusAbstract
|
||||||
|
from iwd import NetworkType
|
||||||
|
from hwsim import Hwsim
|
||||||
|
from hostapd import HostapdCLI
|
||||||
|
|
||||||
|
#
|
||||||
|
# Separate client used to test DBus disconnects so we don't bring down the
|
||||||
|
# entire IWD python library
|
||||||
|
#
|
||||||
|
class AffinityClient(IWDDBusAbstract):
|
||||||
|
def __init__(self, device_path):
|
||||||
|
self._bus = dbus.bus.BusConnection(address_or_type=ctx.dbus_address)
|
||||||
|
self._station_prop_if = dbus.Interface(
|
||||||
|
self._bus.get_object(iwd.IWD_SERVICE, device_path),
|
||||||
|
iwd.DBUS_PROPERTIES)
|
||||||
|
|
||||||
|
def set(self, values):
|
||||||
|
self._station_prop_if.Set(iwd.IWD_STATION_INTERFACE, 'Affinities', dbus.Array([dbus.ObjectPath(v) for v in values], signature="o"))
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self._bus.close()
|
||||||
|
|
||||||
|
class Test(unittest.TestCase):
|
||||||
|
def connect(self, device, hapd):
|
||||||
|
ordered_network = device.get_ordered_network('TestFT', full_scan=True)
|
||||||
|
|
||||||
|
self.assertEqual(ordered_network.type, NetworkType.psk)
|
||||||
|
|
||||||
|
condition = 'not obj.connected'
|
||||||
|
self.wd.wait_for_object_condition(ordered_network.network_object, condition)
|
||||||
|
|
||||||
|
device.connect_bssid(hapd.bssid)
|
||||||
|
|
||||||
|
condition = 'obj.state == DeviceState.connected'
|
||||||
|
self.wd.wait_for_object_condition(device, condition)
|
||||||
|
|
||||||
|
def test_set_affinity(self):
|
||||||
|
device = self.wd.list_devices(1)[0]
|
||||||
|
|
||||||
|
self.connect(device, self.bss_hostapd[0])
|
||||||
|
|
||||||
|
print(device.connected_bss)
|
||||||
|
|
||||||
|
device.affinities = [device.connected_bss]
|
||||||
|
|
||||||
|
# IWD should not attempt to roam
|
||||||
|
with self.assertRaises(TimeoutError):
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
device.affinities = []
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
def test_roam_below_critical(self):
|
||||||
|
device = self.wd.list_devices(1)[0]
|
||||||
|
|
||||||
|
self.connect(device, self.bss_hostapd[0])
|
||||||
|
|
||||||
|
device.affinities = [device.connected_bss]
|
||||||
|
|
||||||
|
# IWD should not attempt to roam
|
||||||
|
with self.assertRaises(TimeoutError):
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
# Lower signal past critical level
|
||||||
|
self.bss0_rule.signal = -9000
|
||||||
|
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
def test_error_conditions(self):
|
||||||
|
device = self.wd.list_devices(1)[0]
|
||||||
|
|
||||||
|
# Calling set while disconnected should fail
|
||||||
|
with self.assertRaises(iwd.NotConnectedEx):
|
||||||
|
device.affinities = ["/some/path"]
|
||||||
|
|
||||||
|
self.connect(device, self.bss_hostapd[0])
|
||||||
|
|
||||||
|
device.affinities = [device.connected_bss]
|
||||||
|
|
||||||
|
# An invalid path should fail
|
||||||
|
with self.assertRaises(iwd.InvalidArgumentsEx):
|
||||||
|
device.affinities = [device.connected_bss, "/an/invalid/path"]
|
||||||
|
|
||||||
|
def test_affinity_client_disconnect(self):
|
||||||
|
device = self.wd.list_devices(1)[0]
|
||||||
|
|
||||||
|
client = AffinityClient(device.device_path)
|
||||||
|
|
||||||
|
self.connect(device, self.bss_hostapd[0])
|
||||||
|
|
||||||
|
client.set([device.connected_bss])
|
||||||
|
|
||||||
|
with self.assertRaises(TimeoutError):
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
client._bus.close()
|
||||||
|
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
def test_affinity_client_reconnect_during_roam(self):
|
||||||
|
device = self.wd.list_devices(1)[0]
|
||||||
|
|
||||||
|
client = AffinityClient(device.device_path)
|
||||||
|
|
||||||
|
self.connect(device, self.bss_hostapd[0])
|
||||||
|
|
||||||
|
client.set([device.connected_bss])
|
||||||
|
|
||||||
|
# Lower signal past critical level
|
||||||
|
self.bss0_rule.signal = -9000
|
||||||
|
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
client.close()
|
||||||
|
del client
|
||||||
|
client = AffinityClient(device.device_path)
|
||||||
|
# setting here should get cleared after connecting
|
||||||
|
client.set([device.connected_bss])
|
||||||
|
|
||||||
|
device.wait_for_event("ft-authenticating")
|
||||||
|
device.wait_for_event("associating")
|
||||||
|
device.wait_for_event("connected")
|
||||||
|
|
||||||
|
# Affinity should be reset, and IWD should be trying to roam
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
def test_cleanup_with_connected_client(self):
|
||||||
|
device = self.wd.list_devices(1)[0]
|
||||||
|
|
||||||
|
client = AffinityClient(device.device_path)
|
||||||
|
|
||||||
|
self.connect(device, self.bss_hostapd[0])
|
||||||
|
|
||||||
|
client.set([device.connected_bss])
|
||||||
|
self.wd.stop()
|
||||||
|
|
||||||
|
def test_affinity_removed_after_roam(self):
|
||||||
|
device = self.wd.list_devices(1)[0]
|
||||||
|
|
||||||
|
self.connect(device, self.bss_hostapd[0])
|
||||||
|
|
||||||
|
device.affinities = [device.connected_bss]
|
||||||
|
|
||||||
|
# Lower signal past critical level
|
||||||
|
self.bss0_rule.signal = -9000
|
||||||
|
|
||||||
|
device.wait_for_event("roam-scan-triggered")
|
||||||
|
|
||||||
|
device.wait_for_event("ft-authenticating")
|
||||||
|
device.wait_for_event("associating")
|
||||||
|
device.wait_for_event("connected")
|
||||||
|
|
||||||
|
self.assertEqual(device.affinities, [])
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
self.wd.stop()
|
||||||
|
self.wd = None
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.bss0_rule.signal = -8000
|
||||||
|
self.bss1_rule.signal = -8000
|
||||||
|
|
||||||
|
self.wd = IWD(True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
hwsim = Hwsim()
|
||||||
|
|
||||||
|
IWD.copy_to_storage('TestFT.psk')
|
||||||
|
|
||||||
|
cls.bss_hostapd = [ HostapdCLI(config='ft-psk-ccmp-1.conf'),
|
||||||
|
HostapdCLI(config='ft-psk-ccmp-2.conf') ]
|
||||||
|
|
||||||
|
rad0 = hwsim.get_radio('rad0')
|
||||||
|
rad1 = hwsim.get_radio('rad1')
|
||||||
|
|
||||||
|
cls.bss0_rule = hwsim.rules.create()
|
||||||
|
cls.bss0_rule.source = rad0.addresses[0]
|
||||||
|
cls.bss0_rule.bidirectional = True
|
||||||
|
cls.bss0_rule.signal = -8000
|
||||||
|
cls.bss0_rule.enabled = True
|
||||||
|
|
||||||
|
cls.bss1_rule = hwsim.rules.create()
|
||||||
|
cls.bss1_rule.source = rad1.addresses[0]
|
||||||
|
cls.bss1_rule.bidirectional = True
|
||||||
|
cls.bss1_rule.signal = -8000
|
||||||
|
cls.bss1_rule.enabled = True
|
||||||
|
|
||||||
|
cls.bss_hostapd[0].set_address('12:00:00:00:00:01')
|
||||||
|
cls.bss_hostapd[1].set_address('12:00:00:00:00:02')
|
||||||
|
|
||||||
|
HostapdCLI.group_neighbors(*cls.bss_hostapd)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
IWD.clear_storage()
|
||||||
|
cls.bss_hostapd = None
|
||||||
|
cls.bss0_rule.remove()
|
||||||
|
cls.bss1_rule.remove()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(exit=True)
|
2
autotests/testBSSBlacklist/TestBlacklist.psk
Normal file
2
autotests/testBSSBlacklist/TestBlacklist.psk
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[Security]
|
||||||
|
Passphrase=secret123
|
@ -260,12 +260,69 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
self.wd.unregister_psk_agent(psk_agent)
|
self.wd.unregister_psk_agent(psk_agent)
|
||||||
|
|
||||||
|
def test_blacklist_disabled(self):
|
||||||
|
wd = self.wd
|
||||||
|
bss_hostapd = self.bss_hostapd
|
||||||
|
|
||||||
|
rule0 = self.rule0
|
||||||
|
rule1 = self.rule1
|
||||||
|
rule2 = self.rule2
|
||||||
|
|
||||||
|
psk_agent = PSKAgent(["secret123", 'secret123'])
|
||||||
|
wd.register_psk_agent(psk_agent)
|
||||||
|
|
||||||
|
devices = wd.list_devices(1)
|
||||||
|
device = devices[0]
|
||||||
|
|
||||||
|
rule0.drop = True
|
||||||
|
rule0.enabled = True
|
||||||
|
|
||||||
|
device.autoconnect = True
|
||||||
|
|
||||||
|
condition = 'obj.state == DeviceState.connected'
|
||||||
|
wd.wait_for_object_condition(device, condition)
|
||||||
|
|
||||||
|
ordered_network = device.get_ordered_network("TestBlacklist", full_scan=True)
|
||||||
|
|
||||||
|
self.assertEqual(ordered_network.type, NetworkType.psk)
|
||||||
|
|
||||||
|
# The first BSS should fail, and we should connect to the second. This
|
||||||
|
# should not result in a connection blacklist though since its disabled.
|
||||||
|
bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % device.address)
|
||||||
|
|
||||||
|
device.disconnect()
|
||||||
|
|
||||||
|
rule0.drop = False
|
||||||
|
device.autoconnect = True
|
||||||
|
|
||||||
|
# Verify the first BSS wasn't blacklisted.
|
||||||
|
bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % device.address)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
_, _, name = self.id().split(".")
|
||||||
|
|
||||||
|
# TODO: If we have this pattern elsewhere it might be nice to turn this
|
||||||
|
# into a decorator e.g.
|
||||||
|
#
|
||||||
|
# @config("main.conf.disabled")
|
||||||
|
# @profile("TestBlacklist.psk")
|
||||||
|
# def test_blacklist_disabled(self)
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
if name == "test_blacklist_disabled":
|
||||||
|
IWD.copy_to_storage("main.conf.disabled", IWD_CONFIG_DIR, "main.conf")
|
||||||
|
IWD.copy_to_storage("TestBlacklist.psk")
|
||||||
|
else:
|
||||||
|
IWD.copy_to_storage("main.conf.default", IWD_CONFIG_DIR, "main.conf")
|
||||||
|
|
||||||
self.wd = IWD(True)
|
self.wd = IWD(True)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
IWD.clear_storage()
|
IWD.clear_storage()
|
||||||
self.wd = None
|
self.wd = None
|
||||||
|
self.rule0.drop = False
|
||||||
|
self.rule1.drop = False
|
||||||
|
self.rule2.drop = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
2
autotests/testBSSBlacklist/main.conf.disabled
Normal file
2
autotests/testBSSBlacklist/main.conf.disabled
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[Blacklist]
|
||||||
|
InitialTimeout=0
|
@ -11,7 +11,7 @@ from iwd import IWD
|
|||||||
|
|
||||||
class Test8021xNetwork(unittest.TestCase):
|
class Test8021xNetwork(unittest.TestCase):
|
||||||
'''
|
'''
|
||||||
The bellow test cases excesise the following connection scenarios:
|
The below test cases excesise the following connection scenarios:
|
||||||
|
|
||||||
Network config is
|
Network config is
|
||||||
present at start time: Connect: AutoConnect: Result:
|
present at start time: Connect: AutoConnect: Result:
|
||||||
|
@ -11,7 +11,7 @@ from iwd import IWD
|
|||||||
|
|
||||||
class TestOpenNetwork(unittest.TestCase):
|
class TestOpenNetwork(unittest.TestCase):
|
||||||
'''
|
'''
|
||||||
The bellow test cases excesise the following connection scenarios:
|
The below test cases excesise the following connection scenarios:
|
||||||
|
|
||||||
Network config is
|
Network config is
|
||||||
present at start time: Connect: AutoConnect: Result:
|
present at start time: Connect: AutoConnect: Result:
|
||||||
|
@ -11,7 +11,7 @@ from iwd import IWD
|
|||||||
|
|
||||||
class TestWpaNetwork(unittest.TestCase):
|
class TestWpaNetwork(unittest.TestCase):
|
||||||
'''
|
'''
|
||||||
The bellow test cases exercise the following connection scenarios:
|
The below test cases exercise the following connection scenarios:
|
||||||
|
|
||||||
Network config is
|
Network config is
|
||||||
present at start time: Connect: AutoConnect: Result:
|
present at start time: Connect: AutoConnect: Result:
|
||||||
|
@ -104,7 +104,7 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertTrue(self.profile_is_encrypted('ssidCCMP.psk'))
|
self.assertTrue(self.profile_is_encrypted('ssidCCMP.psk'))
|
||||||
|
|
||||||
# Tests that a profile that doesn't decrypt wont become a known network
|
# Tests that a profile that doesn't decrypt won't become a known network
|
||||||
def test_decryption_failure(self):
|
def test_decryption_failure(self):
|
||||||
bad_config = \
|
bad_config = \
|
||||||
'''
|
'''
|
||||||
|
@ -3,7 +3,11 @@ import sys
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
from scapy.layers.dot11 import *
|
from scapy.layers.dot11 import *
|
||||||
from scapy.arch import str2mac, get_if_raw_hwaddr
|
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 time import time, sleep
|
from time import time, sleep
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ from hostapd import HostapdCLI
|
|||||||
|
|
||||||
class TestWpaNetwork(unittest.TestCase):
|
class TestWpaNetwork(unittest.TestCase):
|
||||||
'''
|
'''
|
||||||
The bellow test cases excesise the following connection scenarios:
|
The below test cases excesise the following connection scenarios:
|
||||||
|
|
||||||
Network config is
|
Network config is
|
||||||
present at start time: Connect: AutoConnect: Result:
|
present at start time: Connect: AutoConnect: Result:
|
||||||
|
@ -12,7 +12,7 @@ from hostapd import HostapdCLI
|
|||||||
|
|
||||||
class TestOpenNetwork(unittest.TestCase):
|
class TestOpenNetwork(unittest.TestCase):
|
||||||
'''
|
'''
|
||||||
The bellow test cases excesise the following connection scenarios:
|
The below test cases excesise the following connection scenarios:
|
||||||
|
|
||||||
Network config is
|
Network config is
|
||||||
present at start time: Connect: AutoConnect: Result:
|
present at start time: Connect: AutoConnect: Result:
|
||||||
|
@ -12,7 +12,7 @@ from hostapd import HostapdCLI
|
|||||||
|
|
||||||
class TestWpaNetwork(unittest.TestCase):
|
class TestWpaNetwork(unittest.TestCase):
|
||||||
'''
|
'''
|
||||||
The bellow test cases excesise the following connection scenarios:
|
The below test cases excesise the following connection scenarios:
|
||||||
|
|
||||||
Network config is
|
Network config is
|
||||||
present at start time: Connect: AutoConnect: Result:
|
present at start time: Connect: AutoConnect: Result:
|
||||||
|
@ -11,7 +11,7 @@ from iwd import NetworkType
|
|||||||
|
|
||||||
class TestMFP(unittest.TestCase):
|
class TestMFP(unittest.TestCase):
|
||||||
'''
|
'''
|
||||||
The bellow test cases excesise the following MFP option setting scenarios:
|
The below test cases excesise the following MFP option setting scenarios:
|
||||||
|
|
||||||
IWD_MFP: AP_MFP: Result:
|
IWD_MFP: AP_MFP: Result:
|
||||||
0 0 No MFP, connection succeeds
|
0 0 No MFP, connection succeeds
|
||||||
|
@ -137,7 +137,7 @@ class Test(unittest.TestCase):
|
|||||||
# since (T2 - T1) / 2 is shorter than 60s. It is now about 10s since the last
|
# since (T2 - T1) / 2 is shorter than 60s. It is now about 10s since the last
|
||||||
# renewal or 5s before the next DHCPREQUEST frame that is going to be lost. We'll
|
# renewal or 5s before the next DHCPREQUEST frame that is going to be lost. We'll
|
||||||
# wait T1 seconds, so until about 10s after the failed attempt, we'll check that
|
# wait T1 seconds, so until about 10s after the failed attempt, we'll check that
|
||||||
# there was no renewal by that time, just in case, and we'll reenable frame delivery.
|
# there was no renewal by that time, just in case, and we'll re-enable frame delivery.
|
||||||
# We'll then wait another 60s and we should see the lease has been successfully
|
# We'll then wait another 60s and we should see the lease has been successfully
|
||||||
# renewed some 10 seconds earlier on the 1st DHCPREQUEST retransmission.
|
# renewed some 10 seconds earlier on the 1st DHCPREQUEST retransmission.
|
||||||
#
|
#
|
||||||
|
114
autotests/testPMKSA-SAE/connection_test.py
Normal file
114
autotests/testPMKSA-SAE/connection_test.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.append('../util')
|
||||||
|
from iwd import IWD
|
||||||
|
from iwd import PSKAgent
|
||||||
|
from iwd import NetworkType
|
||||||
|
from hostapd import HostapdCLI
|
||||||
|
import testutil
|
||||||
|
|
||||||
|
class Test(unittest.TestCase):
|
||||||
|
|
||||||
|
def validate_connection(self, wd, ssid, hostapd, expected_group):
|
||||||
|
psk_agent = PSKAgent("secret123")
|
||||||
|
wd.register_psk_agent(psk_agent)
|
||||||
|
|
||||||
|
devices = wd.list_devices(1)
|
||||||
|
self.assertIsNotNone(devices)
|
||||||
|
device = devices[0]
|
||||||
|
|
||||||
|
device.disconnect()
|
||||||
|
|
||||||
|
network = device.get_ordered_network(ssid, full_scan=True)
|
||||||
|
|
||||||
|
self.assertEqual(network.type, NetworkType.psk)
|
||||||
|
|
||||||
|
network.network_object.connect()
|
||||||
|
|
||||||
|
condition = 'obj.state == DeviceState.connected'
|
||||||
|
wd.wait_for_object_condition(device, condition)
|
||||||
|
|
||||||
|
wd.wait(2)
|
||||||
|
|
||||||
|
testutil.test_iface_operstate(intf=device.name)
|
||||||
|
testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
|
||||||
|
|
||||||
|
# Initial connection PMKSA should not be used. So we should see the
|
||||||
|
# SAE group set.
|
||||||
|
sta_status = hostapd.sta_status(device.address)
|
||||||
|
self.assertEqual(int(sta_status["sae_group"]), expected_group)
|
||||||
|
|
||||||
|
device.disconnect()
|
||||||
|
|
||||||
|
condition = 'not obj.connected'
|
||||||
|
wd.wait_for_object_condition(network.network_object, condition)
|
||||||
|
|
||||||
|
wd.unregister_psk_agent(psk_agent)
|
||||||
|
|
||||||
|
network.network_object.connect(wait=False)
|
||||||
|
|
||||||
|
condition = 'obj.state == DeviceState.connected'
|
||||||
|
wd.wait_for_object_condition(device, condition)
|
||||||
|
|
||||||
|
wd.wait(2)
|
||||||
|
|
||||||
|
testutil.test_iface_operstate(intf=device.name)
|
||||||
|
testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
|
||||||
|
|
||||||
|
# Having connected once prior we should have a PMKSA and SAE should not
|
||||||
|
# have been used.
|
||||||
|
sta_status = hostapd.sta_status(device.address)
|
||||||
|
self.assertNotIn("sae_group", sta_status.keys())
|
||||||
|
|
||||||
|
device.disconnect()
|
||||||
|
|
||||||
|
condition = 'not obj.connected'
|
||||||
|
wd.wait_for_object_condition(network.network_object, condition)
|
||||||
|
|
||||||
|
hostapd.pmksa_flush()
|
||||||
|
|
||||||
|
wd.wait(5)
|
||||||
|
|
||||||
|
network.network_object.connect()
|
||||||
|
|
||||||
|
device.wait_for_event("pmksa-invalid-pmkid")
|
||||||
|
|
||||||
|
condition = 'obj.state == DeviceState.connected'
|
||||||
|
wd.wait_for_object_condition(device, condition)
|
||||||
|
|
||||||
|
wd.wait(2)
|
||||||
|
|
||||||
|
testutil.test_iface_operstate(intf=device.name)
|
||||||
|
testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
|
||||||
|
|
||||||
|
# Manually flushing the PMKSA from the AP then reconnecting we should
|
||||||
|
# have failed (INVALID_PMKID) then retried the same BSS with SAE, not
|
||||||
|
# PMKSA.
|
||||||
|
sta_status = hostapd.sta_status(device.address)
|
||||||
|
self.assertEqual(int(sta_status["sae_group"]), expected_group)
|
||||||
|
|
||||||
|
def test_pmksa_sae(self):
|
||||||
|
self.hostapd.wait_for_event("AP-ENABLED")
|
||||||
|
self.validate_connection(self.wd, "ssidSAE", self.hostapd, 19)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.hostapd.default()
|
||||||
|
self.wd = IWD(True)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.wd.clear_storage()
|
||||||
|
self.wd = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
cls.hostapd = HostapdCLI(config='ssidSAE.conf')
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main(exit=True)
|
7
autotests/testPMKSA-SAE/hw.conf
Normal file
7
autotests/testPMKSA-SAE/hw.conf
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[SETUP]
|
||||||
|
num_radios=2
|
||||||
|
start_iwd=0
|
||||||
|
hwsim_medium=yes
|
||||||
|
|
||||||
|
[HOSTAPD]
|
||||||
|
rad0=ssidSAE.conf
|
12
autotests/testPMKSA-SAE/ssidSAE.conf
Normal file
12
autotests/testPMKSA-SAE/ssidSAE.conf
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
hw_mode=g
|
||||||
|
channel=1
|
||||||
|
ssid=ssidSAE
|
||||||
|
|
||||||
|
wpa=2
|
||||||
|
wpa_key_mgmt=SAE
|
||||||
|
wpa_pairwise=CCMP
|
||||||
|
sae_password=secret123
|
||||||
|
sae_groups=19
|
||||||
|
ieee80211w=2
|
||||||
|
sae_pwe=0
|
||||||
|
rsn_preauth=1
|
@ -17,7 +17,7 @@ from hwsim import Hwsim
|
|||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
# Normally the time between a failed roam attempt and the next roam attempt
|
# Normally the time between a failed roam attempt and the next roam attempt
|
||||||
# is 60 seconds (default RoamRetryInterval). Test that we retry roaming
|
# is 60 seconds (default RoamRetryInterval). Test that we retry roaming
|
||||||
# faster if the transision looks like this: LOW [roam] [same bss] HIGH LOW.
|
# faster if the transition looks like this: LOW [roam] [same bss] HIGH LOW.
|
||||||
def test_fast_retry(self):
|
def test_fast_retry(self):
|
||||||
hwsim = Hwsim()
|
hwsim = Hwsim()
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from hostapd import HostapdCLI
|
|||||||
from hwsim import Hwsim
|
from hwsim import Hwsim
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
# Test that we do not periodically retry roaming if the transision looks
|
# Test that we do not periodically retry roaming if the transition looks
|
||||||
# like this: LOW [roam] [new bss] HIGH.
|
# like this: LOW [roam] [new bss] HIGH.
|
||||||
def test_stop_retry(self):
|
def test_stop_retry(self):
|
||||||
hwsim = Hwsim()
|
hwsim = Hwsim()
|
||||||
|
@ -13,7 +13,7 @@ import testutil
|
|||||||
from config import ctx
|
from config import ctx
|
||||||
|
|
||||||
class Test(unittest.TestCase):
|
class Test(unittest.TestCase):
|
||||||
def validate_connection(self, wd, ft=True):
|
def validate_connection(self, wd, ft=True, check_used_pmksa=False):
|
||||||
device = wd.list_devices(1)[0]
|
device = wd.list_devices(1)[0]
|
||||||
|
|
||||||
# This won't guarantee all BSS's are found, but at least ensures that
|
# This won't guarantee all BSS's are found, but at least ensures that
|
||||||
@ -37,6 +37,14 @@ class Test(unittest.TestCase):
|
|||||||
self.assertRaises(Exception, testutil.test_ifaces_connected,
|
self.assertRaises(Exception, testutil.test_ifaces_connected,
|
||||||
(self.bss_hostapd[1].ifname, device.name, True, True))
|
(self.bss_hostapd[1].ifname, device.name, True, True))
|
||||||
|
|
||||||
|
# If PMKSA was used, hostapd should not include the sae_group key in
|
||||||
|
# its status for the station.
|
||||||
|
sta_status = self.bss_hostapd[0].sta_status(device.address)
|
||||||
|
if check_used_pmksa:
|
||||||
|
self.assertNotIn("sae_group", sta_status.keys())
|
||||||
|
else:
|
||||||
|
self.assertIn("sae_group", sta_status.keys())
|
||||||
|
|
||||||
device.roam(self.bss_hostapd[1].bssid)
|
device.roam(self.bss_hostapd[1].bssid)
|
||||||
|
|
||||||
# Check that iwd is on BSS 1 once out of roaming state and doesn't
|
# Check that iwd is on BSS 1 once out of roaming state and doesn't
|
||||||
@ -88,6 +96,31 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
self.validate_connection(wd, True)
|
self.validate_connection(wd, True)
|
||||||
|
|
||||||
|
def test_ft_roam_pmksa(self):
|
||||||
|
wd = IWD(True)
|
||||||
|
|
||||||
|
self.bss_hostapd[0].set_value('wpa_key_mgmt', 'FT-SAE SAE')
|
||||||
|
self.bss_hostapd[0].reload()
|
||||||
|
self.bss_hostapd[0].wait_for_event("AP-ENABLED")
|
||||||
|
self.bss_hostapd[1].set_value('wpa_key_mgmt', 'FT-SAE SAE')
|
||||||
|
self.bss_hostapd[1].reload()
|
||||||
|
self.bss_hostapd[1].wait_for_event("AP-ENABLED")
|
||||||
|
self.bss_hostapd[2].set_value('wpa_key_mgmt', 'FT-PSK')
|
||||||
|
self.bss_hostapd[2].reload()
|
||||||
|
self.bss_hostapd[2].wait_for_event("AP-ENABLED")
|
||||||
|
|
||||||
|
self.validate_connection(wd, True)
|
||||||
|
|
||||||
|
device = wd.list_devices(1)[0]
|
||||||
|
device.disconnect()
|
||||||
|
|
||||||
|
for hapd in self.bss_hostapd:
|
||||||
|
hapd.deauthenticate(device.address)
|
||||||
|
|
||||||
|
wd.wait(5)
|
||||||
|
|
||||||
|
self.validate_connection(wd, True, check_used_pmksa=True)
|
||||||
|
|
||||||
def test_reassociate_roam_success(self):
|
def test_reassociate_roam_success(self):
|
||||||
wd = IWD(True)
|
wd = IWD(True)
|
||||||
|
|
||||||
@ -103,6 +136,31 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
self.validate_connection(wd, False)
|
self.validate_connection(wd, False)
|
||||||
|
|
||||||
|
def test_reassociate_roam_pmksa(self):
|
||||||
|
wd = IWD(True)
|
||||||
|
|
||||||
|
self.bss_hostapd[0].set_value('wpa_key_mgmt', 'SAE')
|
||||||
|
self.bss_hostapd[0].reload()
|
||||||
|
self.bss_hostapd[0].wait_for_event("AP-ENABLED")
|
||||||
|
self.bss_hostapd[1].set_value('wpa_key_mgmt', 'SAE')
|
||||||
|
self.bss_hostapd[1].reload()
|
||||||
|
self.bss_hostapd[1].wait_for_event("AP-ENABLED")
|
||||||
|
self.bss_hostapd[2].set_value('wpa_key_mgmt', 'WPA-PSK')
|
||||||
|
self.bss_hostapd[2].reload()
|
||||||
|
self.bss_hostapd[2].wait_for_event("AP-ENABLED")
|
||||||
|
|
||||||
|
self.validate_connection(wd, False)
|
||||||
|
|
||||||
|
device = wd.list_devices(1)[0]
|
||||||
|
device.disconnect()
|
||||||
|
|
||||||
|
for hapd in self.bss_hostapd:
|
||||||
|
hapd.deauthenticate(device.address)
|
||||||
|
|
||||||
|
wd.wait(5)
|
||||||
|
|
||||||
|
self.validate_connection(wd, False, check_used_pmksa=True)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" down')
|
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[1].ifname + '" down')
|
||||||
|
@ -4,3 +4,6 @@
|
|||||||
# hardware, but fails when used in simulated environment with mac80211_hwsim.
|
# hardware, but fails when used in simulated environment with mac80211_hwsim.
|
||||||
# Disable MAC randomization for the tests with hidden networks.
|
# Disable MAC randomization for the tests with hidden networks.
|
||||||
DisableMacAddressRandomization=true
|
DisableMacAddressRandomization=true
|
||||||
|
|
||||||
|
[General]
|
||||||
|
DisablePMKSA=true
|
||||||
|
@ -368,3 +368,7 @@ class HostapdCLI(object):
|
|||||||
others = [h for h in args if h != hapd]
|
others = [h for h in args if h != hapd]
|
||||||
|
|
||||||
hapd._add_neighbors(*others)
|
hapd._add_neighbors(*others)
|
||||||
|
|
||||||
|
def pmksa_flush(self):
|
||||||
|
cmd = self.cmdline + ['pmksa_flush']
|
||||||
|
ctx.start_process(cmd).wait()
|
||||||
|
@ -7,7 +7,10 @@ from weakref import WeakValueDictionary
|
|||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from scapy.all import *
|
from scapy.all import *
|
||||||
from scapy.contrib.wpa_eapol import WPA_key
|
try:
|
||||||
|
from scapy.contrib.wpa_eapol import WPA_key
|
||||||
|
except:
|
||||||
|
from scapy.layers.eap import EAPOL_KEY
|
||||||
|
|
||||||
import iwd
|
import iwd
|
||||||
from config import ctx
|
from config import ctx
|
||||||
@ -444,9 +447,15 @@ class Hwsim(iwd.AsyncOpAbstract):
|
|||||||
|
|
||||||
# NOTE: Expected key_info is 0x008a, with the install flag
|
# NOTE: Expected key_info is 0x008a, with the install flag
|
||||||
# this becomes 0x00ca.
|
# this becomes 0x00ca.
|
||||||
|
try:
|
||||||
eapol = WPA_key( descriptor_type = 2,
|
eapol = WPA_key( descriptor_type = 2,
|
||||||
key_info = 0x00ca, # Includes an invalid install flag!
|
key_info = 0x00ca, # Includes an invalid install flag!
|
||||||
replay_counter = struct.pack(">Q", 100))
|
replay_counter = struct.pack(">Q", 100))
|
||||||
|
except:
|
||||||
|
eapol = EAPOL_KEY( key_descriptor_type = 2,
|
||||||
|
install = 1,
|
||||||
|
key_ack = 1,
|
||||||
|
key_replay_counter = 1)
|
||||||
frame /= LLC()/SNAP()/EAPOL(version="802.1X-2004", type="EAPOL-Key")
|
frame /= LLC()/SNAP()/EAPOL(version="802.1X-2004", type="EAPOL-Key")
|
||||||
frame /= eapol
|
frame /= eapol
|
||||||
|
|
||||||
|
@ -450,13 +450,15 @@ class Device(IWDDBusAbstract):
|
|||||||
self._wps_manager_if = None
|
self._wps_manager_if = None
|
||||||
self._station_if = None
|
self._station_if = None
|
||||||
self._station_props = None
|
self._station_props = None
|
||||||
self._station_debug_obj = None
|
|
||||||
self._dpp_obj = None
|
self._dpp_obj = None
|
||||||
self._sc_dpp_obj = None
|
self._sc_dpp_obj = None
|
||||||
self._ap_obj = None
|
self._ap_obj = None
|
||||||
|
|
||||||
IWDDBusAbstract.__init__(self, *args, **kwargs)
|
IWDDBusAbstract.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
self._station_debug_obj = StationDebug(object_path=self._object_path,
|
||||||
|
namespace=self._namespace)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _wps_manager(self):
|
def _wps_manager(self):
|
||||||
if self._wps_manager_if is None:
|
if self._wps_manager_if is None:
|
||||||
@ -596,6 +598,11 @@ class Device(IWDDBusAbstract):
|
|||||||
props = self._station_properties()
|
props = self._station_properties()
|
||||||
return props.get('ConnectedNetwork')
|
return props.get('ConnectedNetwork')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def connected_bss(self):
|
||||||
|
props = self._station_properties()
|
||||||
|
return props.get('ConnectedAccessPoint')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def powered(self):
|
def powered(self):
|
||||||
'''
|
'''
|
||||||
@ -630,6 +637,19 @@ class Device(IWDDBusAbstract):
|
|||||||
self._station_debug._prop_proxy.Set(IWD_STATION_DEBUG_INTERFACE,
|
self._station_debug._prop_proxy.Set(IWD_STATION_DEBUG_INTERFACE,
|
||||||
'AutoConnect', value)
|
'AutoConnect', value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def affinities(self):
|
||||||
|
return self._station_properties()['Affinities']
|
||||||
|
|
||||||
|
@affinities.setter
|
||||||
|
def affinities(self, values):
|
||||||
|
self._station_properties()
|
||||||
|
self._station_prop_if.Set(
|
||||||
|
IWD_STATION_INTERFACE, 'Affinities',
|
||||||
|
dbus.Array([dbus.ObjectPath(v) for v in values], signature="o"),
|
||||||
|
reply_handler=self._success, error_handler=self._failure)
|
||||||
|
self._wait_for_async_op()
|
||||||
|
|
||||||
def scan(self, wait=True):
|
def scan(self, wait=True):
|
||||||
'''Schedule a network scan.
|
'''Schedule a network scan.
|
||||||
|
|
||||||
@ -1463,10 +1483,10 @@ class IWD(AsyncOpAbstract):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_in_storage(file_name, file_content, storage_dir=IWD_STORAGE_DIR):
|
def create_in_storage(file_name, file_content, storage_dir=IWD_STORAGE_DIR):
|
||||||
fo = open(storage_dir + '/' + file_name, 'w')
|
f = open(storage_dir + '/' + file_name, 'w')
|
||||||
|
|
||||||
fo.write(file_content)
|
f.write(file_content)
|
||||||
fo.close()
|
f.close()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _ensure_storage_dir_exists(storage_dir):
|
def _ensure_storage_dir_exists(storage_dir):
|
||||||
|
@ -237,7 +237,7 @@ class Wpas:
|
|||||||
('' if go_intent is None else ' go_intent=' + str(go_intent)))
|
('' if go_intent is None else ' go_intent=' + str(go_intent)))
|
||||||
self.wait_for_event('OK')
|
self.wait_for_event('OK')
|
||||||
|
|
||||||
# Pre-accept the next GO Negotiation Request from this peer to avoid the extra Respone + Request frames
|
# Pre-accept the next GO Negotiation Request from this peer to avoid the extra Response + Request frames
|
||||||
def p2p_authorize(self, peer, pin=None, go_intent=None):
|
def p2p_authorize(self, peer, pin=None, go_intent=None):
|
||||||
self._rx_data = []
|
self._rx_data = []
|
||||||
self._ctrl_request('P2P_CONNECT ' + peer['p2p_dev_addr'] + ' ' + ('pbc' if pin is None else pin) +
|
self._ctrl_request('P2P_CONNECT ' + peer['p2p_dev_addr'] + ' ' + ('pbc' if pin is None else pin) +
|
||||||
|
@ -186,5 +186,5 @@ void diagnostic_display(struct l_dbus_message_iter *dict,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
parse_error:
|
parse_error:
|
||||||
display_error("Error parsing dignostics");
|
display_error("Error parsing diagnostics");
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ static void network_display_inline(const char *margin, const void *data)
|
|||||||
|
|
||||||
display("%s%s %s %s\n", margin, network->name ? network->name : "",
|
display("%s%s %s %s\n", margin, network->name ? network->name : "",
|
||||||
network->type ? network->type : "",
|
network->type ? network->type : "",
|
||||||
network->connected ? "connected" : "diconnected");
|
network->connected ? "connected" : "disconnected");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *network_create(void)
|
static void *network_create(void)
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
AC_PREREQ([2.69])
|
AC_PREREQ([2.69])
|
||||||
AC_INIT([iwd],[2.20])
|
AC_INIT([iwd],[3.6])
|
||||||
|
|
||||||
AC_CONFIG_HEADERS(config.h)
|
AC_CONFIG_HEADERS(config.h)
|
||||||
AC_CONFIG_AUX_DIR(build-aux)
|
AC_CONFIG_AUX_DIR(build-aux)
|
||||||
AC_CONFIG_MACRO_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
|
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
|
||||||
tar-pax no-dist-gzip dist-xz])
|
tar-pax no-dist-gzip dist-xz])
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ AC_PROG_CC_GCOV
|
|||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
AC_PROG_MKDIR_P
|
AC_PROG_MKDIR_P
|
||||||
AC_PROG_LN_S
|
AC_PROG_LN_S
|
||||||
|
AC_PROG_AWK
|
||||||
|
|
||||||
AC_SYS_LARGEFILE
|
AC_SYS_LARGEFILE
|
||||||
|
|
||||||
@ -297,7 +300,7 @@ if (test "${enable_external_ell}" = "yes"); then
|
|||||||
test "${enable_monitor}" != "no" ||
|
test "${enable_monitor}" != "no" ||
|
||||||
test "${enable_wired}" = "yes" ||
|
test "${enable_wired}" = "yes" ||
|
||||||
test "${enable_hwsim}" = "yes"); then
|
test "${enable_hwsim}" = "yes"); then
|
||||||
ell_min_version="0.68"
|
ell_min_version="0.72"
|
||||||
else
|
else
|
||||||
ell_min_version="0.5"
|
ell_min_version="0.5"
|
||||||
fi
|
fi
|
||||||
|
@ -322,10 +322,10 @@ M18: Use appropriate logging levels
|
|||||||
An appropriate log level should be used depending on the type of message
|
An appropriate log level should be used depending on the type of message
|
||||||
being logged. Logging is done using the l_log APIs in ELL:
|
being logged. Logging is done using the l_log APIs in ELL:
|
||||||
|
|
||||||
l_error An unexpected condition ocurred. These are generally fatal to the
|
l_error An unexpected condition occurred. These are generally fatal to the
|
||||||
current connection/protocol that is running but not generally to IWD's
|
current connection/protocol that is running but not generally to IWD's
|
||||||
overall operation.
|
overall operation.
|
||||||
l_warn An unexpected, but non-fatal condition ocurred
|
l_warn An unexpected, but non-fatal condition occurred
|
||||||
l_notice Should not be used directly. This log level is reserved for special
|
l_notice Should not be used directly. This log level is reserved for special
|
||||||
event type notifications which is handled by iwd_notice().
|
event type notifications which is handled by iwd_notice().
|
||||||
l_info Information that is expected during normal operation. l_info's use
|
l_info Information that is expected during normal operation. l_info's use
|
||||||
|
@ -135,7 +135,7 @@ Object path /net/connman/iwd/{phy0,phy1,...}/{1,2,...}
|
|||||||
void StartConfigurator(object agent_path)
|
void StartConfigurator(object agent_path)
|
||||||
|
|
||||||
Start a shared code configurator using an agent
|
Start a shared code configurator using an agent
|
||||||
(distingushed by 'agent_path') to obtain the shared
|
(distinguished by 'agent_path') to obtain the shared
|
||||||
code. This method is meant for an automated use case
|
code. This method is meant for an automated use case
|
||||||
where a configurator is capable of configuring multiple
|
where a configurator is capable of configuring multiple
|
||||||
enrollees, and distinguishing between them by their
|
enrollees, and distinguishing between them by their
|
||||||
@ -196,7 +196,7 @@ Methods void Release() [noreply]
|
|||||||
string RequestSharedCode(string identifier)
|
string RequestSharedCode(string identifier)
|
||||||
|
|
||||||
This method gets called when a shared code is requested
|
This method gets called when a shared code is requested
|
||||||
for a particular enrollee, distingushed by the
|
for a particular enrollee, distinguished by the
|
||||||
identifier. The shared code agent should lookup the
|
identifier. The shared code agent should lookup the
|
||||||
identifier and return the shared code, or return an
|
identifier and return the shared code, or return an
|
||||||
error if not found.
|
error if not found.
|
||||||
|
@ -5,7 +5,7 @@ credentials for your e.g. cable/cellular provider, or via a dedicated account
|
|||||||
like Boingo. Lots of these services also allow you to roam between networks.
|
like Boingo. Lots of these services also allow you to roam between networks.
|
||||||
|
|
||||||
The underlying authentication is standard WPA2-Enterprise but Hotspot 2.0 adds a
|
The underlying authentication is standard WPA2-Enterprise but Hotspot 2.0 adds a
|
||||||
'discovery' stage to identifiying networks. This discovery is done using ANQP,
|
'discovery' stage to identifying networks. This discovery is done using ANQP,
|
||||||
which queries the network for additional information to determine if the client
|
which queries the network for additional information to determine if the client
|
||||||
has the credentials to connect.
|
has the credentials to connect.
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ Methods array(on) GetPeers()
|
|||||||
between requested threshold values is a compromise
|
between requested threshold values is a compromise
|
||||||
between resolution and the frequency of system
|
between resolution and the frequency of system
|
||||||
wakeups and context-switches that are going to be
|
wakeups and context-switches that are going to be
|
||||||
occuring to update the client's signal meter. Only
|
occurring to update the client's signal meter. Only
|
||||||
one agent can be registered at any time.
|
one agent can be registered at any time.
|
||||||
|
|
||||||
Possible errors: [service].Error.InvalidArguments
|
Possible errors: [service].Error.InvalidArguments
|
||||||
|
@ -166,10 +166,27 @@ Properties string State [readonly]
|
|||||||
|
|
||||||
object ConnectedAccessPoint [readonly, optional]
|
object ConnectedAccessPoint [readonly, optional]
|
||||||
|
|
||||||
net.connman.iwd.BasicServiceSet object represeting the
|
net.connman.iwd.BasicServiceSet object representing the
|
||||||
BSS the device is currently connected to or to which
|
BSS the device is currently connected to or to which
|
||||||
a connection is in progress.
|
a connection is in progress.
|
||||||
|
|
||||||
|
ao Affinities [optional] [experimental]
|
||||||
|
|
||||||
|
Array of net.connman.iwd.BasicServiceSet object paths
|
||||||
|
that will be treated with higher affinity compared to
|
||||||
|
other BSS's. Currently the only allowed value to be
|
||||||
|
set in this array is the path to the currently connected
|
||||||
|
BasicServiceSet object, i.e.
|
||||||
|
Station.ConnectedAccessPoint.
|
||||||
|
|
||||||
|
Setting the affinity will lower the roaming threshold,
|
||||||
|
effectively locking IWD to the current BSS unless the
|
||||||
|
RSSI drops below the critical threshold set by
|
||||||
|
[General].CriticalRoamThreshold{5G} at which point
|
||||||
|
IWD will proceed with normal roaming behavior.
|
||||||
|
|
||||||
|
This property is cleared on roams/disconnections.
|
||||||
|
|
||||||
SignalLevelAgent hierarchy
|
SignalLevelAgent hierarchy
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ supplicant running IWD:
|
|||||||
#~~~~~~~~~~~~~~~~~~~~~~~~~ hw.conf ~~~~~~~~~~~~~~~~~~~~~~~~~
|
#~~~~~~~~~~~~~~~~~~~~~~~~~ hw.conf ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
# Lines starting with # are ignored
|
# Lines starting with # are ignored
|
||||||
|
|
||||||
# 'SETUP' is a manditory configuration group.
|
# 'SETUP' is a mandatory configuration group.
|
||||||
[SETUP]
|
[SETUP]
|
||||||
#
|
#
|
||||||
# Total number of radios requested per network setup. This includes
|
# Total number of radios requested per network setup. This includes
|
||||||
|
@ -578,7 +578,7 @@ static int analyze_pcap(const char *pathname)
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
printf(" Number of packets: %lu\n", pkt_count);
|
printf(" Number of packets: %lu\n", pkt_count);
|
||||||
printf(" Short packets: %lu\n", pkt_short);
|
printf(" Short packets: %lu\n", pkt_short);
|
||||||
printf(" Tuncated packets: %lu\n", pkt_trunc);
|
printf(" Truncated packets: %lu\n", pkt_trunc);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf(" Ethernet packets: %lu\n", pkt_ether);
|
printf(" Ethernet packets: %lu\n", pkt_ether);
|
||||||
printf(" PAE packets: %lu\n", pkt_pae);
|
printf(" PAE packets: %lu\n", pkt_pae);
|
||||||
@ -726,6 +726,10 @@ static void usage(void)
|
|||||||
"\t-y, --nowiphy Don't show 'New Wiphy' output\n"
|
"\t-y, --nowiphy Don't show 'New Wiphy' output\n"
|
||||||
"\t-s, --noscan Don't show scan result output\n"
|
"\t-s, --noscan Don't show scan result output\n"
|
||||||
"\t-e, --noies Don't show IEs except SSID\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-h, --help Show help options\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -739,6 +743,9 @@ static const struct option main_options[] = {
|
|||||||
{ "nowiphy", no_argument, NULL, 'y' },
|
{ "nowiphy", no_argument, NULL, 'y' },
|
||||||
{ "noscan", no_argument, NULL, 's' },
|
{ "noscan", no_argument, NULL, 's' },
|
||||||
{ "noies", no_argument, NULL, 'e' },
|
{ "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' },
|
{ "version", no_argument, NULL, 'v' },
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ }
|
{ }
|
||||||
@ -754,7 +761,7 @@ int main(int argc, char *argv[])
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
int opt;
|
int opt;
|
||||||
|
|
||||||
opt = getopt_long(argc, argv, "r:w:a:i:nvhyse",
|
opt = getopt_long(argc, argv, "r:w:a:i:t:W:C:nvhyse",
|
||||||
main_options, NULL);
|
main_options, NULL);
|
||||||
if (opt < 0)
|
if (opt < 0)
|
||||||
break;
|
break;
|
||||||
@ -784,6 +791,35 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
case 'e':
|
case 'e':
|
||||||
config.noies = true;
|
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;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
printf("%s\n", VERSION);
|
printf("%s\n", VERSION);
|
||||||
|
163
monitor/nlmon.c
163
monitor/nlmon.c
@ -29,6 +29,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
@ -42,12 +43,21 @@
|
|||||||
#include <linux/genetlink.h>
|
#include <linux/genetlink.h>
|
||||||
#include <linux/rtnetlink.h>
|
#include <linux/rtnetlink.h>
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
#include <ell/ell.h>
|
#include <ell/ell.h>
|
||||||
|
|
||||||
#ifndef ARPHRD_NETLINK
|
#ifndef ARPHRD_NETLINK
|
||||||
#define ARPHRD_NETLINK 824
|
#define ARPHRD_NETLINK 824
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef RMNET_FLAGS_INGRESS_MAP_CKSUMV5
|
||||||
|
#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 4)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef RMNET_FLAGS_EGRESS_MAP_CKSUMV5
|
||||||
|
#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 5)
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "linux/nl80211.h"
|
#include "linux/nl80211.h"
|
||||||
|
|
||||||
#include "ell/useful.h"
|
#include "ell/useful.h"
|
||||||
@ -85,6 +95,8 @@
|
|||||||
#define BSS_CAPABILITY_APSD (1<<11)
|
#define BSS_CAPABILITY_APSD (1<<11)
|
||||||
#define BSS_CAPABILITY_DSSS_OFDM (1<<13)
|
#define BSS_CAPABILITY_DSSS_OFDM (1<<13)
|
||||||
|
|
||||||
|
#define BYTES_PER_MB 1000000
|
||||||
|
|
||||||
struct nlmon *cur_nlmon;
|
struct nlmon *cur_nlmon;
|
||||||
|
|
||||||
enum msg_type {
|
enum msg_type {
|
||||||
@ -105,6 +117,12 @@ struct nlmon {
|
|||||||
bool noscan;
|
bool noscan;
|
||||||
bool noies;
|
bool noies;
|
||||||
bool read;
|
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 {
|
struct nlmon_req {
|
||||||
@ -177,11 +195,15 @@ static void nlmon_req_free(void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static time_t time_offset = ((time_t) -1);
|
static time_t time_offset = ((time_t) -1);
|
||||||
|
static enum time_format time_format;
|
||||||
|
|
||||||
static inline void update_time_offset(const struct timeval *tv)
|
static inline void update_time_offset(const struct timeval *tv,
|
||||||
|
enum time_format tf)
|
||||||
{
|
{
|
||||||
if (tv && time_offset == ((time_t) -1))
|
if (tv && time_offset == ((time_t) -1)) {
|
||||||
time_offset = tv->tv_sec;
|
time_offset = tv->tv_sec;
|
||||||
|
time_format = tf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
|
#define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
|
||||||
@ -217,15 +239,38 @@ static void print_packet(const struct timeval *tv, char ident,
|
|||||||
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
|
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
|
||||||
|
|
||||||
if (tv) {
|
if (tv) {
|
||||||
|
struct tm *tm;
|
||||||
|
|
||||||
if (use_color()) {
|
if (use_color()) {
|
||||||
n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP);
|
n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP);
|
||||||
if (n > 0)
|
if (n > 0)
|
||||||
ts_pos += n;
|
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_sec - time_offset,
|
||||||
(int64_t)tv->tv_usec);
|
(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) {
|
if (n > 0) {
|
||||||
ts_pos += n;
|
ts_pos += n;
|
||||||
ts_len += n;
|
ts_len += n;
|
||||||
@ -483,7 +528,30 @@ static void print_ie_country(unsigned int level, const char *label,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_attr(level, "%s: %c%c%c", label, code[0], code[1], code[2]);
|
print_attr(level, "%s: %c%c", label, code[0], code[1]);
|
||||||
|
|
||||||
|
switch (code[2]) {
|
||||||
|
case ' ':
|
||||||
|
print_attr(level + 1,
|
||||||
|
"3rd octet: 0x%02x: All environments", code[2]);
|
||||||
|
break;
|
||||||
|
case 'O':
|
||||||
|
print_attr(level + 1,
|
||||||
|
"3rd octet: 0x%02x: Outdoor environments", code[2]);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
print_attr(level + 1,
|
||||||
|
"3rd octet: 0x%02x: Indoor environments", code[2]);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
print_attr(level + 1,
|
||||||
|
"3rd octet: 0x%02x: Non-country entity", code[2]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
print_attr(level + 1,
|
||||||
|
"3rd octet: 0x%02x: Annex E table", code[2]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
while (i < size) {
|
while (i < size) {
|
||||||
if (code[i] > 200) {
|
if (code[i] > 200) {
|
||||||
@ -1650,7 +1718,7 @@ static void print_ie_vht_capabilities(unsigned int level,
|
|||||||
[21] = "TXOP PS",
|
[21] = "TXOP PS",
|
||||||
[22] = "+HTC-VHT Capable",
|
[22] = "+HTC-VHT Capable",
|
||||||
[23 ... 25] = "Maximum A-MPDU Length Exponent",
|
[23 ... 25] = "Maximum A-MPDU Length Exponent",
|
||||||
[26 ... 27] = "VHT Link Adapation Capable",
|
[26 ... 27] = "VHT Link Adaptation Capable",
|
||||||
[28] = "RX Antenna Pattern Consistency",
|
[28] = "RX Antenna Pattern Consistency",
|
||||||
[29] = "TX Antenna Pattern Consistency",
|
[29] = "TX Antenna Pattern Consistency",
|
||||||
[30 ... 31] = "Extended NSS BW Support",
|
[30 ... 31] = "Extended NSS BW Support",
|
||||||
@ -1847,7 +1915,7 @@ static void print_ie_interworking(unsigned int level,
|
|||||||
size--;
|
size--;
|
||||||
ptr++;
|
ptr++;
|
||||||
|
|
||||||
if (!size)
|
if (size < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -7333,6 +7401,64 @@ static bool nlmon_req_match(const void *a, const void *b)
|
|||||||
return (req->seq == match->seq && req->pid == match->pid);
|
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,
|
static void store_packet(struct nlmon *nlmon, const struct timeval *tv,
|
||||||
uint16_t pkt_type,
|
uint16_t pkt_type,
|
||||||
uint16_t arphrd_type,
|
uint16_t arphrd_type,
|
||||||
@ -7341,7 +7467,7 @@ static void store_packet(struct nlmon *nlmon, const struct timeval *tv,
|
|||||||
{
|
{
|
||||||
uint8_t sll_hdr[16], *buf = sll_hdr;
|
uint8_t sll_hdr[16], *buf = sll_hdr;
|
||||||
|
|
||||||
if (!nlmon->pcap)
|
if (!check_pcap(nlmon, sizeof(sll_hdr) + size))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memset(sll_hdr, 0, sizeof(sll_hdr));
|
memset(sll_hdr, 0, sizeof(sll_hdr));
|
||||||
@ -7466,6 +7592,10 @@ struct nlmon *nlmon_create(uint16_t id, const struct nlmon_config *config)
|
|||||||
nlmon->noscan = config->noscan;
|
nlmon->noscan = config->noscan;
|
||||||
nlmon->noies = config->noies;
|
nlmon->noies = config->noies;
|
||||||
nlmon->read = config->read_only;
|
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;
|
return nlmon;
|
||||||
}
|
}
|
||||||
@ -8302,7 +8432,10 @@ void nlmon_print_rtnl(struct nlmon *nlmon, const struct timeval *tv,
|
|||||||
int64_t aligned_size = NLMSG_ALIGN(size);
|
int64_t aligned_size = NLMSG_ALIGN(size);
|
||||||
const struct nlmsghdr *nlmsg;
|
const struct nlmsghdr *nlmsg;
|
||||||
|
|
||||||
update_time_offset(tv);
|
if (nlmon->nortnl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
update_time_offset(tv, nlmon->time_format);
|
||||||
|
|
||||||
for (nlmsg = data; NLMSG_OK(nlmsg, aligned_size);
|
for (nlmsg = data; NLMSG_OK(nlmsg, aligned_size);
|
||||||
nlmsg = NLMSG_NEXT(nlmsg, aligned_size)) {
|
nlmsg = NLMSG_NEXT(nlmsg, aligned_size)) {
|
||||||
@ -8340,7 +8473,7 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
|
|||||||
{
|
{
|
||||||
const struct nlmsghdr *nlmsg;
|
const struct nlmsghdr *nlmsg;
|
||||||
|
|
||||||
update_time_offset(tv);
|
update_time_offset(tv, nlmon->time_format);
|
||||||
|
|
||||||
for (nlmsg = data; NLMSG_OK(nlmsg, size);
|
for (nlmsg = data; NLMSG_OK(nlmsg, size);
|
||||||
nlmsg = NLMSG_NEXT(nlmsg, size)) {
|
nlmsg = NLMSG_NEXT(nlmsg, size)) {
|
||||||
@ -8363,7 +8496,7 @@ void nlmon_print_pae(struct nlmon *nlmon, const struct timeval *tv,
|
|||||||
{
|
{
|
||||||
char extra_str[16];
|
char extra_str[16];
|
||||||
|
|
||||||
update_time_offset(tv);
|
update_time_offset(tv, nlmon->time_format);
|
||||||
|
|
||||||
sprintf(extra_str, "len %u", size);
|
sprintf(extra_str, "len %u", size);
|
||||||
|
|
||||||
@ -8490,13 +8623,20 @@ struct nlmon *nlmon_open(uint16_t id, const char *pathname,
|
|||||||
struct nlmon *nlmon;
|
struct nlmon *nlmon;
|
||||||
struct l_io *pae_io;
|
struct l_io *pae_io;
|
||||||
struct pcap *pcap;
|
struct pcap *pcap;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
pae_io = open_pae();
|
pae_io = open_pae();
|
||||||
if (!pae_io)
|
if (!pae_io)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (pathname) {
|
if (pathname) {
|
||||||
pcap = pcap_create(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);
|
||||||
if (!pcap) {
|
if (!pcap) {
|
||||||
l_io_destroy(pae_io);
|
l_io_destroy(pae_io);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -8509,6 +8649,7 @@ struct nlmon *nlmon_open(uint16_t id, const char *pathname,
|
|||||||
|
|
||||||
nlmon->pae_io = pae_io;
|
nlmon->pae_io = pae_io;
|
||||||
nlmon->pcap = pcap;
|
nlmon->pcap = pcap;
|
||||||
|
nlmon->file_prefix = l_strdup(pathname);
|
||||||
|
|
||||||
l_io_set_read_handler(nlmon->pae_io, pae_receive, nlmon, NULL);
|
l_io_set_read_handler(nlmon->pae_io, pae_receive, nlmon, NULL);
|
||||||
|
|
||||||
@ -8531,5 +8672,7 @@ void nlmon_close(struct nlmon *nlmon)
|
|||||||
if (nlmon->pcap)
|
if (nlmon->pcap)
|
||||||
pcap_close(nlmon->pcap);
|
pcap_close(nlmon->pcap);
|
||||||
|
|
||||||
|
l_free(nlmon->file_prefix);
|
||||||
|
|
||||||
l_free(nlmon);
|
l_free(nlmon);
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,22 @@
|
|||||||
|
|
||||||
struct nlmon;
|
struct nlmon;
|
||||||
|
|
||||||
|
enum time_format {
|
||||||
|
TIME_FORMAT_DELTA,
|
||||||
|
TIME_FORMAT_UTC,
|
||||||
|
};
|
||||||
|
|
||||||
struct nlmon_config {
|
struct nlmon_config {
|
||||||
bool nortnl;
|
bool nortnl;
|
||||||
bool nowiphy;
|
bool nowiphy;
|
||||||
bool noscan;
|
bool noscan;
|
||||||
bool noies;
|
bool noies;
|
||||||
bool read_only;
|
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,
|
struct nlmon *nlmon_open(uint16_t id, const char *pathname,
|
||||||
|
@ -60,6 +60,7 @@ struct pcap {
|
|||||||
bool closed;
|
bool closed;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t snaplen;
|
uint32_t snaplen;
|
||||||
|
size_t size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pcap *pcap_open(const char *pathname)
|
struct pcap *pcap_open(const char *pathname)
|
||||||
@ -152,6 +153,8 @@ struct pcap *pcap_create(const char *pathname)
|
|||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcap->size += len;
|
||||||
|
|
||||||
return pcap;
|
return pcap;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
@ -188,6 +191,11 @@ uint32_t pcap_get_snaplen(struct pcap *pcap)
|
|||||||
return pcap->snaplen;
|
return pcap->snaplen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t pcap_get_size(struct pcap *pcap)
|
||||||
|
{
|
||||||
|
return pcap->size;
|
||||||
|
}
|
||||||
|
|
||||||
bool pcap_read(struct pcap *pcap, struct timeval *tv,
|
bool pcap_read(struct pcap *pcap, struct timeval *tv,
|
||||||
void *data, uint32_t size, uint32_t *len, uint32_t *real_len)
|
void *data, uint32_t size, uint32_t *len, uint32_t *real_len)
|
||||||
{
|
{
|
||||||
@ -279,5 +287,7 @@ bool pcap_write(struct pcap *pcap, const struct timeval *tv,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pcap->size += written;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ void pcap_close(struct pcap *pcap);
|
|||||||
|
|
||||||
uint32_t pcap_get_type(struct pcap *pcap);
|
uint32_t pcap_get_type(struct pcap *pcap);
|
||||||
uint32_t pcap_get_snaplen(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,
|
bool pcap_read(struct pcap *pcap, struct timeval *tv,
|
||||||
void *data, uint32_t size, uint32_t *len, uint32_t *real_len);
|
void *data, uint32_t size, uint32_t *len, uint32_t *real_len);
|
||||||
|
@ -94,13 +94,13 @@ static void adhoc_sta_free(void *data)
|
|||||||
eapol_sm_free(sta->sm);
|
eapol_sm_free(sta->sm);
|
||||||
|
|
||||||
if (sta->hs_sta)
|
if (sta->hs_sta)
|
||||||
handshake_state_free(sta->hs_sta);
|
handshake_state_unref(sta->hs_sta);
|
||||||
|
|
||||||
if (sta->sm_a)
|
if (sta->sm_a)
|
||||||
eapol_sm_free(sta->sm_a);
|
eapol_sm_free(sta->sm_a);
|
||||||
|
|
||||||
if (sta->hs_auth)
|
if (sta->hs_auth)
|
||||||
handshake_state_free(sta->hs_auth);
|
handshake_state_unref(sta->hs_auth);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
l_free(sta);
|
l_free(sta);
|
||||||
|
@ -234,7 +234,7 @@ uint32_t anqp_request(uint64_t wdev_id, const uint8_t *addr,
|
|||||||
request->anqp_cb = cb;
|
request->anqp_cb = cb;
|
||||||
request->anqp_destroy = destroy;
|
request->anqp_destroy = destroy;
|
||||||
/*
|
/*
|
||||||
* WPA3 Specificiation version 3, Section 9.4:
|
* WPA3 Specification version 3, Section 9.4:
|
||||||
* "A STA shall use a randomized dialog token for every new GAS
|
* "A STA shall use a randomized dialog token for every new GAS
|
||||||
* exchange."
|
* exchange."
|
||||||
*/
|
*/
|
||||||
|
@ -131,7 +131,7 @@ char **anqp_parse_nai_realms(const unsigned char *anqp, unsigned int len)
|
|||||||
uint16_t count;
|
uint16_t count;
|
||||||
|
|
||||||
if (len < 2)
|
if (len < 2)
|
||||||
return false;
|
return NULL;
|
||||||
|
|
||||||
count = l_get_le16(anqp);
|
count = l_get_le16(anqp);
|
||||||
|
|
||||||
|
14
src/ap.c
14
src/ap.c
@ -230,7 +230,7 @@ static void ap_stop_handshake(struct sta_state *sta)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sta->hs) {
|
if (sta->hs) {
|
||||||
handshake_state_free(sta->hs);
|
handshake_state_unref(sta->hs);
|
||||||
sta->hs = NULL;
|
sta->hs = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3914,34 +3914,34 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
|
|||||||
|
|
||||||
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
||||||
(MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST << 4),
|
(MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST << 4),
|
||||||
NULL, 0, ap_assoc_req_cb, ap, NULL))
|
NULL, 0, false, ap_assoc_req_cb, ap, NULL))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
||||||
(MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST << 4),
|
(MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST << 4),
|
||||||
NULL, 0, ap_reassoc_req_cb, ap, NULL))
|
NULL, 0, false, ap_reassoc_req_cb, ap, NULL))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!wiphy_supports_probe_resp_offload(wiphy)) {
|
if (!wiphy_supports_probe_resp_offload(wiphy)) {
|
||||||
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
||||||
(MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST << 4),
|
(MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST << 4),
|
||||||
NULL, 0, ap_probe_req_cb, ap, NULL))
|
NULL, 0, false, ap_probe_req_cb, ap, NULL))
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
||||||
(MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION << 4),
|
(MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION << 4),
|
||||||
NULL, 0, ap_disassoc_cb, ap, NULL))
|
NULL, 0, false, ap_disassoc_cb, ap, NULL))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
||||||
(MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION << 4),
|
(MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION << 4),
|
||||||
NULL, 0, ap_auth_cb, ap, NULL))
|
NULL, 0, false, ap_auth_cb, ap, NULL))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
if (!frame_watch_add(wdev_id, 0, 0x0000 |
|
||||||
(MPDU_MANAGEMENT_SUBTYPE_DEAUTHENTICATION << 4),
|
(MPDU_MANAGEMENT_SUBTYPE_DEAUTHENTICATION << 4),
|
||||||
NULL, 0, ap_deauth_cb, ap, NULL))
|
NULL, 0, false, ap_deauth_cb, ap, NULL))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
ap->mlme_watch = l_genl_family_register(ap->nl80211, "mlme",
|
ap->mlme_watch = l_genl_family_register(ap->nl80211, "mlme",
|
||||||
|
2
src/ap.h
2
src/ap.h
@ -79,7 +79,7 @@ struct ap_ops {
|
|||||||
void *user_data);
|
void *user_data);
|
||||||
/*
|
/*
|
||||||
* If not null, writes extra IEs to be added to the outgoing frame of
|
* If not null, writes extra IEs to be added to the outgoing frame of
|
||||||
* given type and, if it's not a beacon frame, in reponse to a given
|
* given type and, if it's not a beacon frame, in response to a given
|
||||||
* client frame. May also react to the extra IEs in that frame.
|
* client frame. May also react to the extra IEs in that frame.
|
||||||
* Returns the number of bytes written which must be less than or
|
* Returns the number of bytes written which must be less than or
|
||||||
* equal to the number returned by .get_extra_ies_len when called
|
* equal to the number returned by .get_extra_ies_len when called
|
||||||
|
@ -54,7 +54,7 @@ void __iwd_backtrace_print(unsigned int offset)
|
|||||||
int pathlen;
|
int pathlen;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
|
||||||
if (program_exec == NULL)
|
if (!program_exec)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pathlen = strlen(program_path);
|
pathlen = strlen(program_path);
|
||||||
@ -186,7 +186,7 @@ void __iwd_backtrace_init(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (program_exec == NULL)
|
if (!program_exec)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
program_path = getcwd(cwd, sizeof(cwd));
|
program_path = getcwd(cwd, sizeof(cwd));
|
||||||
|
@ -896,7 +896,7 @@ static const struct operating_class_info e4_operating_classes[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.operating_class = 136,
|
.operating_class = 136,
|
||||||
.starting_frequency = 5950,
|
.starting_frequency = 5925,
|
||||||
.channel_spacing = 20,
|
.channel_spacing = 20,
|
||||||
.center_frequencies = { 2 },
|
.center_frequencies = { 2 },
|
||||||
}
|
}
|
||||||
@ -1352,6 +1352,10 @@ check_e4:
|
|||||||
const struct operating_class_info *info =
|
const struct operating_class_info *info =
|
||||||
&e4_operating_classes[i];
|
&e4_operating_classes[i];
|
||||||
|
|
||||||
|
if (band != band_oper_class_to_band(NULL,
|
||||||
|
info->operating_class))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (e4_has_frequency(info, freq) == 0 ||
|
if (e4_has_frequency(info, freq) == 0 ||
|
||||||
e4_has_ccfi(info, freq) == 0) {
|
e4_has_ccfi(info, freq) == 0) {
|
||||||
if (out_band)
|
if (out_band)
|
||||||
@ -1426,7 +1430,7 @@ static const char *const oper_class_eu_codes[] = {
|
|||||||
"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
|
"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
|
||||||
"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
|
"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
|
||||||
"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
|
"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
|
||||||
"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK"
|
"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", "GB"
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Annex E, table E-1 */
|
/* Annex E, table E-1 */
|
||||||
|
120
src/blacklist.c
120
src/blacklist.c
@ -45,22 +45,42 @@
|
|||||||
|
|
||||||
static uint64_t blacklist_multiplier;
|
static uint64_t blacklist_multiplier;
|
||||||
static uint64_t blacklist_initial_timeout;
|
static uint64_t blacklist_initial_timeout;
|
||||||
|
static uint64_t blacklist_roam_initial_timeout;
|
||||||
static uint64_t blacklist_max_timeout;
|
static uint64_t blacklist_max_timeout;
|
||||||
|
|
||||||
struct blacklist_entry {
|
struct blacklist_entry {
|
||||||
uint8_t addr[6];
|
uint8_t addr[6];
|
||||||
uint64_t added_time;
|
uint64_t added_time;
|
||||||
uint64_t expire_time;
|
uint64_t expire_time;
|
||||||
|
enum blacklist_reason reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct blacklist_search {
|
||||||
|
const uint8_t *addr;
|
||||||
|
enum blacklist_reason reason;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct l_queue *blacklist;
|
static struct l_queue *blacklist;
|
||||||
|
|
||||||
|
static uint64_t get_reason_timeout(enum blacklist_reason reason)
|
||||||
|
{
|
||||||
|
switch (reason) {
|
||||||
|
case BLACKLIST_REASON_CONNECT_FAILED:
|
||||||
|
return blacklist_initial_timeout;
|
||||||
|
case BLACKLIST_REASON_ROAM_REQUESTED:
|
||||||
|
return blacklist_roam_initial_timeout;
|
||||||
|
default:
|
||||||
|
l_warn("Unhandled blacklist reason: %u", reason);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool check_if_expired(void *data, void *user_data)
|
static bool check_if_expired(void *data, void *user_data)
|
||||||
{
|
{
|
||||||
struct blacklist_entry *entry = data;
|
struct blacklist_entry *entry = data;
|
||||||
uint64_t now = l_get_u64(user_data);
|
uint64_t now = l_get_u64(user_data);
|
||||||
|
|
||||||
if (l_time_diff(now, entry->added_time) > blacklist_max_timeout) {
|
if (l_time_after(now, entry->expire_time)) {
|
||||||
l_debug("Removing entry "MAC" on prune", MAC_STR(entry->addr));
|
l_debug("Removing entry "MAC" on prune", MAC_STR(entry->addr));
|
||||||
l_free(entry);
|
l_free(entry);
|
||||||
return true;
|
return true;
|
||||||
@ -87,17 +107,53 @@ static bool match_addr(const void *a, const void *b)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blacklist_add_bss(const uint8_t *addr)
|
static bool match_addr_and_reason(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct blacklist_entry *entry = a;
|
||||||
|
const struct blacklist_search *search = b;
|
||||||
|
|
||||||
|
if (entry->reason != search->reason)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!memcmp(entry->addr, search->addr, 6))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason)
|
||||||
{
|
{
|
||||||
struct blacklist_entry *entry;
|
struct blacklist_entry *entry;
|
||||||
|
uint64_t timeout;
|
||||||
|
|
||||||
blacklist_prune();
|
blacklist_prune();
|
||||||
|
|
||||||
|
timeout = get_reason_timeout(reason);
|
||||||
|
if (!timeout)
|
||||||
|
return;
|
||||||
|
|
||||||
entry = l_queue_find(blacklist, match_addr, addr);
|
entry = l_queue_find(blacklist, match_addr, addr);
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
uint64_t offset = l_time_diff(entry->added_time,
|
uint64_t offset;
|
||||||
entry->expire_time);
|
|
||||||
|
if (reason < entry->reason) {
|
||||||
|
l_debug("Promoting "MAC" blacklist to reason %u",
|
||||||
|
MAC_STR(addr), reason);
|
||||||
|
/* Reset this to the new timeout and reason */
|
||||||
|
entry->reason = reason;
|
||||||
|
entry->added_time = l_time_now();
|
||||||
|
entry->expire_time = l_time_offset(entry->added_time,
|
||||||
|
timeout);
|
||||||
|
return;
|
||||||
|
} else if (reason > entry->reason) {
|
||||||
|
l_debug("Ignoring blacklist extension of "MAC", "
|
||||||
|
"current blacklist status is more severe!",
|
||||||
|
MAC_STR(addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = l_time_diff(entry->added_time, entry->expire_time);
|
||||||
|
|
||||||
offset *= blacklist_multiplier;
|
offset *= blacklist_multiplier;
|
||||||
|
|
||||||
@ -112,40 +168,36 @@ void blacklist_add_bss(const uint8_t *addr)
|
|||||||
entry = l_new(struct blacklist_entry, 1);
|
entry = l_new(struct blacklist_entry, 1);
|
||||||
|
|
||||||
entry->added_time = l_time_now();
|
entry->added_time = l_time_now();
|
||||||
entry->expire_time = l_time_offset(entry->added_time,
|
entry->expire_time = l_time_offset(entry->added_time, timeout);
|
||||||
blacklist_initial_timeout);
|
entry->reason = reason;
|
||||||
memcpy(entry->addr, addr, 6);
|
memcpy(entry->addr, addr, 6);
|
||||||
|
|
||||||
l_queue_push_tail(blacklist, entry);
|
l_queue_push_tail(blacklist, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool blacklist_contains_bss(const uint8_t *addr)
|
bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason)
|
||||||
{
|
{
|
||||||
bool ret;
|
struct blacklist_search search = {
|
||||||
uint64_t time_now;
|
.addr = addr,
|
||||||
struct blacklist_entry *entry;
|
.reason = reason
|
||||||
|
};
|
||||||
|
|
||||||
blacklist_prune();
|
blacklist_prune();
|
||||||
|
|
||||||
entry = l_queue_find(blacklist, match_addr, addr);
|
return l_queue_find(blacklist, match_addr_and_reason, &search) != NULL;
|
||||||
|
|
||||||
if (!entry)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
time_now = l_time_now();
|
|
||||||
|
|
||||||
ret = l_time_after(time_now, entry->expire_time) ? false : true;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void blacklist_remove_bss(const uint8_t *addr)
|
void blacklist_remove_bss(const uint8_t *addr, enum blacklist_reason reason)
|
||||||
{
|
{
|
||||||
struct blacklist_entry *entry;
|
struct blacklist_entry *entry;
|
||||||
|
struct blacklist_search search = {
|
||||||
|
.addr = addr,
|
||||||
|
.reason = reason
|
||||||
|
};
|
||||||
|
|
||||||
blacklist_prune();
|
blacklist_prune();
|
||||||
|
|
||||||
entry = l_queue_remove_if(blacklist, match_addr, addr);
|
entry = l_queue_remove_if(blacklist, match_addr_and_reason, &search);
|
||||||
|
|
||||||
if (!entry)
|
if (!entry)
|
||||||
return;
|
return;
|
||||||
@ -162,19 +214,39 @@ static int blacklist_init(void)
|
|||||||
blacklist_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
|
blacklist_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
|
||||||
|
|
||||||
/* For easier user configuration the timeout values are in seconds */
|
/* For easier user configuration the timeout values are in seconds */
|
||||||
blacklist_initial_timeout *= 1000000;
|
blacklist_initial_timeout *= L_USEC_PER_SEC;
|
||||||
|
|
||||||
|
if (!l_settings_get_uint64(config, "Blacklist",
|
||||||
|
"InitialRoamRequestedTimeout",
|
||||||
|
&blacklist_roam_initial_timeout))
|
||||||
|
blacklist_roam_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
|
||||||
|
|
||||||
|
/* For easier user configuration the timeout values are in seconds */
|
||||||
|
blacklist_roam_initial_timeout *= L_USEC_PER_SEC;
|
||||||
|
|
||||||
if (!l_settings_get_uint64(config, "Blacklist",
|
if (!l_settings_get_uint64(config, "Blacklist",
|
||||||
"Multiplier",
|
"Multiplier",
|
||||||
&blacklist_multiplier))
|
&blacklist_multiplier))
|
||||||
blacklist_multiplier = BLACKLIST_DEFAULT_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",
|
if (!l_settings_get_uint64(config, "Blacklist",
|
||||||
"MaximumTimeout",
|
"MaximumTimeout",
|
||||||
&blacklist_max_timeout))
|
&blacklist_max_timeout))
|
||||||
blacklist_max_timeout = BLACKLIST_DEFAULT_MAX_TIMEOUT;
|
blacklist_max_timeout = BLACKLIST_DEFAULT_MAX_TIMEOUT;
|
||||||
|
|
||||||
blacklist_max_timeout *= 1000000;
|
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 = l_queue_new();
|
blacklist = l_queue_new();
|
||||||
|
|
||||||
|
@ -20,6 +20,21 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void blacklist_add_bss(const uint8_t *addr);
|
enum blacklist_reason {
|
||||||
bool blacklist_contains_bss(const uint8_t *addr);
|
/*
|
||||||
void blacklist_remove_bss(const uint8_t *addr);
|
* When a BSS is blacklisted using this reason IWD will refuse to
|
||||||
|
* connect to it via autoconnect
|
||||||
|
*/
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED,
|
||||||
|
/*
|
||||||
|
* This type of blacklist is added when a BSS requests IWD roams
|
||||||
|
* elsewhere. This is to aid in preventing IWD from roaming/connecting
|
||||||
|
* back to that BSS in the future unless there are no other "good"
|
||||||
|
* candidates to connect to.
|
||||||
|
*/
|
||||||
|
BLACKLIST_REASON_ROAM_REQUESTED,
|
||||||
|
};
|
||||||
|
|
||||||
|
void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason);
|
||||||
|
bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason);
|
||||||
|
void blacklist_remove_bss(const uint8_t *addr, enum blacklist_reason reason);
|
||||||
|
@ -1212,7 +1212,7 @@ struct l_ecc_point *crypto_derive_sae_pwe_from_pt_ecc(const uint8_t *mac1,
|
|||||||
struct l_ecc_point *pwe;
|
struct l_ecc_point *pwe;
|
||||||
|
|
||||||
if (!pt || !curve)
|
if (!pt || !curve)
|
||||||
return false;
|
return NULL;
|
||||||
|
|
||||||
hash = crypto_sae_hash_from_ecc_prime_len(CRYPTO_SAE_HASH_TO_ELEMENT,
|
hash = crypto_sae_hash_from_ecc_prime_len(CRYPTO_SAE_HASH_TO_ELEMENT,
|
||||||
l_ecc_curve_get_scalar_bytes(curve));
|
l_ecc_curve_get_scalar_bytes(curve));
|
||||||
|
@ -134,6 +134,12 @@ struct l_dbus_message *dbus_error_not_hidden(struct l_dbus_message *msg)
|
|||||||
"Not hidden");
|
"Not hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct l_dbus_message *dbus_error_permission_denied(struct l_dbus_message *msg)
|
||||||
|
{
|
||||||
|
return l_dbus_message_new_error(msg, IWD_SERVICE ".PermissionDenied",
|
||||||
|
"Permission Denied");
|
||||||
|
}
|
||||||
|
|
||||||
struct l_dbus_message *dbus_error_from_errno(int err,
|
struct l_dbus_message *dbus_error_from_errno(int err,
|
||||||
struct l_dbus_message *msg)
|
struct l_dbus_message *msg)
|
||||||
{
|
{
|
||||||
|
@ -82,6 +82,7 @@ struct l_dbus_message *dbus_error_service_set_overlap(
|
|||||||
struct l_dbus_message *dbus_error_already_provisioned(
|
struct l_dbus_message *dbus_error_already_provisioned(
|
||||||
struct l_dbus_message *msg);
|
struct l_dbus_message *msg);
|
||||||
struct l_dbus_message *dbus_error_not_hidden(struct l_dbus_message *msg);
|
struct l_dbus_message *dbus_error_not_hidden(struct l_dbus_message *msg);
|
||||||
|
struct l_dbus_message *dbus_error_permission_denied(struct l_dbus_message *msg);
|
||||||
|
|
||||||
struct l_dbus_message *dbus_error_from_errno(int err,
|
struct l_dbus_message *dbus_error_from_errno(int err,
|
||||||
struct l_dbus_message *msg);
|
struct l_dbus_message *msg);
|
||||||
|
@ -1166,21 +1166,34 @@ struct dpp_uri_info *dpp_parse_uri(const char *uri)
|
|||||||
|
|
||||||
switch (*pos) {
|
switch (*pos) {
|
||||||
case 'C':
|
case 'C':
|
||||||
|
if (L_WARN_ON(info->freqs))
|
||||||
|
goto free_info;
|
||||||
|
|
||||||
info->freqs = dpp_parse_class_and_channel(pos + 2, len);
|
info->freqs = dpp_parse_class_and_channel(pos + 2, len);
|
||||||
if (!info->freqs)
|
if (!info->freqs)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
break;
|
break;
|
||||||
case 'M':
|
case 'M':
|
||||||
|
if (L_WARN_ON(!l_memeqzero(info->mac,
|
||||||
|
sizeof(info->mac))))
|
||||||
|
goto free_info;
|
||||||
|
|
||||||
ret = dpp_parse_mac(pos + 2, len, info->mac);
|
ret = dpp_parse_mac(pos + 2, len, info->mac);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
break;
|
break;
|
||||||
case 'V':
|
case 'V':
|
||||||
|
if (L_WARN_ON(info->version != 0))
|
||||||
|
goto free_info;
|
||||||
|
|
||||||
ret = dpp_parse_version(pos + 2, len, &info->version);
|
ret = dpp_parse_version(pos + 2, len, &info->version);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
break;
|
break;
|
||||||
case 'K':
|
case 'K':
|
||||||
|
if (L_WARN_ON(info->boot_public))
|
||||||
|
goto free_info;
|
||||||
|
|
||||||
info->boot_public = dpp_parse_key(pos + 2, len);
|
info->boot_public = dpp_parse_key(pos + 2, len);
|
||||||
if (!info->boot_public)
|
if (!info->boot_public)
|
||||||
goto free_info;
|
goto free_info;
|
||||||
|
176
src/dpp.c
176
src/dpp.c
@ -66,7 +66,6 @@ static struct l_genl_family *nl80211;
|
|||||||
static uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
static uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||||
static struct l_queue *dpp_list;
|
static struct l_queue *dpp_list;
|
||||||
static uint32_t mlme_watch;
|
static uint32_t mlme_watch;
|
||||||
static uint32_t unicast_watch;
|
|
||||||
|
|
||||||
static uint8_t dpp_prefix[] = { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x1a, 0x01 };
|
static uint8_t dpp_prefix[] = { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x1a, 0x01 };
|
||||||
|
|
||||||
@ -552,6 +551,8 @@ static void dpp_reset(struct dpp_sm *dpp)
|
|||||||
|
|
||||||
dpp_property_changed_notify(dpp);
|
dpp_property_changed_notify(dpp);
|
||||||
|
|
||||||
|
frame_watch_group_remove(dpp->wdev_id, FRAME_GROUP_DPP);
|
||||||
|
|
||||||
dpp->interface = DPP_INTERFACE_UNBOUND;
|
dpp->interface = DPP_INTERFACE_UNBOUND;
|
||||||
|
|
||||||
if (station) {
|
if (station) {
|
||||||
@ -1840,7 +1841,7 @@ static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp)
|
|||||||
|
|
||||||
l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group);
|
l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group);
|
||||||
|
|
||||||
iov[0].iov_len = dpp_build_header(own_mac, broadcast,
|
iov[0].iov_len = dpp_build_header(own_mac, dpp->peer_addr,
|
||||||
DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST, hdr);
|
DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST, hdr);
|
||||||
iov[0].iov_base = hdr;
|
iov[0].iov_base = hdr;
|
||||||
|
|
||||||
@ -2000,7 +2001,7 @@ static void dpp_offchannel_timeout(int error, void *user_data)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* We have a pending agent request but it did not arrive in
|
* We have a pending agent request but it did not arrive in
|
||||||
* time, we cant assume the enrollee will be waiting around
|
* time, we can't assume the enrollee will be waiting around
|
||||||
* for our response so cancel the request and continue waiting
|
* for our response so cancel the request and continue waiting
|
||||||
* for another request
|
* for another request
|
||||||
*/
|
*/
|
||||||
@ -2721,7 +2722,7 @@ static void dpp_handle_pkex_exchange_response(struct dpp_sm *dpp,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (version && version != dpp->pkex_version) {
|
if (version && version != dpp->pkex_version) {
|
||||||
l_debug("PKEX version does not match, igoring");
|
l_debug("PKEX version does not match, ignoring");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2831,6 +2832,12 @@ static bool dpp_pkex_start_authentication(struct dpp_sm *dpp)
|
|||||||
|
|
||||||
dpp_property_changed_notify(dpp);
|
dpp_property_changed_notify(dpp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* No longer waiting for an arbitrary peer to respond, reduce the
|
||||||
|
* timeout now that we are proceeding to authentication
|
||||||
|
*/
|
||||||
|
dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT);
|
||||||
|
|
||||||
if (dpp->role == DPP_CAPABILITY_ENROLLEE) {
|
if (dpp->role == DPP_CAPABILITY_ENROLLEE) {
|
||||||
dpp->new_freq = dpp->current_freq;
|
dpp->new_freq = dpp->current_freq;
|
||||||
|
|
||||||
@ -3431,7 +3438,7 @@ static void dpp_handle_pkex_commit_reveal_request(struct dpp_sm *dpp,
|
|||||||
dpp->peer_boot_public = l_ecc_point_from_data(dpp->curve,
|
dpp->peer_boot_public = l_ecc_point_from_data(dpp->curve,
|
||||||
L_ECC_POINT_TYPE_FULL, key, key_len);
|
L_ECC_POINT_TYPE_FULL, key, key_len);
|
||||||
if (!dpp->peer_boot_public) {
|
if (!dpp->peer_boot_public) {
|
||||||
l_debug("peers boostrapping key did not validate");
|
l_debug("peers bootstrapping key did not validate");
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3469,10 +3476,11 @@ failed:
|
|||||||
dpp_reset(dpp);
|
dpp_reset(dpp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dpp_handle_frame(struct dpp_sm *dpp,
|
static void dpp_handle_frame(const struct mmpdu_header *frame,
|
||||||
const struct mmpdu_header *frame,
|
const void *body, size_t body_len,
|
||||||
const void *body, size_t body_len)
|
int rssi, void *user_data)
|
||||||
{
|
{
|
||||||
|
struct dpp_sm *dpp = user_data;
|
||||||
const uint8_t *ptr;
|
const uint8_t *ptr;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3590,7 +3598,7 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Only want to handle the no-ACK case. Re-transmitting an ACKed
|
* Only want to handle the no-ACK case. Re-transmitting an ACKed
|
||||||
* frame likely wont do any good, at least in the case of DPP.
|
* frame likely won't do any good, at least in the case of DPP.
|
||||||
*/
|
*/
|
||||||
if (!ack)
|
if (!ack)
|
||||||
goto retransmit;
|
goto retransmit;
|
||||||
@ -3632,99 +3640,6 @@ retransmit:
|
|||||||
dpp_frame_timeout, dpp, NULL);
|
dpp_frame_timeout, dpp, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dpp_unicast_notify(struct l_genl_msg *msg, void *user_data)
|
|
||||||
{
|
|
||||||
struct dpp_sm *dpp;
|
|
||||||
const uint64_t *wdev_id = NULL;
|
|
||||||
struct l_genl_attr attr;
|
|
||||||
uint16_t type, len, frame_len;
|
|
||||||
const void *data;
|
|
||||||
const struct mmpdu_header *mpdu = NULL;
|
|
||||||
const uint8_t *body;
|
|
||||||
size_t body_len;
|
|
||||||
|
|
||||||
if (l_genl_msg_get_command(msg) != NL80211_CMD_FRAME)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!l_genl_attr_init(&attr, msg))
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (l_genl_attr_next(&attr, &type, &len, &data)) {
|
|
||||||
switch (type) {
|
|
||||||
case NL80211_ATTR_WDEV:
|
|
||||||
if (len != 8)
|
|
||||||
break;
|
|
||||||
|
|
||||||
wdev_id = data;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NL80211_ATTR_FRAME:
|
|
||||||
mpdu = mpdu_validate(data, len);
|
|
||||||
if (!mpdu) {
|
|
||||||
l_warn("Frame didn't validate as MMPDU");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_len = len;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wdev_id) {
|
|
||||||
l_warn("Bad wdev attribute");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dpp = l_queue_find(dpp_list, match_wdev, wdev_id);
|
|
||||||
if (!dpp)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!mpdu) {
|
|
||||||
l_warn("Missing frame data");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
body = mmpdu_body(mpdu);
|
|
||||||
body_len = (const uint8_t *) mpdu + frame_len - body;
|
|
||||||
|
|
||||||
if (body_len < sizeof(dpp_prefix) ||
|
|
||||||
memcmp(body, dpp_prefix, sizeof(dpp_prefix)) != 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dpp_handle_frame(dpp, mpdu, body, body_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dpp_frame_watch_cb(struct l_genl_msg *msg, void *user_data)
|
|
||||||
{
|
|
||||||
if (l_genl_msg_get_error(msg) < 0)
|
|
||||||
l_error("Could not register frame watch type %04x: %i",
|
|
||||||
L_PTR_TO_UINT(user_data), l_genl_msg_get_error(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Special case the frame watch which includes the presence frames since they
|
|
||||||
* require multicast support. This is only supported by ath9k, so adding
|
|
||||||
* general support to frame-xchg isn't desireable.
|
|
||||||
*/
|
|
||||||
static void dpp_frame_watch(struct dpp_sm *dpp, uint16_t frame_type,
|
|
||||||
const uint8_t *prefix, size_t prefix_len)
|
|
||||||
{
|
|
||||||
struct l_genl_msg *msg;
|
|
||||||
|
|
||||||
msg = l_genl_msg_new_sized(NL80211_CMD_REGISTER_FRAME, 32 + prefix_len);
|
|
||||||
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &dpp->wdev_id);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_TYPE, 2, &frame_type);
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_MATCH,
|
|
||||||
prefix_len, prefix);
|
|
||||||
if (dpp->mcast_support)
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_RECEIVE_MULTICAST,
|
|
||||||
0, NULL);
|
|
||||||
|
|
||||||
l_genl_family_send(nl80211, msg, dpp_frame_watch_cb,
|
|
||||||
L_UINT_TO_PTR(frame_type), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dpp_start_enrollee(struct dpp_sm *dpp);
|
static void dpp_start_enrollee(struct dpp_sm *dpp);
|
||||||
static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp);
|
static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp);
|
||||||
|
|
||||||
@ -3814,8 +3729,6 @@ static void dpp_create(struct netdev *netdev)
|
|||||||
{
|
{
|
||||||
struct l_dbus *dbus = dbus_get_bus();
|
struct l_dbus *dbus = dbus_get_bus();
|
||||||
struct dpp_sm *dpp = l_new(struct dpp_sm, 1);
|
struct dpp_sm *dpp = l_new(struct dpp_sm, 1);
|
||||||
uint8_t dpp_conf_response_prefix[] = { 0x04, 0x0b };
|
|
||||||
uint8_t dpp_conf_request_prefix[] = { 0x04, 0x0a };
|
|
||||||
uint64_t wdev_id = netdev_get_wdev_id(netdev);
|
uint64_t wdev_id = netdev_get_wdev_id(netdev);
|
||||||
|
|
||||||
dpp->netdev = netdev;
|
dpp->netdev = netdev;
|
||||||
@ -3826,9 +3739,8 @@ static void dpp_create(struct netdev *netdev)
|
|||||||
dpp->key_len = l_ecc_curve_get_scalar_bytes(dpp->curve);
|
dpp->key_len = l_ecc_curve_get_scalar_bytes(dpp->curve);
|
||||||
dpp->nonce_len = dpp_nonce_len_from_key_len(dpp->key_len);
|
dpp->nonce_len = dpp_nonce_len_from_key_len(dpp->key_len);
|
||||||
dpp->max_roc = wiphy_get_max_roc_duration(wiphy_find_by_wdev(wdev_id));
|
dpp->max_roc = wiphy_get_max_roc_duration(wiphy_find_by_wdev(wdev_id));
|
||||||
dpp->mcast_support = wiphy_has_ext_feature(
|
dpp->mcast_support = wiphy_supports_multicast_rx(
|
||||||
wiphy_find_by_wdev(dpp->wdev_id),
|
wiphy_find_by_wdev(dpp->wdev_id));
|
||||||
NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS);
|
|
||||||
|
|
||||||
l_ecdh_generate_key_pair(dpp->curve, &dpp->boot_private,
|
l_ecdh_generate_key_pair(dpp->curve, &dpp->boot_private,
|
||||||
&dpp->boot_public);
|
&dpp->boot_public);
|
||||||
@ -3851,17 +3763,6 @@ static void dpp_create(struct netdev *netdev)
|
|||||||
*/
|
*/
|
||||||
dpp->refcount = 2;
|
dpp->refcount = 2;
|
||||||
|
|
||||||
dpp_frame_watch(dpp, 0x00d0, dpp_prefix, sizeof(dpp_prefix));
|
|
||||||
|
|
||||||
frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
|
|
||||||
dpp_conf_response_prefix,
|
|
||||||
sizeof(dpp_conf_response_prefix),
|
|
||||||
dpp_handle_config_response_frame, dpp, NULL);
|
|
||||||
frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
|
|
||||||
dpp_conf_request_prefix,
|
|
||||||
sizeof(dpp_conf_request_prefix),
|
|
||||||
dpp_handle_config_request_frame, dpp, NULL);
|
|
||||||
|
|
||||||
dpp->known_network_watch = known_networks_watch_add(
|
dpp->known_network_watch = known_networks_watch_add(
|
||||||
dpp_known_network_watch, dpp, NULL);
|
dpp_known_network_watch, dpp, NULL);
|
||||||
|
|
||||||
@ -4006,6 +3907,26 @@ done:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dpp_add_frame_watches(struct dpp_sm *dpp, bool multicast_rx)
|
||||||
|
{
|
||||||
|
uint8_t dpp_conf_response_prefix[] = { 0x04, 0x0b };
|
||||||
|
uint8_t dpp_conf_request_prefix[] = { 0x04, 0x0a };
|
||||||
|
|
||||||
|
frame_watch_add(dpp->wdev_id, FRAME_GROUP_DPP, 0x00d0,
|
||||||
|
dpp_prefix, sizeof(dpp_prefix), multicast_rx,
|
||||||
|
dpp_handle_frame, dpp, NULL);
|
||||||
|
|
||||||
|
frame_watch_add(dpp->wdev_id, FRAME_GROUP_DPP, 0x00d0,
|
||||||
|
dpp_conf_response_prefix,
|
||||||
|
sizeof(dpp_conf_response_prefix), false,
|
||||||
|
dpp_handle_config_response_frame, dpp, NULL);
|
||||||
|
|
||||||
|
frame_watch_add(dpp->wdev_id, FRAME_GROUP_DPP, 0x00d0,
|
||||||
|
dpp_conf_request_prefix,
|
||||||
|
sizeof(dpp_conf_request_prefix), false,
|
||||||
|
dpp_handle_config_request_frame, dpp, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static void dpp_start_enrollee(struct dpp_sm *dpp)
|
static void dpp_start_enrollee(struct dpp_sm *dpp)
|
||||||
{
|
{
|
||||||
uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ);
|
uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ);
|
||||||
@ -4045,6 +3966,8 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
|
|||||||
dpp->role = DPP_CAPABILITY_ENROLLEE;
|
dpp->role = DPP_CAPABILITY_ENROLLEE;
|
||||||
dpp->interface = DPP_INTERFACE_DPP;
|
dpp->interface = DPP_INTERFACE_DPP;
|
||||||
|
|
||||||
|
dpp_add_frame_watches(dpp, false);
|
||||||
|
|
||||||
ret = dpp_try_disconnect_station(dpp, &wait_for_disconnect);
|
ret = dpp_try_disconnect_station(dpp, &wait_for_disconnect);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dpp_reset(dpp);
|
dpp_reset(dpp);
|
||||||
@ -4182,6 +4105,8 @@ static struct l_dbus_message *dpp_start_configurator_common(
|
|||||||
} else
|
} else
|
||||||
dpp->current_freq = bss->frequency;
|
dpp->current_freq = bss->frequency;
|
||||||
|
|
||||||
|
dpp_add_frame_watches(dpp, responder && dpp->mcast_support);
|
||||||
|
|
||||||
dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2,
|
dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2,
|
||||||
netdev_get_address(dpp->netdev),
|
netdev_get_address(dpp->netdev),
|
||||||
&bss->frequency, 1, NULL, NULL);
|
&bss->frequency, 1, NULL, NULL);
|
||||||
@ -4283,10 +4208,11 @@ static uint32_t *dpp_default_freqs(struct dpp_sm *dpp, size_t *out_len)
|
|||||||
|
|
||||||
static void __dpp_pkex_start_enrollee(struct dpp_sm *dpp)
|
static void __dpp_pkex_start_enrollee(struct dpp_sm *dpp)
|
||||||
{
|
{
|
||||||
|
uint32_t timeout = minsize(DPP_PKEX_PROTO_TIMEOUT,
|
||||||
|
dpp->freqs_len * DPP_PKEX_PROTO_PER_FREQ_TIMEOUT);
|
||||||
dpp->current_freq = dpp->freqs[0];
|
dpp->current_freq = dpp->freqs[0];
|
||||||
|
|
||||||
dpp_reset_protocol_timer(dpp,
|
dpp_reset_protocol_timer(dpp, timeout);
|
||||||
dpp->freqs_len * DPP_PKEX_PROTO_PER_FREQ_TIMEOUT);
|
|
||||||
|
|
||||||
l_debug("PKEX start enrollee (id=%s)", dpp->pkex_id ?: "unset");
|
l_debug("PKEX start enrollee (id=%s)", dpp->pkex_id ?: "unset");
|
||||||
|
|
||||||
@ -4510,6 +4436,8 @@ static struct l_dbus_message *dpp_dbus_pkex_start_enrollee(struct l_dbus *dbus,
|
|||||||
dpp->state = DPP_STATE_PKEX_EXCHANGE;
|
dpp->state = DPP_STATE_PKEX_EXCHANGE;
|
||||||
dpp->interface = DPP_INTERFACE_PKEX;
|
dpp->interface = DPP_INTERFACE_PKEX;
|
||||||
|
|
||||||
|
dpp_add_frame_watches(dpp, false);
|
||||||
|
|
||||||
ret = dpp_try_disconnect_station(dpp, &wait_for_disconnect);
|
ret = dpp_try_disconnect_station(dpp, &wait_for_disconnect);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dpp_reset(dpp);
|
dpp_reset(dpp);
|
||||||
@ -4606,6 +4534,7 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp,
|
|||||||
dpp->config = dpp_configuration_new(network_get_settings(network),
|
dpp->config = dpp_configuration_new(network_get_settings(network),
|
||||||
network_get_ssid(network),
|
network_get_ssid(network),
|
||||||
hs->akm_suite);
|
hs->akm_suite);
|
||||||
|
dpp_add_frame_watches(dpp, dpp->mcast_support);
|
||||||
|
|
||||||
dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT);
|
dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT);
|
||||||
dpp_property_changed_notify(dpp);
|
dpp_property_changed_notify(dpp);
|
||||||
@ -4734,11 +4663,6 @@ static int dpp_init(void)
|
|||||||
mlme_watch = l_genl_family_register(nl80211, "mlme", dpp_mlme_notify,
|
mlme_watch = l_genl_family_register(nl80211, "mlme", dpp_mlme_notify,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
|
|
||||||
unicast_watch = l_genl_add_unicast_watch(iwd_get_genl(),
|
|
||||||
NL80211_GENL_NAME,
|
|
||||||
dpp_unicast_notify,
|
|
||||||
NULL, NULL);
|
|
||||||
|
|
||||||
dpp_list = l_queue_new();
|
dpp_list = l_queue_new();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -4753,8 +4677,6 @@ static void dpp_exit(void)
|
|||||||
|
|
||||||
netdev_watch_remove(netdev_watch);
|
netdev_watch_remove(netdev_watch);
|
||||||
|
|
||||||
l_genl_remove_unicast_watch(iwd_get_genl(), unicast_watch);
|
|
||||||
|
|
||||||
l_genl_family_unregister(nl80211, mlme_watch);
|
l_genl_family_unregister(nl80211, mlme_watch);
|
||||||
mlme_watch = 0;
|
mlme_watch = 0;
|
||||||
|
|
||||||
|
@ -544,7 +544,8 @@ static bool eap_mschapv2_load_settings(struct eap_state *eap,
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
free(state);
|
l_free(state->user);
|
||||||
|
l_free(state);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ static void __eap_tls_common_state_reset(struct eap_state *eap)
|
|||||||
*
|
*
|
||||||
* Drop the cache even if we have no indication that the
|
* Drop the cache even if we have no indication that the
|
||||||
* method failed but it just didn't succeed, to handle cases like
|
* method failed but it just didn't succeed, to handle cases like
|
||||||
* the server getting stuck and a timout occuring at a higher
|
* the server getting stuck and a timeout occurring at a higher
|
||||||
* layer. The risk is that we may occasionally flush the session
|
* layer. The risk is that we may occasionally flush the session
|
||||||
* data when there was only a momentary radio issue, invalid
|
* data when there was only a momentary radio issue, invalid
|
||||||
* phase2 credentials or decision to abort. Those are not hot
|
* phase2 credentials or decision to abort. Those are not hot
|
||||||
|
@ -974,7 +974,7 @@ static void __eap_method_enable(struct eap_method_desc *start,
|
|||||||
|
|
||||||
l_debug("");
|
l_debug("");
|
||||||
|
|
||||||
if (start == NULL || stop == NULL)
|
if (!start || !stop)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (desc = start; desc < stop; desc++) {
|
for (desc = start; desc < stop; desc++) {
|
||||||
@ -992,7 +992,7 @@ static void __eap_method_disable(struct eap_method_desc *start,
|
|||||||
|
|
||||||
l_debug("");
|
l_debug("");
|
||||||
|
|
||||||
if (start == NULL || stop == NULL)
|
if (!start || !stop)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (desc = start; desc < stop; desc++) {
|
for (desc = start; desc < stop; desc++) {
|
||||||
|
@ -342,8 +342,7 @@ static bool frame_watch_group_io_read(struct l_io *io, void *user_data)
|
|||||||
|
|
||||||
nlmsg_len = bytes_read;
|
nlmsg_len = bytes_read;
|
||||||
|
|
||||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
|
||||||
struct nl_pktinfo pktinfo;
|
struct nl_pktinfo pktinfo;
|
||||||
|
|
||||||
if (cmsg->cmsg_level != SOL_NETLINK)
|
if (cmsg->cmsg_level != SOL_NETLINK)
|
||||||
@ -575,6 +574,7 @@ drop:
|
|||||||
|
|
||||||
bool frame_watch_add(uint64_t wdev_id, uint32_t group_id, uint16_t frame_type,
|
bool frame_watch_add(uint64_t wdev_id, uint32_t group_id, uint16_t frame_type,
|
||||||
const uint8_t *prefix, size_t prefix_len,
|
const uint8_t *prefix, size_t prefix_len,
|
||||||
|
bool multicast_rx,
|
||||||
frame_watch_cb_t handler, void *user_data,
|
frame_watch_cb_t handler, void *user_data,
|
||||||
frame_xchg_destroy_func_t destroy)
|
frame_xchg_destroy_func_t destroy)
|
||||||
{
|
{
|
||||||
@ -614,6 +614,10 @@ bool frame_watch_add(uint64_t wdev_id, uint32_t group_id, uint16_t frame_type,
|
|||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_MATCH,
|
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_MATCH,
|
||||||
prefix_len, prefix);
|
prefix_len, prefix);
|
||||||
|
|
||||||
|
if (multicast_rx)
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_RECEIVE_MULTICAST,
|
||||||
|
0, NULL);
|
||||||
|
|
||||||
if (group->id == 0)
|
if (group->id == 0)
|
||||||
l_genl_family_send(nl80211, msg, frame_watch_register_cb,
|
l_genl_family_send(nl80211, msg, frame_watch_register_cb,
|
||||||
L_UINT_TO_PTR(frame_type), NULL);
|
L_UINT_TO_PTR(frame_type), NULL);
|
||||||
@ -1194,7 +1198,7 @@ uint32_t frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
|
|||||||
watch->prefix = prefix;
|
watch->prefix = prefix;
|
||||||
watch->cb = va_arg(resp_args, void *);
|
watch->cb = va_arg(resp_args, void *);
|
||||||
frame_watch_add(wdev_id, group_id, prefix->frame_type,
|
frame_watch_add(wdev_id, group_id, prefix->frame_type,
|
||||||
prefix->data, prefix->len,
|
prefix->data, prefix->len, false,
|
||||||
frame_xchg_resp_cb, fx, NULL);
|
frame_xchg_resp_cb, fx, NULL);
|
||||||
|
|
||||||
if (!fx->rx_watches)
|
if (!fx->rx_watches)
|
||||||
|
@ -41,10 +41,12 @@ enum frame_xchg_group {
|
|||||||
FRAME_GROUP_DEFAULT = 0,
|
FRAME_GROUP_DEFAULT = 0,
|
||||||
FRAME_GROUP_P2P_LISTEN,
|
FRAME_GROUP_P2P_LISTEN,
|
||||||
FRAME_GROUP_P2P_CONNECT,
|
FRAME_GROUP_P2P_CONNECT,
|
||||||
|
FRAME_GROUP_DPP,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool frame_watch_add(uint64_t wdev_id, uint32_t group, uint16_t frame_type,
|
bool frame_watch_add(uint64_t wdev_id, uint32_t group, uint16_t frame_type,
|
||||||
const uint8_t *prefix, size_t prefix_len,
|
const uint8_t *prefix, size_t prefix_len,
|
||||||
|
bool multicast_rx,
|
||||||
frame_watch_cb_t handler, void *user_data,
|
frame_watch_cb_t handler, void *user_data,
|
||||||
frame_xchg_destroy_func_t destroy);
|
frame_xchg_destroy_func_t destroy);
|
||||||
bool frame_watch_group_remove(uint64_t wdev_id, uint32_t group);
|
bool frame_watch_group_remove(uint64_t wdev_id, uint32_t group);
|
||||||
|
107
src/handshake.c
107
src/handshake.c
@ -43,6 +43,7 @@
|
|||||||
#include "src/handshake.h"
|
#include "src/handshake.h"
|
||||||
#include "src/erp.h"
|
#include "src/erp.h"
|
||||||
#include "src/band.h"
|
#include "src/band.h"
|
||||||
|
#include "src/pmksa.h"
|
||||||
|
|
||||||
static inline unsigned int n_ecc_groups(void)
|
static inline unsigned int n_ecc_groups(void)
|
||||||
{
|
{
|
||||||
@ -103,7 +104,14 @@ void __handshake_set_install_ext_tk_func(handshake_install_ext_tk_func_t func)
|
|||||||
install_ext_tk = func;
|
install_ext_tk = func;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handshake_state_free(struct handshake_state *s)
|
struct handshake_state *handshake_state_ref(struct handshake_state *s)
|
||||||
|
{
|
||||||
|
__sync_fetch_and_add(&s->refcount, 1);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handshake_state_unref(struct handshake_state *s)
|
||||||
{
|
{
|
||||||
__typeof__(s->free) destroy;
|
__typeof__(s->free) destroy;
|
||||||
|
|
||||||
@ -117,6 +125,9 @@ void handshake_state_free(struct handshake_state *s)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (__sync_sub_and_fetch(&s->refcount, 1))
|
||||||
|
return;
|
||||||
|
|
||||||
l_free(s->authenticator_ie);
|
l_free(s->authenticator_ie);
|
||||||
l_free(s->supplicant_ie);
|
l_free(s->supplicant_ie);
|
||||||
l_free(s->authenticator_rsnxe);
|
l_free(s->authenticator_rsnxe);
|
||||||
@ -128,6 +139,9 @@ void handshake_state_free(struct handshake_state *s)
|
|||||||
l_free(s->fils_ip_resp_ie);
|
l_free(s->fils_ip_resp_ie);
|
||||||
l_free(s->vendor_ies);
|
l_free(s->vendor_ies);
|
||||||
|
|
||||||
|
if (s->have_pmksa)
|
||||||
|
l_free(s->pmksa);
|
||||||
|
|
||||||
if (s->erp_cache)
|
if (s->erp_cache)
|
||||||
erp_cache_put(s->erp_cache);
|
erp_cache_put(s->erp_cache);
|
||||||
|
|
||||||
@ -691,6 +705,11 @@ void handshake_state_install_ptk(struct handshake_state *s)
|
|||||||
{
|
{
|
||||||
s->ptk_complete = true;
|
s->ptk_complete = true;
|
||||||
|
|
||||||
|
if (!s->have_pmksa && IE_AKM_IS_SAE(s->akm_suite)) {
|
||||||
|
l_debug("Adding PMKSA expiration");
|
||||||
|
s->expiration = l_time_now() + pmksa_lifetime();
|
||||||
|
}
|
||||||
|
|
||||||
if (install_tk) {
|
if (install_tk) {
|
||||||
uint32_t cipher = ie_rsn_cipher_suite_to_cipher(
|
uint32_t cipher = ie_rsn_cipher_suite_to_cipher(
|
||||||
s->pairwise_cipher);
|
s->pairwise_cipher);
|
||||||
@ -1193,3 +1212,89 @@ done:
|
|||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool handshake_state_set_pmksa(struct handshake_state *s,
|
||||||
|
struct pmksa *pmksa)
|
||||||
|
{
|
||||||
|
/* checks for both expiration || pmksa being set */
|
||||||
|
if (s->expiration)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
s->pmksa = pmksa;
|
||||||
|
s->have_pmksa = true;
|
||||||
|
|
||||||
|
handshake_state_set_pmkid(s, pmksa->pmkid);
|
||||||
|
handshake_state_set_pmk(s, pmksa->pmk, pmksa->pmk_len);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pmksa *handshake_state_steal_pmksa(struct handshake_state *s)
|
||||||
|
{
|
||||||
|
struct pmksa *pmksa;
|
||||||
|
uint64_t now = l_time_now();
|
||||||
|
|
||||||
|
if (s->have_pmksa) {
|
||||||
|
pmksa = l_steal_ptr(s->pmksa);
|
||||||
|
s->have_pmksa = false;
|
||||||
|
|
||||||
|
if (l_time_after(now, pmksa->expiration)) {
|
||||||
|
pmksa_cache_free(pmksa);
|
||||||
|
pmksa = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pmksa;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->expiration && l_time_after(now, s->expiration)) {
|
||||||
|
s->expiration = 0;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s->have_pmkid)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pmksa = l_new(struct pmksa, 1);
|
||||||
|
pmksa->expiration = s->expiration;
|
||||||
|
memcpy(pmksa->spa, s->spa, sizeof(s->spa));
|
||||||
|
memcpy(pmksa->aa, s->aa, sizeof(s->aa));
|
||||||
|
memcpy(pmksa->ssid, s->ssid, s->ssid_len);
|
||||||
|
pmksa->ssid_len = s->ssid_len;
|
||||||
|
pmksa->akm = s->akm_suite;
|
||||||
|
memcpy(pmksa->pmkid, s->pmkid, sizeof(s->pmkid));
|
||||||
|
pmksa->pmk_len = s->pmk_len;
|
||||||
|
memcpy(pmksa->pmk, s->pmk, s->pmk_len);
|
||||||
|
|
||||||
|
return pmksa;
|
||||||
|
}
|
||||||
|
|
||||||
|
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("Caching PMKSA for "MAC, MAC_STR(s->aa));
|
||||||
|
|
||||||
|
if (L_WARN_ON(pmksa_cache_put(pmksa) < 0))
|
||||||
|
pmksa_cache_free(pmksa);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handshake_state_remove_pmksa(struct handshake_state *s)
|
||||||
|
{
|
||||||
|
struct pmksa *pmksa;
|
||||||
|
|
||||||
|
if (!s->have_pmksa)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pmksa = handshake_state_steal_pmksa(s);
|
||||||
|
if (!pmksa)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
pmksa_cache_free(pmksa);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
struct handshake_state;
|
struct handshake_state;
|
||||||
enum crypto_cipher;
|
enum crypto_cipher;
|
||||||
struct eapol_frame;
|
struct eapol_frame;
|
||||||
|
struct pmksa;
|
||||||
|
|
||||||
enum handshake_kde {
|
enum handshake_kde {
|
||||||
/* 802.11-2020 Table 12-9 in section 12.7.2 */
|
/* 802.11-2020 Table 12-9 in section 12.7.2 */
|
||||||
@ -141,6 +142,11 @@ struct handshake_state {
|
|||||||
bool supplicant_ocvc : 1;
|
bool supplicant_ocvc : 1;
|
||||||
bool ext_key_id_capable : 1;
|
bool ext_key_id_capable : 1;
|
||||||
bool force_default_ecc_group : 1;
|
bool force_default_ecc_group : 1;
|
||||||
|
bool have_pmksa : 1;
|
||||||
|
union {
|
||||||
|
struct pmksa *pmksa;
|
||||||
|
uint64_t expiration;
|
||||||
|
};
|
||||||
uint8_t ssid[SSID_MAX_SIZE];
|
uint8_t ssid[SSID_MAX_SIZE];
|
||||||
size_t ssid_len;
|
size_t ssid_len;
|
||||||
char *passphrase;
|
char *passphrase;
|
||||||
@ -170,6 +176,8 @@ struct handshake_state {
|
|||||||
bool in_event;
|
bool in_event;
|
||||||
|
|
||||||
handshake_event_func_t event_func;
|
handshake_event_func_t event_func;
|
||||||
|
|
||||||
|
int refcount;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HSID(x) UNIQUE_ID(handshake_, x)
|
#define HSID(x) UNIQUE_ID(handshake_, x)
|
||||||
@ -186,7 +194,7 @@ struct handshake_state {
|
|||||||
##__VA_ARGS__); \
|
##__VA_ARGS__); \
|
||||||
\
|
\
|
||||||
if (!HSID(hs)->in_event) { \
|
if (!HSID(hs)->in_event) { \
|
||||||
handshake_state_free(HSID(hs)); \
|
handshake_state_unref(HSID(hs)); \
|
||||||
HSID(freed) = true; \
|
HSID(freed) = true; \
|
||||||
} else \
|
} else \
|
||||||
HSID(hs)->in_event = false; \
|
HSID(hs)->in_event = false; \
|
||||||
@ -194,7 +202,8 @@ struct handshake_state {
|
|||||||
HSID(freed); \
|
HSID(freed); \
|
||||||
})
|
})
|
||||||
|
|
||||||
void handshake_state_free(struct handshake_state *s);
|
struct handshake_state *handshake_state_ref(struct handshake_state *s);
|
||||||
|
void handshake_state_unref(struct handshake_state *s);
|
||||||
|
|
||||||
void handshake_state_set_supplicant_address(struct handshake_state *s,
|
void handshake_state_set_supplicant_address(struct handshake_state *s,
|
||||||
const uint8_t *spa);
|
const uint8_t *spa);
|
||||||
@ -299,6 +308,10 @@ void handshake_state_set_chandef(struct handshake_state *s,
|
|||||||
int handshake_state_verify_oci(struct handshake_state *s, const uint8_t *oci,
|
int handshake_state_verify_oci(struct handshake_state *s, const uint8_t *oci,
|
||||||
size_t oci_len);
|
size_t oci_len);
|
||||||
|
|
||||||
|
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(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 *scan_ie, bool is_wpa);
|
||||||
|
|
||||||
@ -316,4 +329,4 @@ void handshake_util_build_gtk_kde(enum crypto_cipher cipher, const uint8_t *key,
|
|||||||
void handshake_util_build_igtk_kde(enum crypto_cipher cipher, const uint8_t *key,
|
void handshake_util_build_igtk_kde(enum crypto_cipher cipher, const uint8_t *key,
|
||||||
unsigned int key_index, uint8_t *to);
|
unsigned int key_index, uint8_t *to);
|
||||||
|
|
||||||
DEFINE_CLEANUP_FUNC(handshake_state_free);
|
DEFINE_CLEANUP_FUNC(handshake_state_unref);
|
||||||
|
17
src/ie.h
17
src/ie.h
@ -598,6 +598,23 @@ static inline const unsigned char *ie_tlv_iter_get_data(
|
|||||||
return iter->data;
|
return iter->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool ie_tlv_iter_data_eq(struct ie_tlv_iter *a,
|
||||||
|
struct ie_tlv_iter *b)
|
||||||
|
{
|
||||||
|
if (a == b)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (a == NULL || b == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ie_tlv_iter_get_length(a) != ie_tlv_iter_get_length(b))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return memcmp(ie_tlv_iter_get_data(a),
|
||||||
|
ie_tlv_iter_get_data(b),
|
||||||
|
ie_tlv_iter_get_length(a)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
void *ie_tlv_extract_wsc_payload(const uint8_t *ies, size_t len,
|
void *ie_tlv_extract_wsc_payload(const uint8_t *ies, size_t len,
|
||||||
ssize_t *out_len);
|
ssize_t *out_len);
|
||||||
void *ie_tlv_encapsulate_wsc_payload(const uint8_t *data, size_t len,
|
void *ie_tlv_encapsulate_wsc_payload(const uint8_t *data, size_t len,
|
||||||
|
@ -133,6 +133,22 @@ The group ``[General]`` contains general settings.
|
|||||||
This value can be used to control how aggressively **iwd** roams when
|
This value can be used to control how aggressively **iwd** roams when
|
||||||
connected to a 5GHz access point.
|
connected to a 5GHz access point.
|
||||||
|
|
||||||
|
* - CriticalRoamThreshold
|
||||||
|
- Value: rssi dBm value, from -100 to -1, default: **-80**
|
||||||
|
|
||||||
|
The threshold (for 2.4GHz) at which IWD will roam regardless of the
|
||||||
|
affinity set to the current BSS. If the connected BSS has affinity
|
||||||
|
(set in Station's Affinities list) the roam threshold will be lowed to
|
||||||
|
this value and IWD will not attempt to roam (or roam scan) until either
|
||||||
|
the affinity is cleared, or the signal drops below this threshold.
|
||||||
|
|
||||||
|
|
||||||
|
* - CriticalRoamThreshold5G
|
||||||
|
- Value: rssi dBm value, from -100 to -1, default: **-82**
|
||||||
|
|
||||||
|
This has the same effect as ``CriticalRoamThreshold``, but for the 5GHz
|
||||||
|
band.
|
||||||
|
|
||||||
* - RoamRetryInterval
|
* - RoamRetryInterval
|
||||||
- Value: unsigned int value in seconds (default: **60**)
|
- Value: unsigned int value in seconds (default: **60**)
|
||||||
|
|
||||||
@ -209,6 +225,11 @@ The group ``[General]`` contains general settings.
|
|||||||
request is just a 'hint' and ultimately left up to the kernel to set the
|
request is just a 'hint' and ultimately left up to the kernel to set the
|
||||||
country.
|
country.
|
||||||
|
|
||||||
|
* - DisablePMKSA
|
||||||
|
- Value: **false**, true
|
||||||
|
|
||||||
|
Disable PMKSA support in IWD
|
||||||
|
|
||||||
Network
|
Network
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -269,9 +290,17 @@ control how long a misbehaved BSS spends on the blacklist.
|
|||||||
* - InitialTimeout
|
* - InitialTimeout
|
||||||
- Values: uint64 value in seconds (default: **60**)
|
- Values: uint64 value in seconds (default: **60**)
|
||||||
|
|
||||||
The initial time that a BSS spends on the blacklist.
|
The initial time that a BSS spends on the blacklist. Setting this to zero
|
||||||
|
will disable blacklisting functionality in IWD.
|
||||||
|
* - InitialRoamRequestedTimeout
|
||||||
|
- Values: uint64 value in seconds (default: **30**)
|
||||||
|
|
||||||
|
The initial time that a BSS will be marked after a BSS requests a roam.
|
||||||
|
This is to aid in avoiding roaming back to BSS's which are likely
|
||||||
|
overloaded. Setting this to zero will disabled this form of blacklisting.
|
||||||
* - Multiplier
|
* - Multiplier
|
||||||
- Values: unsigned int value in seconds (default: **30**)
|
- Values: unsigned int value greater than zero, in seconds
|
||||||
|
(default: **30**)
|
||||||
|
|
||||||
If the BSS was blacklisted previously and another connection attempt
|
If the BSS was blacklisted previously and another connection attempt
|
||||||
has failed after the initial timeout has expired, then the BSS blacklist
|
has failed after the initial timeout has expired, then the BSS blacklist
|
||||||
@ -325,6 +354,28 @@ autoconnect purposes.
|
|||||||
A value of 0.0 will disable the 6GHz band and prevent scanning or
|
A value of 0.0 will disable the 6GHz band and prevent scanning or
|
||||||
connecting on those frequencies.
|
connecting on those frequencies.
|
||||||
|
|
||||||
|
* - HighUtilizationThreshold
|
||||||
|
- Values: unsigned integer value 0 - 255 (default: **0**, disabled)
|
||||||
|
|
||||||
|
**Warning: This is an experimental feature**
|
||||||
|
|
||||||
|
The BSS utilization threshold at which a negative rank factor begins to
|
||||||
|
be applied to the BSS. As the load increases for a BSS the ranking factor
|
||||||
|
decays exponentially, meaning the ranking factor will decrease
|
||||||
|
exponentially. Setting this can have very drastic effects on the BSS rank
|
||||||
|
if its utilization is high, use with care.
|
||||||
|
|
||||||
|
* - HighStationCountThreshold
|
||||||
|
- Values: unsigned integer value 0 - 255 (default: **0**, disabled)
|
||||||
|
|
||||||
|
**Warning: This is an experimental feature**
|
||||||
|
|
||||||
|
The BSS station count threshold at which a negative rank factor begins to
|
||||||
|
be applied to the BSS. As the station count increases for a BSS the
|
||||||
|
ranking factor decays exponentially, meaning the ranking factor will
|
||||||
|
decrease exponentially. Setting this can have very drastic effects on the
|
||||||
|
BSS rank if its station count is high, use with care.
|
||||||
|
|
||||||
Scan
|
Scan
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -416,6 +467,20 @@ are buggy or just don't behave similar enough to the majority of other drivers.
|
|||||||
|
|
||||||
If a driver in user matches one in this list power save will be disabled.
|
If a driver in user matches one in this list power save will be disabled.
|
||||||
|
|
||||||
|
* - MulticastRxDisable
|
||||||
|
- Values: comma-separated list of drivers or glob matches
|
||||||
|
|
||||||
|
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
|
SEE ALSO
|
||||||
========
|
========
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ connect to that network.
|
|||||||
* - PasswordIdentifier
|
* - PasswordIdentifier
|
||||||
- string
|
- string
|
||||||
|
|
||||||
An identifer string to be used with the passphrase. This is used for
|
An identifier string to be used with the passphrase. This is used for
|
||||||
WPA3-Personal (SAE) networks if the security has enabled password
|
WPA3-Personal (SAE) networks if the security has enabled password
|
||||||
identifiers for clients.
|
identifiers for clients.
|
||||||
* - PreSharedKey
|
* - PreSharedKey
|
||||||
|
@ -894,11 +894,11 @@ static int manager_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!l_settings_get_bool(config, "General",
|
if (!l_settings_get_bool(config, "General",
|
||||||
"UseDefaultInterface", &use_default)) {
|
"UseDefaultInterface", &use_default))
|
||||||
|
use_default = false;
|
||||||
|
else
|
||||||
l_warn("[General].UseDefaultInterface is deprecated, please "
|
l_warn("[General].UseDefaultInterface is deprecated, please "
|
||||||
"use [DriverQuirks].DefaultInterface instead");
|
"use [DriverQuirks].DefaultInterface instead");
|
||||||
use_default = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -398,9 +398,16 @@ static bool validate_mgmt_ies(const uint8_t *ies, size_t ies_len,
|
|||||||
|
|
||||||
memcpy(&clone, &iter, sizeof(clone));
|
memcpy(&clone, &iter, sizeof(clone));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some APs send completely identical duplicate IEs:
|
||||||
|
* Since these are harmless (and ignored by us) we're
|
||||||
|
* going to allow them here for interoperability.
|
||||||
|
*/
|
||||||
while (ie_tlv_iter_next(&clone)) {
|
while (ie_tlv_iter_next(&clone)) {
|
||||||
if (ie_tlv_iter_get_tag(&clone) != tag)
|
if (ie_tlv_iter_get_tag(&clone) != tag)
|
||||||
continue;
|
continue;
|
||||||
|
else if (ie_tlv_iter_data_eq(&iter, &clone))
|
||||||
|
continue;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ bool mschap_challenge_response(const uint8_t *challenge,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Hash the utf8 encoded nt password.
|
* Hash the utf8 encoded nt password.
|
||||||
* It is asumed, that the password is valid utf8!
|
* It is assumed, that the password is valid utf8!
|
||||||
* The rfc says "unicode-char", but never specifies which encoding.
|
* The rfc says "unicode-char", but never specifies which encoding.
|
||||||
* This function converts the password to ucs-2.
|
* This function converts the password to ucs-2.
|
||||||
* The example in the code uses LE for the unicode chars, so it is forced here.
|
* The example in the code uses LE for the unicode chars, so it is forced here.
|
||||||
|
@ -622,7 +622,7 @@ bool netconfig_get_fils_ip_req(struct netconfig *netconfig,
|
|||||||
struct ie_fils_ip_addr_request_info *info)
|
struct ie_fils_ip_addr_request_info *info)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Fill in the fields used for building the FILS IP Address Assigment
|
* Fill in the fields used for building the FILS IP Address Assignment
|
||||||
* IE during connection if we're configured to do automatic network
|
* IE during connection if we're configured to do automatic network
|
||||||
* configuration (usually DHCP). If we're configured with static
|
* configuration (usually DHCP). If we're configured with static
|
||||||
* values return false to mean the IE should not be sent.
|
* values return false to mean the IE should not be sent.
|
||||||
@ -710,7 +710,7 @@ struct netconfig *netconfig_new(uint32_t ifindex)
|
|||||||
netconfig_commit_init(netconfig);
|
netconfig_commit_init(netconfig);
|
||||||
|
|
||||||
debug_level = getenv("IWD_DHCP_DEBUG");
|
debug_level = getenv("IWD_DHCP_DEBUG");
|
||||||
if (debug_level != NULL) {
|
if (debug_level) {
|
||||||
if (!strcmp("debug", debug_level))
|
if (!strcmp("debug", debug_level))
|
||||||
dhcp_priority = L_LOG_DEBUG;
|
dhcp_priority = L_LOG_DEBUG;
|
||||||
else if (!strcmp("info", debug_level))
|
else if (!strcmp("info", debug_level))
|
||||||
|
580
src/netdev.c
580
src/netdev.c
@ -65,6 +65,7 @@
|
|||||||
#include "src/frame-xchg.h"
|
#include "src/frame-xchg.h"
|
||||||
#include "src/diagnostic.h"
|
#include "src/diagnostic.h"
|
||||||
#include "src/band.h"
|
#include "src/band.h"
|
||||||
|
#include "src/pmksa.h"
|
||||||
|
|
||||||
#ifndef ENOTSUPP
|
#ifndef ENOTSUPP
|
||||||
#define ENOTSUPP 524
|
#define ENOTSUPP 524
|
||||||
@ -133,6 +134,7 @@ struct netdev {
|
|||||||
uint32_t get_oci_cmd_id;
|
uint32_t get_oci_cmd_id;
|
||||||
uint32_t get_link_cmd_id;
|
uint32_t get_link_cmd_id;
|
||||||
uint32_t power_save_cmd_id;
|
uint32_t power_save_cmd_id;
|
||||||
|
uint32_t set_cqm_cmd_id;
|
||||||
enum netdev_result result;
|
enum netdev_result result;
|
||||||
uint16_t last_code; /* reason or status, depending on result */
|
uint16_t last_code; /* reason or status, depending on result */
|
||||||
struct l_timeout *neighbor_report_timeout;
|
struct l_timeout *neighbor_report_timeout;
|
||||||
@ -190,6 +192,8 @@ struct netdev {
|
|||||||
bool retry_auth : 1;
|
bool retry_auth : 1;
|
||||||
bool in_reassoc : 1;
|
bool in_reassoc : 1;
|
||||||
bool privacy : 1;
|
bool privacy : 1;
|
||||||
|
bool cqm_poll_fallback : 1;
|
||||||
|
bool external_auth : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct netdev_preauth_state {
|
struct netdev_preauth_state {
|
||||||
@ -373,6 +377,7 @@ struct handshake_state *netdev_handshake_state_new(struct netdev *netdev)
|
|||||||
|
|
||||||
nhs->super.ifindex = netdev->index;
|
nhs->super.ifindex = netdev->index;
|
||||||
nhs->super.free = netdev_handshake_state_free;
|
nhs->super.free = netdev_handshake_state_free;
|
||||||
|
nhs->super.refcount = 1;
|
||||||
|
|
||||||
nhs->netdev = netdev;
|
nhs->netdev = netdev;
|
||||||
/*
|
/*
|
||||||
@ -458,6 +463,14 @@ uint8_t netdev_get_rssi_level_idx(struct netdev *netdev)
|
|||||||
return netdev->cur_rssi_level_idx;
|
return netdev->cur_rssi_level_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int netdev_get_low_signal_threshold(uint32_t frequency)
|
||||||
|
{
|
||||||
|
if (frequency > 4000)
|
||||||
|
return LOW_SIGNAL_THRESHOLD_5GHZ;
|
||||||
|
|
||||||
|
return LOW_SIGNAL_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
static void netdev_set_powered_result(int error, uint16_t type,
|
static void netdev_set_powered_result(int error, uint16_t type,
|
||||||
const void *data,
|
const void *data,
|
||||||
uint32_t len, void *user_data)
|
uint32_t len, void *user_data)
|
||||||
@ -600,7 +613,7 @@ static bool netdev_parse_sta_info(struct l_genl_attr *attr,
|
|||||||
if (!netdev_parse_bitrate(&nested, &info->rx_mcs_type,
|
if (!netdev_parse_bitrate(&nested, &info->rx_mcs_type,
|
||||||
&info->rx_bitrate,
|
&info->rx_bitrate,
|
||||||
&info->rx_mcs))
|
&info->rx_mcs))
|
||||||
return false;
|
continue;
|
||||||
|
|
||||||
info->have_rx_bitrate = true;
|
info->have_rx_bitrate = true;
|
||||||
|
|
||||||
@ -616,7 +629,7 @@ static bool netdev_parse_sta_info(struct l_genl_attr *attr,
|
|||||||
if (!netdev_parse_bitrate(&nested, &info->tx_mcs_type,
|
if (!netdev_parse_bitrate(&nested, &info->tx_mcs_type,
|
||||||
&info->tx_bitrate,
|
&info->tx_bitrate,
|
||||||
&info->tx_mcs))
|
&info->tx_mcs))
|
||||||
return false;
|
continue;
|
||||||
|
|
||||||
info->have_tx_bitrate = true;
|
info->have_tx_bitrate = true;
|
||||||
|
|
||||||
@ -650,6 +663,47 @@ static void netdev_set_rssi_level_idx(struct netdev *netdev)
|
|||||||
netdev->cur_rssi_level_idx = new_level;
|
netdev->cur_rssi_level_idx = new_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void netdev_cqm_event_rssi_value(struct netdev *netdev, int rssi_val)
|
||||||
|
{
|
||||||
|
bool new_rssi_low;
|
||||||
|
uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx;
|
||||||
|
int threshold = netdev->frequency > 4000 ?
|
||||||
|
netdev->low_signal_threshold_5ghz :
|
||||||
|
netdev->low_signal_threshold;
|
||||||
|
|
||||||
|
if (!netdev->connected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (rssi_val > 127)
|
||||||
|
rssi_val = 127;
|
||||||
|
else if (rssi_val < -127)
|
||||||
|
rssi_val = -127;
|
||||||
|
|
||||||
|
netdev->cur_rssi = rssi_val;
|
||||||
|
|
||||||
|
if (!netdev->event_filter)
|
||||||
|
return;
|
||||||
|
|
||||||
|
new_rssi_low = rssi_val < threshold;
|
||||||
|
if (netdev->cur_rssi_low != new_rssi_low) {
|
||||||
|
int event = new_rssi_low ?
|
||||||
|
NETDEV_EVENT_RSSI_THRESHOLD_LOW :
|
||||||
|
NETDEV_EVENT_RSSI_THRESHOLD_HIGH;
|
||||||
|
|
||||||
|
netdev->cur_rssi_low = new_rssi_low;
|
||||||
|
netdev->event_filter(netdev, event, NULL, netdev->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!netdev->rssi_levels_num)
|
||||||
|
return;
|
||||||
|
|
||||||
|
netdev_set_rssi_level_idx(netdev);
|
||||||
|
if (netdev->cur_rssi_level_idx != prev_rssi_level_idx)
|
||||||
|
netdev->event_filter(netdev, NETDEV_EVENT_RSSI_LEVEL_NOTIFY,
|
||||||
|
&netdev->cur_rssi_level_idx,
|
||||||
|
netdev->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
static void netdev_rssi_poll_cb(struct l_genl_msg *msg, void *user_data)
|
static void netdev_rssi_poll_cb(struct l_genl_msg *msg, void *user_data)
|
||||||
{
|
{
|
||||||
struct netdev *netdev = user_data;
|
struct netdev *netdev = user_data;
|
||||||
@ -686,11 +740,16 @@ static void netdev_rssi_poll_cb(struct l_genl_msg *msg, void *user_data)
|
|||||||
netdev->cur_rssi = info.cur_rssi;
|
netdev->cur_rssi = info.cur_rssi;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note we don't have to handle LOW_SIGNAL_THRESHOLD here. The
|
* If the CMD_SET_CQM call failed RSSI polling was started. In this case
|
||||||
* CQM single threshold RSSI monitoring should work even if the
|
* we should behave just like its a CQM event and check both the RSSI
|
||||||
* kernel driver doesn't support multiple thresholds. So the
|
* level indexes and the HIGH/LOW thresholds.
|
||||||
* polling only handles the client-supplied threshold list.
|
|
||||||
*/
|
*/
|
||||||
|
if (netdev->cqm_poll_fallback) {
|
||||||
|
netdev_cqm_event_rssi_value(netdev, info.cur_rssi);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise just update the level notifications, CQM events work */
|
||||||
netdev_set_rssi_level_idx(netdev);
|
netdev_set_rssi_level_idx(netdev);
|
||||||
if (netdev->cur_rssi_level_idx != prev_rssi_level_idx)
|
if (netdev->cur_rssi_level_idx != prev_rssi_level_idx)
|
||||||
netdev->event_filter(netdev, NETDEV_EVENT_RSSI_LEVEL_NOTIFY,
|
netdev->event_filter(netdev, NETDEV_EVENT_RSSI_LEVEL_NOTIFY,
|
||||||
@ -720,11 +779,12 @@ static void netdev_rssi_poll(struct l_timeout *timeout, void *user_data)
|
|||||||
/* To be called whenever operational or rssi_levels_num are updated */
|
/* To be called whenever operational or rssi_levels_num are updated */
|
||||||
static void netdev_rssi_polling_update(struct netdev *netdev)
|
static void netdev_rssi_polling_update(struct netdev *netdev)
|
||||||
{
|
{
|
||||||
if (wiphy_has_ext_feature(netdev->wiphy,
|
if (!netdev->cqm_poll_fallback && wiphy_has_ext_feature(netdev->wiphy,
|
||||||
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (netdev->operational && netdev->rssi_levels_num > 0) {
|
if (netdev->operational && (netdev->rssi_levels_num > 0 ||
|
||||||
|
netdev->cqm_poll_fallback)) {
|
||||||
if (netdev->rssi_poll_timeout)
|
if (netdev->rssi_poll_timeout)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -778,7 +838,7 @@ static void netdev_connect_free(struct netdev *netdev)
|
|||||||
eapol_preauth_cancel(netdev->index);
|
eapol_preauth_cancel(netdev->index);
|
||||||
|
|
||||||
if (netdev->handshake) {
|
if (netdev->handshake) {
|
||||||
handshake_state_free(netdev->handshake);
|
handshake_state_unref(netdev->handshake);
|
||||||
netdev->handshake = NULL;
|
netdev->handshake = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -823,6 +883,7 @@ static void netdev_connect_free(struct netdev *netdev)
|
|||||||
netdev->expect_connect_failure = false;
|
netdev->expect_connect_failure = false;
|
||||||
netdev->cur_rssi_low = false;
|
netdev->cur_rssi_low = false;
|
||||||
netdev->privacy = false;
|
netdev->privacy = false;
|
||||||
|
netdev->external_auth = false;
|
||||||
|
|
||||||
if (netdev->connect_cmd) {
|
if (netdev->connect_cmd) {
|
||||||
l_genl_msg_unref(netdev->connect_cmd);
|
l_genl_msg_unref(netdev->connect_cmd);
|
||||||
@ -1031,6 +1092,11 @@ static void netdev_free(void *data)
|
|||||||
netdev->get_station_cmd_id = 0;
|
netdev->get_station_cmd_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (netdev->set_cqm_cmd_id) {
|
||||||
|
l_genl_family_cancel(nl80211, netdev->set_cqm_cmd_id);
|
||||||
|
netdev->set_cqm_cmd_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (netdev->fw_roam_bss)
|
if (netdev->fw_roam_bss)
|
||||||
scan_bss_free(netdev->fw_roam_bss);
|
scan_bss_free(netdev->fw_roam_bss);
|
||||||
|
|
||||||
@ -1039,7 +1105,11 @@ static void netdev_free(void *data)
|
|||||||
netdev->get_link_cmd_id = 0;
|
netdev->get_link_cmd_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (netdev->rssi_poll_timeout)
|
||||||
|
l_timeout_remove(netdev->rssi_poll_timeout);
|
||||||
|
|
||||||
scan_wdev_remove(netdev->wdev_id);
|
scan_wdev_remove(netdev->wdev_id);
|
||||||
|
frame_watch_wdev_remove(netdev->wdev_id);
|
||||||
|
|
||||||
watchlist_destroy(&netdev->station_watches);
|
watchlist_destroy(&netdev->station_watches);
|
||||||
|
|
||||||
@ -1093,47 +1163,6 @@ static void netdev_cqm_event_rssi_threshold(struct netdev *netdev,
|
|||||||
netdev->event_filter(netdev, event, NULL, netdev->user_data);
|
netdev->event_filter(netdev, event, NULL, netdev->user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void netdev_cqm_event_rssi_value(struct netdev *netdev, int rssi_val)
|
|
||||||
{
|
|
||||||
bool new_rssi_low;
|
|
||||||
uint8_t prev_rssi_level_idx = netdev->cur_rssi_level_idx;
|
|
||||||
int threshold = netdev->frequency > 4000 ?
|
|
||||||
netdev->low_signal_threshold_5ghz :
|
|
||||||
netdev->low_signal_threshold;
|
|
||||||
|
|
||||||
if (!netdev->connected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (rssi_val > 127)
|
|
||||||
rssi_val = 127;
|
|
||||||
else if (rssi_val < -127)
|
|
||||||
rssi_val = -127;
|
|
||||||
|
|
||||||
netdev->cur_rssi = rssi_val;
|
|
||||||
|
|
||||||
if (!netdev->event_filter)
|
|
||||||
return;
|
|
||||||
|
|
||||||
new_rssi_low = rssi_val < threshold;
|
|
||||||
if (netdev->cur_rssi_low != new_rssi_low) {
|
|
||||||
int event = new_rssi_low ?
|
|
||||||
NETDEV_EVENT_RSSI_THRESHOLD_LOW :
|
|
||||||
NETDEV_EVENT_RSSI_THRESHOLD_HIGH;
|
|
||||||
|
|
||||||
netdev->cur_rssi_low = new_rssi_low;
|
|
||||||
netdev->event_filter(netdev, event, NULL, netdev->user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!netdev->rssi_levels_num)
|
|
||||||
return;
|
|
||||||
|
|
||||||
netdev_set_rssi_level_idx(netdev);
|
|
||||||
if (netdev->cur_rssi_level_idx != prev_rssi_level_idx)
|
|
||||||
netdev->event_filter(netdev, NETDEV_EVENT_RSSI_LEVEL_NOTIFY,
|
|
||||||
&netdev->cur_rssi_level_idx,
|
|
||||||
netdev->user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void netdev_cqm_event(struct l_genl_msg *msg, struct netdev *netdev)
|
static void netdev_cqm_event(struct l_genl_msg *msg, struct netdev *netdev)
|
||||||
{
|
{
|
||||||
struct l_genl_attr attr;
|
struct l_genl_attr attr;
|
||||||
@ -1478,6 +1507,105 @@ static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
|
|||||||
handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err);
|
handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool netdev_match_addr(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
const struct netdev *netdev = a;
|
||||||
|
const uint8_t *addr = b;
|
||||||
|
|
||||||
|
return memcmp(netdev->addr, addr, ETH_ALEN) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct netdev *netdev_find_by_address(const uint8_t *addr)
|
||||||
|
{
|
||||||
|
return l_queue_find(netdev_list, netdev_match_addr, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_pmksa_driver_add(const struct pmksa *pmksa)
|
||||||
|
{
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
struct netdev *netdev = netdev_find_by_address(pmksa->spa);
|
||||||
|
uint32_t expiration = (uint32_t)pmksa->expiration;
|
||||||
|
|
||||||
|
if (!netdev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Only need to set the PMKSA into the kernel for fullmac drivers */
|
||||||
|
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
|
||||||
|
return;
|
||||||
|
|
||||||
|
l_debug("Adding PMKSA to kernel");
|
||||||
|
|
||||||
|
msg = l_genl_msg_new(NL80211_CMD_SET_PMKSA);
|
||||||
|
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PMKID, 16, pmksa->pmkid);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, pmksa->aa);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
|
||||||
|
pmksa->ssid_len, pmksa->ssid);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK_LIFETIME, 4, &expiration);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK,
|
||||||
|
pmksa->pmk_len, pmksa->pmk);
|
||||||
|
|
||||||
|
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
|
||||||
|
l_error("error sending SET_PMKSA");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_pmksa_driver_remove(const struct pmksa *pmksa)
|
||||||
|
{
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
struct netdev *netdev = netdev_find_by_address(pmksa->spa);
|
||||||
|
|
||||||
|
if (!netdev)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Only need to set the PMKSA into the kernel for fullmac drivers */
|
||||||
|
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
|
||||||
|
return;
|
||||||
|
|
||||||
|
l_debug("Removing PMKSA from kernel");
|
||||||
|
|
||||||
|
msg = l_genl_msg_new(NL80211_CMD_DEL_PMKSA);
|
||||||
|
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_PMKID, 16, pmksa->pmkid);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, pmksa->aa);
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
|
||||||
|
pmksa->ssid_len, pmksa->ssid);
|
||||||
|
|
||||||
|
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
|
||||||
|
l_error("error sending DEL_PMKSA");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_flush_pmksa(struct netdev *netdev)
|
||||||
|
{
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We only utilize the kernel's PMKSA cache for fullmac cards,
|
||||||
|
* so no need to flush if this is a softmac.
|
||||||
|
*/
|
||||||
|
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
|
||||||
|
return;
|
||||||
|
|
||||||
|
msg = l_genl_msg_new(NL80211_CMD_FLUSH_PMKSA);
|
||||||
|
|
||||||
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
|
||||||
|
|
||||||
|
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
|
||||||
|
l_error("Failed to flush PMKSA for %u", netdev->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_pmksa_driver_flush(void)
|
||||||
|
{
|
||||||
|
const struct l_queue_entry *e;
|
||||||
|
|
||||||
|
for (e = l_queue_get_entries(netdev_list); e; e = e->next) {
|
||||||
|
struct netdev *netdev = e->data;
|
||||||
|
|
||||||
|
netdev_flush_pmksa(netdev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void try_handshake_complete(struct netdev_handshake_state *nhs)
|
static void try_handshake_complete(struct netdev_handshake_state *nhs)
|
||||||
{
|
{
|
||||||
l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u",
|
l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u",
|
||||||
@ -1498,6 +1626,8 @@ static void try_handshake_complete(struct netdev_handshake_state *nhs)
|
|||||||
|
|
||||||
l_debug("Invoking handshake_event()");
|
l_debug("Invoking handshake_event()");
|
||||||
|
|
||||||
|
handshake_state_cache_pmksa(&nhs->super);
|
||||||
|
|
||||||
if (handshake_event(&nhs->super, HANDSHAKE_EVENT_COMPLETE))
|
if (handshake_event(&nhs->super, HANDSHAKE_EVENT_COMPLETE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -2151,7 +2281,7 @@ static void netdev_set_pmk(struct handshake_state *hs, const uint8_t *pmk,
|
|||||||
struct netdev_handshake_state, super);
|
struct netdev_handshake_state, super);
|
||||||
struct netdev *netdev = nhs->netdev;
|
struct netdev *netdev = nhs->netdev;
|
||||||
|
|
||||||
/* Only relevent for 8021x offload */
|
/* Only relevant for 8021x offload */
|
||||||
if (nhs->type != CONNECTION_TYPE_8021X_OFFLOAD)
|
if (nhs->type != CONNECTION_TYPE_8021X_OFFLOAD)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -2263,7 +2393,7 @@ static void netdev_qos_map_cb(struct l_genl_msg *msg, void *user_data)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ext_error = l_genl_msg_get_extended_error(msg);
|
ext_error = l_genl_msg_get_extended_error(msg);
|
||||||
l_error("Couuld not set QoS Map in kernel: %s",
|
l_error("Could not set QoS Map in kernel: %s",
|
||||||
ext_error ? ext_error : strerror(-err));
|
ext_error ? ext_error : strerror(-err));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2336,7 +2466,7 @@ static void netdev_get_oci_cb(struct l_genl_msg *msg, void *user_data)
|
|||||||
done:
|
done:
|
||||||
if (netdev->ap) {
|
if (netdev->ap) {
|
||||||
/*
|
/*
|
||||||
* Cant do much here. IWD assumes every kernel/driver supports
|
* Can't do much here. IWD assumes every kernel/driver supports
|
||||||
* this. There is no way of detecting support either.
|
* this. There is no way of detecting support either.
|
||||||
*/
|
*/
|
||||||
if (L_WARN_ON(err < 0))
|
if (L_WARN_ON(err < 0))
|
||||||
@ -2439,7 +2569,19 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
|
|||||||
{
|
{
|
||||||
struct netdev_handshake_state *nhs =
|
struct netdev_handshake_state *nhs =
|
||||||
l_container_of(hs, struct netdev_handshake_state, super);
|
l_container_of(hs, struct netdev_handshake_state, super);
|
||||||
uint32_t auth_type = IE_AKM_IS_SAE(hs->akm_suite) ?
|
/*
|
||||||
|
* Choose Open system auth type if PMKSA caching is used for an SAE AKM:
|
||||||
|
*
|
||||||
|
* IEEE 802.11-2020 Table 9-151
|
||||||
|
* - SAE authentication:
|
||||||
|
* 3 (SAE) for SAE Authentication
|
||||||
|
* 0 (open) for PMKSA caching
|
||||||
|
* - FT authentication over SAE:
|
||||||
|
* 3 (SAE) for FT Initial Mobility Domain Association
|
||||||
|
* 0 (open) for FT Initial Mobility Domain Association over
|
||||||
|
* PMKSA caching
|
||||||
|
*/
|
||||||
|
uint32_t auth_type = IE_AKM_IS_SAE(hs->akm_suite) && !hs->have_pmksa ?
|
||||||
NL80211_AUTHTYPE_SAE :
|
NL80211_AUTHTYPE_SAE :
|
||||||
NL80211_AUTHTYPE_OPEN_SYSTEM;
|
NL80211_AUTHTYPE_OPEN_SYSTEM;
|
||||||
enum mpdu_management_subtype subtype = prev_bssid ?
|
enum mpdu_management_subtype subtype = prev_bssid ?
|
||||||
@ -2463,7 +2605,10 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
|
|||||||
|
|
||||||
switch (nhs->type) {
|
switch (nhs->type) {
|
||||||
case CONNECTION_TYPE_SOFTMAC:
|
case CONNECTION_TYPE_SOFTMAC:
|
||||||
|
break;
|
||||||
case CONNECTION_TYPE_FULLMAC:
|
case CONNECTION_TYPE_FULLMAC:
|
||||||
|
l_genl_msg_append_attr(msg,
|
||||||
|
NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, 0, NULL);
|
||||||
break;
|
break;
|
||||||
case CONNECTION_TYPE_SAE_OFFLOAD:
|
case CONNECTION_TYPE_SAE_OFFLOAD:
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_SAE_PASSWORD,
|
l_genl_msg_append_attr(msg, NL80211_ATTR_SAE_PASSWORD,
|
||||||
@ -3377,6 +3522,84 @@ static void netdev_fils_tx_associate(struct iovec *fils_iov, size_t n_fils_iov,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void netdev_external_auth_frame_cb(struct l_genl_msg *msg,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
int error = l_genl_msg_get_error(msg);
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
l_debug("Failed to send External Auth Frame: %s(%d)",
|
||||||
|
strerror(-error), -error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_external_auth_sae_tx_authenticate(const uint8_t *body,
|
||||||
|
size_t body_len, void *user_data)
|
||||||
|
{
|
||||||
|
struct netdev *netdev = user_data;
|
||||||
|
struct handshake_state *hs = netdev->handshake;
|
||||||
|
uint16_t frame_type = MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION << 4;
|
||||||
|
struct iovec iov[2];
|
||||||
|
struct l_genl_msg *msg;
|
||||||
|
uint8_t algorithm[2] = { 0x03, 0x00 };
|
||||||
|
|
||||||
|
l_debug("");
|
||||||
|
|
||||||
|
iov[0].iov_base = &algorithm;
|
||||||
|
iov[0].iov_len = sizeof(algorithm);
|
||||||
|
iov[1].iov_base = (void *) body;
|
||||||
|
iov[1].iov_len = body_len;
|
||||||
|
|
||||||
|
msg = nl80211_build_cmd_frame(netdev->index, frame_type,
|
||||||
|
hs->spa, hs->aa, 0, iov, 2);
|
||||||
|
|
||||||
|
if (l_genl_family_send(nl80211, msg, netdev_external_auth_frame_cb,
|
||||||
|
netdev, NULL) > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
l_genl_msg_unref(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_external_auth_cb(struct l_genl_msg *msg, void *user_data)
|
||||||
|
{
|
||||||
|
int error = l_genl_msg_get_error(msg);
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
l_debug("Failed to send External Auth: %s(%d)",
|
||||||
|
strerror(-error), -error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_send_external_auth(struct netdev *netdev,
|
||||||
|
uint16_t status_code)
|
||||||
|
{
|
||||||
|
struct handshake_state *hs = netdev->handshake;
|
||||||
|
struct l_genl_msg *msg =
|
||||||
|
nl80211_build_external_auth(netdev->index, status_code,
|
||||||
|
hs->ssid, hs->ssid_len, hs->aa);
|
||||||
|
|
||||||
|
if (l_genl_family_send(nl80211, msg, netdev_external_auth_cb,
|
||||||
|
netdev, NULL) > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
l_genl_msg_unref(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void netdev_external_auth_sae_tx_associate(void *user_data)
|
||||||
|
{
|
||||||
|
struct netdev *netdev = user_data;
|
||||||
|
|
||||||
|
l_debug("");
|
||||||
|
|
||||||
|
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 {
|
struct rtnl_data {
|
||||||
struct netdev *netdev;
|
struct netdev *netdev;
|
||||||
uint8_t addr[ETH_ALEN];
|
uint8_t addr[ETH_ALEN];
|
||||||
@ -3385,6 +3608,10 @@ struct rtnl_data {
|
|||||||
|
|
||||||
static int netdev_begin_connection(struct netdev *netdev)
|
static int netdev_begin_connection(struct netdev *netdev)
|
||||||
{
|
{
|
||||||
|
struct netdev_handshake_state *nhs =
|
||||||
|
l_container_of(netdev->handshake,
|
||||||
|
struct netdev_handshake_state, super);
|
||||||
|
|
||||||
if (netdev->connect_cmd) {
|
if (netdev->connect_cmd) {
|
||||||
netdev->connect_cmd_id = l_genl_family_send(nl80211,
|
netdev->connect_cmd_id = l_genl_family_send(nl80211,
|
||||||
netdev->connect_cmd,
|
netdev->connect_cmd,
|
||||||
@ -3404,7 +3631,7 @@ static int netdev_begin_connection(struct netdev *netdev)
|
|||||||
*/
|
*/
|
||||||
handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
|
handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
|
||||||
|
|
||||||
if (netdev->ap) {
|
if (netdev->ap && nhs->type == CONNECTION_TYPE_SOFTMAC) {
|
||||||
if (!auth_proto_start(netdev->ap))
|
if (!auth_proto_start(netdev->ap))
|
||||||
goto failed;
|
goto failed;
|
||||||
|
|
||||||
@ -3644,11 +3871,50 @@ static struct l_genl_msg *netdev_build_cmd_cqm_rssi_update(
|
|||||||
|
|
||||||
static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
|
static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
|
||||||
{
|
{
|
||||||
|
struct netdev *netdev = user_data;
|
||||||
int err = l_genl_msg_get_error(msg);
|
int err = l_genl_msg_get_error(msg);
|
||||||
const char *ext_error;
|
const char *ext_error;
|
||||||
|
|
||||||
if (err >= 0)
|
netdev->set_cqm_cmd_id = 0;
|
||||||
|
|
||||||
|
if (err >= 0) {
|
||||||
|
/*
|
||||||
|
* Looking at some driver code it appears that the -ENOTSUP CQM
|
||||||
|
* failure could be transient. Just in case, reset the fallback
|
||||||
|
* flag if CQM happens to start working again.
|
||||||
|
*/
|
||||||
|
if (netdev->cqm_poll_fallback) {
|
||||||
|
l_debug("CMD_SET_CQM succeeded, stop polling fallback");
|
||||||
|
|
||||||
|
if (netdev->rssi_poll_timeout) {
|
||||||
|
l_timeout_remove(netdev->rssi_poll_timeout);
|
||||||
|
netdev->rssi_poll_timeout = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
netdev->cqm_poll_fallback = false;
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some drivers enable beacon filtering but also use software CQM which
|
||||||
|
* mac80211 detects and returns -ENOTSUP. There is no way to check this
|
||||||
|
* ahead of time so if we see this start polling in order to get RSSI
|
||||||
|
* updates.
|
||||||
|
*/
|
||||||
|
if (err == -ENOTSUP) {
|
||||||
|
l_debug("CMD_SET_CQM not supported, falling back to polling");
|
||||||
|
netdev->cqm_poll_fallback = true;
|
||||||
|
|
||||||
|
if (netdev->rssi_poll_timeout)
|
||||||
|
return;
|
||||||
|
|
||||||
|
netdev->rssi_poll_timeout = l_timeout_create(1,
|
||||||
|
netdev_rssi_poll, netdev, NULL);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ext_error = l_genl_msg_get_extended_error(msg);
|
ext_error = l_genl_msg_get_extended_error(msg);
|
||||||
l_error("CMD_SET_CQM failed: %s",
|
l_error("CMD_SET_CQM failed: %s",
|
||||||
@ -3658,9 +3924,21 @@ static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
|
|||||||
static int netdev_cqm_rssi_update(struct netdev *netdev)
|
static int netdev_cqm_rssi_update(struct netdev *netdev)
|
||||||
{
|
{
|
||||||
struct l_genl_msg *msg;
|
struct l_genl_msg *msg;
|
||||||
|
struct netdev_handshake_state *nhs = l_container_of(netdev->handshake,
|
||||||
|
struct netdev_handshake_state, super);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fullmac cards handle roaming in firmware, there is no need to set
|
||||||
|
* CQM thresholds
|
||||||
|
*/
|
||||||
|
if (nhs->type == CONNECTION_TYPE_FULLMAC)
|
||||||
|
return 0;
|
||||||
|
|
||||||
l_debug("");
|
l_debug("");
|
||||||
|
|
||||||
|
if (netdev->set_cqm_cmd_id)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
if (!wiphy_has_ext_feature(netdev->wiphy,
|
if (!wiphy_has_ext_feature(netdev->wiphy,
|
||||||
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
|
||||||
msg = netdev_build_cmd_cqm_rssi_update(netdev, NULL, 0);
|
msg = netdev_build_cmd_cqm_rssi_update(netdev, NULL, 0);
|
||||||
@ -3671,8 +3949,10 @@ static int netdev_cqm_rssi_update(struct netdev *netdev)
|
|||||||
if (!msg)
|
if (!msg)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!l_genl_family_send(nl80211, msg, netdev_cmd_set_cqm_cb,
|
netdev->set_cqm_cmd_id = l_genl_family_send(nl80211, msg,
|
||||||
NULL, NULL)) {
|
netdev_cmd_set_cqm_cb,
|
||||||
|
netdev, NULL);
|
||||||
|
if (!netdev->set_cqm_cmd_id) {
|
||||||
l_genl_msg_unref(msg);
|
l_genl_msg_unref(msg);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
@ -3696,9 +3976,7 @@ static int netdev_set_signal_thresholds(struct netdev *netdev, int threshold,
|
|||||||
netdev->low_signal_threshold = threshold;
|
netdev->low_signal_threshold = threshold;
|
||||||
netdev->low_signal_threshold_5ghz = threshold_5ghz;
|
netdev->low_signal_threshold_5ghz = threshold_5ghz;
|
||||||
|
|
||||||
netdev_cqm_rssi_update(netdev);
|
return netdev_cqm_rssi_update(netdev);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int netdev_lower_signal_threshold(struct netdev *netdev)
|
int netdev_lower_signal_threshold(struct netdev *netdev)
|
||||||
@ -3813,7 +4091,11 @@ static int netdev_handshake_state_setup_connection_type(
|
|||||||
if (softmac && wiphy_has_feature(wiphy, NL80211_FEATURE_SAE))
|
if (softmac && wiphy_has_feature(wiphy, NL80211_FEATURE_SAE))
|
||||||
goto softmac;
|
goto softmac;
|
||||||
|
|
||||||
return -EINVAL;
|
/* FullMAC uses EXTERNAL_AUTH and reuses this feature bit */
|
||||||
|
if (wiphy_has_feature(wiphy, NL80211_FEATURE_SAE))
|
||||||
|
goto fullmac;
|
||||||
|
|
||||||
|
return -ENOTSUP;
|
||||||
case IE_RSN_AKM_SUITE_FILS_SHA256:
|
case IE_RSN_AKM_SUITE_FILS_SHA256:
|
||||||
case IE_RSN_AKM_SUITE_FILS_SHA384:
|
case IE_RSN_AKM_SUITE_FILS_SHA384:
|
||||||
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256:
|
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256:
|
||||||
@ -3884,15 +4166,31 @@ static void netdev_connect_common(struct netdev *netdev,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nhs->type != CONNECTION_TYPE_SOFTMAC)
|
/*
|
||||||
|
* If SAE, and we have a valid PMKSA cache we can skip the entire SAE
|
||||||
|
* protocol and authenticate using the cached keys.
|
||||||
|
*/
|
||||||
|
if (IE_AKM_IS_SAE(hs->akm_suite) && hs->have_pmksa) {
|
||||||
|
l_debug("Skipping SAE by using PMKSA cache");
|
||||||
|
goto build_cmd_connect;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IE_AKM_IS_SAE(hs->akm_suite) ||
|
||||||
|
nhs->type == CONNECTION_TYPE_SAE_OFFLOAD)
|
||||||
goto build_cmd_connect;
|
goto build_cmd_connect;
|
||||||
|
|
||||||
switch (hs->akm_suite) {
|
if (nhs->type == CONNECTION_TYPE_SOFTMAC)
|
||||||
case IE_RSN_AKM_SUITE_SAE_SHA256:
|
|
||||||
case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256:
|
|
||||||
netdev->ap = sae_sm_new(hs, netdev_sae_tx_authenticate,
|
netdev->ap = sae_sm_new(hs, netdev_sae_tx_authenticate,
|
||||||
netdev_sae_tx_associate,
|
netdev_sae_tx_associate,
|
||||||
netdev);
|
netdev);
|
||||||
|
else {
|
||||||
|
netdev->ap =
|
||||||
|
sae_sm_new(hs, netdev_external_auth_sae_tx_authenticate,
|
||||||
|
netdev_external_auth_sae_tx_associate,
|
||||||
|
netdev);
|
||||||
|
sae_sm_force_default_group(netdev->ap);
|
||||||
|
sae_sm_force_hunt_and_peck(netdev->ap);
|
||||||
|
}
|
||||||
|
|
||||||
if (sae_sm_is_h2e(netdev->ap)) {
|
if (sae_sm_is_h2e(netdev->ap)) {
|
||||||
uint8_t own_rsnxe[20];
|
uint8_t own_rsnxe[20];
|
||||||
@ -3905,8 +4203,9 @@ static void netdev_connect_common(struct netdev *netdev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
if (nhs->type == CONNECTION_TYPE_SOFTMAC)
|
||||||
default:
|
goto done;
|
||||||
|
|
||||||
build_cmd_connect:
|
build_cmd_connect:
|
||||||
cmd_connect = netdev_build_cmd_connect(netdev, hs, prev_bssid);
|
cmd_connect = netdev_build_cmd_connect(netdev, hs, prev_bssid);
|
||||||
|
|
||||||
@ -3916,8 +4215,6 @@ build_cmd_connect:
|
|||||||
if (nhs->type == CONNECTION_TYPE_8021X_OFFLOAD)
|
if (nhs->type == CONNECTION_TYPE_8021X_OFFLOAD)
|
||||||
eapol_sm_set_require_handshake(sm, false);
|
eapol_sm_set_require_handshake(sm, false);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
netdev->connect_cmd = cmd_connect;
|
netdev->connect_cmd = cmd_connect;
|
||||||
netdev->event_filter = event_filter;
|
netdev->event_filter = event_filter;
|
||||||
@ -4091,7 +4388,7 @@ int netdev_reassociate(struct netdev *netdev, const struct scan_bss *target_bss,
|
|||||||
eapol_sm_free(old_sm);
|
eapol_sm_free(old_sm);
|
||||||
|
|
||||||
if (old_hs)
|
if (old_hs)
|
||||||
handshake_state_free(old_hs);
|
handshake_state_unref(old_hs);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -4426,6 +4723,52 @@ static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
|
|||||||
netdev_send_qos_map_set(netdev, body + 4, body_len - 4);
|
netdev_send_qos_map_set(netdev, body + 4, body_len - 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void netdev_sae_external_auth_frame_event(const struct mmpdu_header *hdr,
|
||||||
|
const void *body, size_t body_len,
|
||||||
|
int rssi, void *user_data)
|
||||||
|
{
|
||||||
|
struct netdev *netdev = user_data;
|
||||||
|
const struct mmpdu_authentication *auth;
|
||||||
|
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!netdev->external_auth)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!netdev->ap)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auth = mmpdu_body(hdr);
|
||||||
|
/*
|
||||||
|
* Allows station to persist settings so it does not retry
|
||||||
|
* the higher order ECC group again
|
||||||
|
*/
|
||||||
|
if (L_CPU_TO_LE16(auth->status) ==
|
||||||
|
MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP &&
|
||||||
|
netdev->event_filter)
|
||||||
|
netdev->event_filter(netdev, NETDEV_EVENT_ECC_GROUP_RETRY,
|
||||||
|
NULL, netdev->user_data);
|
||||||
|
|
||||||
|
ret = auth_proto_rx_authenticate(netdev->ap, (const void *) hdr,
|
||||||
|
mmpdu_header_len(hdr) + body_len);
|
||||||
|
|
||||||
|
switch (ret) {
|
||||||
|
case 0:
|
||||||
|
case -EAGAIN:
|
||||||
|
return;
|
||||||
|
case -ENOMSG:
|
||||||
|
case -EBADMSG:
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret > 0)
|
||||||
|
status_code = (uint16_t)ret;
|
||||||
|
|
||||||
|
netdev_send_external_auth(netdev, status_code);
|
||||||
|
}
|
||||||
|
|
||||||
static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
|
static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
|
||||||
{
|
{
|
||||||
struct netdev_preauth_state *preauth = user_data;
|
struct netdev_preauth_state *preauth = user_data;
|
||||||
@ -5250,6 +5593,58 @@ static void netdev_control_port_frame_event(struct l_genl_msg *msg,
|
|||||||
frame, frame_len, unencrypted);
|
frame, frame_len, unencrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void netdev_external_auth_event(struct l_genl_msg *msg,
|
||||||
|
struct netdev *netdev)
|
||||||
|
{
|
||||||
|
const uint8_t *bssid;
|
||||||
|
struct iovec ssid;
|
||||||
|
uint32_t akm;
|
||||||
|
uint32_t action;
|
||||||
|
struct handshake_state *hs = netdev->handshake;
|
||||||
|
|
||||||
|
if (L_WARN_ON(nl80211_parse_attrs(msg, NL80211_ATTR_AKM_SUITES, &akm,
|
||||||
|
NL80211_ATTR_EXTERNAL_AUTH_ACTION, &action,
|
||||||
|
NL80211_ATTR_BSSID, &bssid,
|
||||||
|
NL80211_ATTR_SSID, &ssid,
|
||||||
|
NL80211_ATTR_UNSPEC) < 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!L_IN_SET(action, NL80211_EXTERNAL_AUTH_START,
|
||||||
|
NL80211_EXTERNAL_AUTH_ABORT))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* kernel sends SAE_SHA256 AKM in BE order for legacy reasons */
|
||||||
|
if (!L_IN_SET(akm, CRYPTO_AKM_SAE_SHA256, CRYPTO_AKM_FT_OVER_SAE_SHA256,
|
||||||
|
L_CPU_TO_BE32(CRYPTO_AKM_SAE_SHA256))) {
|
||||||
|
l_warn("Unknown AKM: %08x", akm);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == NL80211_EXTERNAL_AUTH_ABORT) {
|
||||||
|
l_warn("External Auth Aborted");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hs->ssid_len != ssid.iov_len ||
|
||||||
|
memcmp(hs->ssid, ssid.iov_base, hs->ssid_len)) {
|
||||||
|
l_warn("Target SSID mismatch");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(hs->aa, bssid, ETH_ALEN)) {
|
||||||
|
l_warn("Target BSSID mismatch");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth_proto_start(netdev->ap)) {
|
||||||
|
netdev->external_auth = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
netdev_send_external_auth(netdev, MMPDU_STATUS_CODE_UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
static void netdev_unicast_notify(struct l_genl_msg *msg, void *user_data)
|
static void netdev_unicast_notify(struct l_genl_msg *msg, void *user_data)
|
||||||
{
|
{
|
||||||
struct netdev *netdev = NULL;
|
struct netdev *netdev = NULL;
|
||||||
@ -5287,6 +5682,9 @@ static void netdev_unicast_notify(struct l_genl_msg *msg, void *user_data)
|
|||||||
case NL80211_CMD_CONTROL_PORT_FRAME:
|
case NL80211_CMD_CONTROL_PORT_FRAME:
|
||||||
netdev_control_port_frame_event(msg, netdev);
|
netdev_control_port_frame_event(msg, netdev);
|
||||||
break;
|
break;
|
||||||
|
case NL80211_CMD_EXTERNAL_AUTH:
|
||||||
|
netdev_external_auth_event(msg, netdev);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5309,8 +5707,10 @@ int netdev_set_rssi_report_levels(struct netdev *netdev, const int8_t *levels,
|
|||||||
if (!cmd_set_cqm)
|
if (!cmd_set_cqm)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!l_genl_family_send(nl80211, cmd_set_cqm, netdev_cmd_set_cqm_cb,
|
netdev->set_cqm_cmd_id = l_genl_family_send(nl80211, cmd_set_cqm,
|
||||||
NULL, NULL)) {
|
netdev_cmd_set_cqm_cb,
|
||||||
|
netdev, NULL);
|
||||||
|
if (!netdev->set_cqm_cmd_id) {
|
||||||
l_genl_msg_unref(cmd_set_cqm);
|
l_genl_msg_unref(cmd_set_cqm);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
@ -5459,33 +5859,41 @@ static void netdev_add_station_frame_watches(struct netdev *netdev)
|
|||||||
static const uint8_t action_ft_response_prefix[] = { 0x06, 0x02 };
|
static const uint8_t action_ft_response_prefix[] = { 0x06, 0x02 };
|
||||||
static const uint8_t auth_ft_response_prefix[] = { 0x02, 0x00 };
|
static const uint8_t auth_ft_response_prefix[] = { 0x02, 0x00 };
|
||||||
static const uint8_t action_qos_map_prefix[] = { 0x01, 0x04 };
|
static const uint8_t action_qos_map_prefix[] = { 0x01, 0x04 };
|
||||||
|
static const uint8_t auth_sae_prefix[] = { 0x03, 0x00 };
|
||||||
uint64_t wdev = netdev->wdev_id;
|
uint64_t wdev = netdev->wdev_id;
|
||||||
|
|
||||||
/* Subscribe to Management -> Action -> RM -> Neighbor Report frames */
|
/* Subscribe to Management -> Action -> RM -> Neighbor Report frames */
|
||||||
frame_watch_add(wdev, 0, 0x00d0, action_neighbor_report_prefix,
|
frame_watch_add(wdev, 0, 0x00d0, action_neighbor_report_prefix,
|
||||||
sizeof(action_neighbor_report_prefix),
|
sizeof(action_neighbor_report_prefix), false,
|
||||||
netdev_neighbor_report_frame_event, netdev, NULL);
|
netdev_neighbor_report_frame_event, netdev, NULL);
|
||||||
|
|
||||||
frame_watch_add(wdev, 0, 0x00d0, action_sa_query_resp_prefix,
|
frame_watch_add(wdev, 0, 0x00d0, action_sa_query_resp_prefix,
|
||||||
sizeof(action_sa_query_resp_prefix),
|
sizeof(action_sa_query_resp_prefix), false,
|
||||||
netdev_sa_query_resp_frame_event, netdev, NULL);
|
netdev_sa_query_resp_frame_event, netdev, NULL);
|
||||||
|
|
||||||
frame_watch_add(wdev, 0, 0x00d0, action_sa_query_req_prefix,
|
frame_watch_add(wdev, 0, 0x00d0, action_sa_query_req_prefix,
|
||||||
sizeof(action_sa_query_req_prefix),
|
sizeof(action_sa_query_req_prefix), false,
|
||||||
netdev_sa_query_req_frame_event, netdev, NULL);
|
netdev_sa_query_req_frame_event, netdev, NULL);
|
||||||
|
|
||||||
frame_watch_add(wdev, 0, 0x00d0, action_ft_response_prefix,
|
frame_watch_add(wdev, 0, 0x00d0, action_ft_response_prefix,
|
||||||
sizeof(action_ft_response_prefix),
|
sizeof(action_ft_response_prefix), false,
|
||||||
netdev_ft_response_frame_event, netdev, NULL);
|
netdev_ft_response_frame_event, netdev, NULL);
|
||||||
|
|
||||||
frame_watch_add(wdev, 0, 0x00b0, auth_ft_response_prefix,
|
frame_watch_add(wdev, 0, 0x00b0, auth_ft_response_prefix,
|
||||||
sizeof(auth_ft_response_prefix),
|
sizeof(auth_ft_response_prefix), false,
|
||||||
netdev_ft_auth_response_frame_event, netdev, NULL);
|
netdev_ft_auth_response_frame_event, netdev, NULL);
|
||||||
|
|
||||||
if (wiphy_supports_qos_set_map(netdev->wiphy))
|
if (wiphy_supports_qos_set_map(netdev->wiphy))
|
||||||
frame_watch_add(wdev, 0, 0x00d0, action_qos_map_prefix,
|
frame_watch_add(wdev, 0, 0x00d0, action_qos_map_prefix,
|
||||||
sizeof(action_qos_map_prefix),
|
sizeof(action_qos_map_prefix), false,
|
||||||
netdev_qos_map_frame_event, netdev, NULL);
|
netdev_qos_map_frame_event, netdev, NULL);
|
||||||
|
|
||||||
|
if (!wiphy_supports_cmds_auth_assoc(netdev->wiphy) &&
|
||||||
|
wiphy_has_feature(netdev->wiphy, NL80211_FEATURE_SAE))
|
||||||
|
frame_watch_add(wdev, 0, 0x00b0,
|
||||||
|
auth_sae_prefix, sizeof(auth_sae_prefix),
|
||||||
|
false, netdev_sae_external_auth_frame_event,
|
||||||
|
netdev, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void netdev_setup_interface(struct netdev *netdev)
|
static void netdev_setup_interface(struct netdev *netdev)
|
||||||
@ -6233,6 +6641,16 @@ struct netdev *netdev_create_from_genl(struct l_genl_msg *msg,
|
|||||||
|
|
||||||
netdev_get_link(netdev);
|
netdev_get_link(netdev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call the netdev-specific variant to flush only this devices PMKSA
|
||||||
|
* cache in the kernel. This will make IWD's cache and the kernel's
|
||||||
|
* cache consistent, i.e. no entries
|
||||||
|
*
|
||||||
|
* TODO: If we ever are storing PMKSA's on disk we would first need to
|
||||||
|
* flush, then add all the PMKSA entries at this time.
|
||||||
|
*/
|
||||||
|
netdev_flush_pmksa(netdev);
|
||||||
|
|
||||||
return netdev;
|
return netdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6348,6 +6766,10 @@ static int netdev_init(void)
|
|||||||
|
|
||||||
__ft_set_tx_frame_func(netdev_tx_ft_frame);
|
__ft_set_tx_frame_func(netdev_tx_ft_frame);
|
||||||
|
|
||||||
|
__pmksa_set_driver_callbacks(netdev_pmksa_driver_add,
|
||||||
|
netdev_pmksa_driver_remove,
|
||||||
|
netdev_pmksa_driver_flush);
|
||||||
|
|
||||||
unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
|
unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
|
||||||
netdev_unicast_notify,
|
netdev_unicast_notify,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
|
@ -158,6 +158,7 @@ const char *netdev_get_name(struct netdev *netdev);
|
|||||||
bool netdev_get_is_up(struct netdev *netdev);
|
bool netdev_get_is_up(struct netdev *netdev);
|
||||||
const char *netdev_get_path(struct netdev *netdev);
|
const char *netdev_get_path(struct netdev *netdev);
|
||||||
uint8_t netdev_get_rssi_level_idx(struct netdev *netdev);
|
uint8_t netdev_get_rssi_level_idx(struct netdev *netdev);
|
||||||
|
int netdev_get_low_signal_threshold(uint32_t frequency);
|
||||||
|
|
||||||
struct handshake_state *netdev_handshake_state_new(struct netdev *netdev);
|
struct handshake_state *netdev_handshake_state_new(struct netdev *netdev);
|
||||||
struct handshake_state *netdev_get_handshake(struct netdev *netdev);
|
struct handshake_state *netdev_get_handshake(struct netdev *netdev);
|
||||||
|
@ -56,6 +56,7 @@
|
|||||||
#include "src/erp.h"
|
#include "src/erp.h"
|
||||||
#include "src/handshake.h"
|
#include "src/handshake.h"
|
||||||
#include "src/band.h"
|
#include "src/band.h"
|
||||||
|
#include "src/util.h"
|
||||||
|
|
||||||
#define SAE_PT_SETTING "SAE-PT-Group%u"
|
#define SAE_PT_SETTING "SAE-PT-Group%u"
|
||||||
|
|
||||||
@ -212,31 +213,10 @@ void network_disconnected(struct network *network)
|
|||||||
station_hide_network(network->station, network);
|
station_hide_network(network->station, network);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First 64 entries calculated by 1 / pow(n, 0.3) for n >= 1 */
|
|
||||||
static const double rankmod_table[] = {
|
|
||||||
1.0000000000, 0.8122523964, 0.7192230933, 0.6597539554,
|
|
||||||
0.6170338627, 0.5841906811, 0.5577898253, 0.5358867313,
|
|
||||||
0.5172818580, 0.5011872336, 0.4870596972, 0.4745102806,
|
|
||||||
0.4632516708, 0.4530661223, 0.4437850034, 0.4352752816,
|
|
||||||
0.4274303178, 0.4201634287, 0.4134032816, 0.4070905315,
|
|
||||||
0.4011753236, 0.3956154062, 0.3903746872, 0.3854221125,
|
|
||||||
0.3807307877, 0.3762772797, 0.3720410580, 0.3680040435,
|
|
||||||
0.3641502401, 0.3604654325, 0.3569369365, 0.3535533906,
|
|
||||||
0.3503045821, 0.3471812999, 0.3441752105, 0.3412787518,
|
|
||||||
0.3384850430, 0.3357878061, 0.3331812996, 0.3306602598,
|
|
||||||
0.3282198502, 0.3258556179, 0.3235634544, 0.3213395618,
|
|
||||||
0.3191804229, 0.3170827751, 0.3150435863, 0.3130600345,
|
|
||||||
0.3111294892, 0.3092494947, 0.3074177553, 0.3056321221,
|
|
||||||
0.3038905808, 0.3021912409, 0.3005323264, 0.2989121662,
|
|
||||||
0.2973291870, 0.2957819051, 0.2942689208, 0.2927889114,
|
|
||||||
0.2913406263, 0.2899228820, 0.2885345572, 0.2871745887,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool network_rankmod(const struct network *network, double *rankmod)
|
bool network_rankmod(const struct network *network, double *rankmod)
|
||||||
{
|
{
|
||||||
struct network_info *info = network->info;
|
struct network_info *info = network->info;
|
||||||
int n;
|
int n;
|
||||||
int nmax;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Current policy is that only networks successfully connected
|
* Current policy is that only networks successfully connected
|
||||||
@ -250,12 +230,7 @@ bool network_rankmod(const struct network *network, double *rankmod)
|
|||||||
if (n < 0)
|
if (n < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
nmax = L_ARRAY_SIZE(rankmod_table);
|
*rankmod = util_exponential_decay(n);
|
||||||
|
|
||||||
if (n >= nmax)
|
|
||||||
n = nmax - 1;
|
|
||||||
|
|
||||||
*rankmod = rankmod_table[n];
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -911,6 +886,9 @@ int network_can_connect_bss(struct network *network, const struct scan_bss *bss)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IE_AKM_IS_OWE(rsn.akm_suites) && wiphy_owe_disabled(wiphy))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
if (!config || !config->have_transition_disable) {
|
if (!config || !config->have_transition_disable) {
|
||||||
if (band == BAND_FREQ_6_GHZ)
|
if (band == BAND_FREQ_6_GHZ)
|
||||||
goto mfp_no_tkip;
|
goto mfp_no_tkip;
|
||||||
@ -1278,6 +1256,7 @@ struct scan_bss *network_bss_select(struct network *network,
|
|||||||
struct l_queue *bss_list = network->bss_list;
|
struct l_queue *bss_list = network->bss_list;
|
||||||
const struct l_queue_entry *bss_entry;
|
const struct l_queue_entry *bss_entry;
|
||||||
struct scan_bss *candidate = NULL;
|
struct scan_bss *candidate = NULL;
|
||||||
|
bool skipped_open = false;
|
||||||
|
|
||||||
for (bss_entry = l_queue_get_entries(bss_list); bss_entry;
|
for (bss_entry = l_queue_get_entries(bss_list); bss_entry;
|
||||||
bss_entry = bss_entry->next) {
|
bss_entry = bss_entry->next) {
|
||||||
@ -1297,22 +1276,27 @@ struct scan_bss *network_bss_select(struct network *network,
|
|||||||
if (!candidate)
|
if (!candidate)
|
||||||
candidate = bss;
|
candidate = bss;
|
||||||
|
|
||||||
|
/* check if temporarily blacklisted */
|
||||||
|
if (l_queue_find(network->blacklist, match_bss, bss))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (blacklist_contains_bss(bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED))
|
||||||
|
continue;
|
||||||
|
|
||||||
/* OWE Transition BSS */
|
/* OWE Transition BSS */
|
||||||
if (bss->owe_trans) {
|
if (bss->owe_trans) {
|
||||||
/* Don't want to connect to the Open BSS if possible */
|
/* Don't want to connect to the Open BSS if possible */
|
||||||
if (!bss->rsne)
|
if (!bss->rsne) {
|
||||||
|
skipped_open = true;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Candidate is not OWE, set this as new candidate */
|
/* Candidate is not OWE, set this as new candidate */
|
||||||
if (!(candidate->owe_trans && candidate->rsne))
|
if (!(candidate->owe_trans && candidate->rsne))
|
||||||
candidate = bss;
|
candidate = bss;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if temporarily blacklisted */
|
|
||||||
if (l_queue_find(network->blacklist, match_bss, bss))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!blacklist_contains_bss(bss->addr))
|
|
||||||
return bss;
|
return bss;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1320,7 +1304,7 @@ struct scan_bss *network_bss_select(struct network *network,
|
|||||||
* No BSS was found, but if we are falling back to blacklisted BSS's we
|
* No BSS was found, but if we are falling back to blacklisted BSS's we
|
||||||
* can just use the first connectable candidate found above.
|
* can just use the first connectable candidate found above.
|
||||||
*/
|
*/
|
||||||
if (fallback_to_blacklist)
|
if (fallback_to_blacklist || skipped_open)
|
||||||
return candidate;
|
return candidate;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -2010,10 +1994,8 @@ void network_rank_update(struct network *network, bool connected)
|
|||||||
|
|
||||||
L_WARN_ON(n < 0);
|
L_WARN_ON(n < 0);
|
||||||
|
|
||||||
if (n >= (int) L_ARRAY_SIZE(rankmod_table))
|
network->rank =
|
||||||
n = L_ARRAY_SIZE(rankmod_table) - 1;
|
util_exponential_decay(n) * best_bss->rank + USHRT_MAX;
|
||||||
|
|
||||||
network->rank = rankmod_table[n] * best_bss->rank + USHRT_MAX;
|
|
||||||
} else
|
} else
|
||||||
network->rank = best_bss->rank;
|
network->rank = best_bss->rank;
|
||||||
|
|
||||||
|
@ -648,6 +648,8 @@ struct l_genl_msg *nl80211_build_cmd_frame(uint32_t ifindex,
|
|||||||
msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + 512);
|
msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + 512);
|
||||||
|
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
|
||||||
|
|
||||||
|
if (freq)
|
||||||
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &freq);
|
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &freq);
|
||||||
l_genl_msg_append_attrv(msg, NL80211_ATTR_FRAME, iovs, iov_len + 1);
|
l_genl_msg_append_attrv(msg, NL80211_ATTR_FRAME, iovs, iov_len + 1);
|
||||||
|
|
||||||
@ -695,8 +697,7 @@ int nl80211_parse_chandef(struct l_genl_msg *msg, struct band_chandef *out)
|
|||||||
|
|
||||||
int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
|
int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
|
||||||
struct scan_freq_set *supported_list,
|
struct scan_freq_set *supported_list,
|
||||||
struct band_freq_attrs *list,
|
struct band *band)
|
||||||
size_t num_channels)
|
|
||||||
{
|
{
|
||||||
uint16_t type, len;
|
uint16_t type, len;
|
||||||
const void *data;
|
const void *data;
|
||||||
@ -710,6 +711,7 @@ int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
|
|||||||
while (l_genl_attr_next(&nested, NULL, NULL, NULL)) {
|
while (l_genl_attr_next(&nested, NULL, NULL, NULL)) {
|
||||||
uint32_t freq = 0;
|
uint32_t freq = 0;
|
||||||
struct band_freq_attrs freq_attr = { 0 };
|
struct band_freq_attrs freq_attr = { 0 };
|
||||||
|
enum band_freq out_band;
|
||||||
|
|
||||||
if (!l_genl_attr_recurse(&nested, &attr))
|
if (!l_genl_attr_recurse(&nested, &attr))
|
||||||
continue;
|
continue;
|
||||||
@ -750,17 +752,20 @@ int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
|
|||||||
if (!freq)
|
if (!freq)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
channel = band_freq_to_channel(freq, NULL);
|
channel = band_freq_to_channel(freq, &out_band);
|
||||||
if (!channel)
|
if (!channel)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (L_WARN_ON(channel > num_channels))
|
if (L_WARN_ON(out_band != band->freq))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (L_WARN_ON(channel > band->freqs_len))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (supported_list)
|
if (supported_list)
|
||||||
scan_freq_set_add(supported_list, freq);
|
scan_freq_set_add(supported_list, freq);
|
||||||
|
|
||||||
list[channel] = freq_attr;
|
band->freq_attrs[channel] = freq_attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -26,6 +26,7 @@ struct band_chandef;
|
|||||||
struct scan_freq_set;
|
struct scan_freq_set;
|
||||||
struct band_freq_attrs;
|
struct band_freq_attrs;
|
||||||
struct handshake_state;
|
struct handshake_state;
|
||||||
|
struct band;
|
||||||
|
|
||||||
int nl80211_parse_attrs(struct l_genl_msg *msg, int tag, ...);
|
int nl80211_parse_attrs(struct l_genl_msg *msg, int tag, ...);
|
||||||
int nl80211_parse_nested(struct l_genl_attr *attr, int type, int tag, ...);
|
int nl80211_parse_nested(struct l_genl_attr *attr, int type, int tag, ...);
|
||||||
@ -95,8 +96,7 @@ struct l_genl_msg *nl80211_build_external_auth(uint32_t ifindex,
|
|||||||
int nl80211_parse_chandef(struct l_genl_msg *msg, struct band_chandef *out);
|
int nl80211_parse_chandef(struct l_genl_msg *msg, struct band_chandef *out);
|
||||||
int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
|
int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
|
||||||
struct scan_freq_set *supported_list,
|
struct scan_freq_set *supported_list,
|
||||||
struct band_freq_attrs *list,
|
struct band *band);
|
||||||
size_t num_channels);
|
|
||||||
|
|
||||||
void nl80211_append_rsn_attributes(struct l_genl_msg *msg,
|
void nl80211_append_rsn_attributes(struct l_genl_msg *msg,
|
||||||
struct handshake_state *hs);
|
struct handshake_state *hs);
|
||||||
|
@ -293,7 +293,7 @@ static void offchannel_mlme_notify(struct l_genl_msg *msg, void *user_data)
|
|||||||
* - an event coming from an external ROC request (we just
|
* - an event coming from an external ROC request (we just
|
||||||
* happened to have also sent an ROC request).
|
* happened to have also sent an ROC request).
|
||||||
*
|
*
|
||||||
* We can't tell where the event originated until we recieve our
|
* We can't tell where the event originated until we receive our
|
||||||
* ACK so set early_cookie to track it.
|
* ACK so set early_cookie to track it.
|
||||||
*/
|
*/
|
||||||
if (i->roc_cmd_id != 0 && l_genl_family_request_sent(nl80211,
|
if (i->roc_cmd_id != 0 && l_genl_family_request_sent(nl80211,
|
||||||
|
13
src/p2p.c
13
src/p2p.c
@ -1497,7 +1497,7 @@ static void p2p_handshake_event(struct handshake_state *hs,
|
|||||||
static void p2p_try_connect_group(struct p2p_device *dev)
|
static void p2p_try_connect_group(struct p2p_device *dev)
|
||||||
{
|
{
|
||||||
struct scan_bss *bss = dev->conn_wsc_bss;
|
struct scan_bss *bss = dev->conn_wsc_bss;
|
||||||
_auto_(handshake_state_free) struct handshake_state *hs = NULL;
|
_auto_(handshake_state_unref) struct handshake_state *hs = NULL;
|
||||||
struct iovec ie_iov[16];
|
struct iovec ie_iov[16];
|
||||||
int ie_num = 0;
|
int ie_num = 0;
|
||||||
int r;
|
int r;
|
||||||
@ -2217,7 +2217,7 @@ static bool p2p_go_negotiation_confirm_cb(const struct mmpdu_header *mpdu,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Start setting the group up right away and we'll add the
|
* Start setting the group up right away and we'll add the
|
||||||
* client's Configuation Timeout to the WSC start timeout's
|
* client's Configuration Timeout to the WSC start timeout's
|
||||||
* value.
|
* value.
|
||||||
*/
|
*/
|
||||||
p2p_device_interface_create(dev);
|
p2p_device_interface_create(dev);
|
||||||
@ -2549,7 +2549,7 @@ static void p2p_go_negotiation_confirm_done(int error, void *user_data)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Frame was ACKed. On the GO start setting the group up right
|
* Frame was ACKed. On the GO start setting the group up right
|
||||||
* away and we'll add the client's Configuation Timeout to the
|
* away and we'll add the client's Configuration Timeout to the
|
||||||
* WSC start timeout's value. On the client wait idly the
|
* WSC start timeout's value. On the client wait idly the
|
||||||
* maximum amount of time indicated by the peer in the GO
|
* maximum amount of time indicated by the peer in the GO
|
||||||
* Negotiation Response's Configuration Timeout attribute and
|
* Negotiation Response's Configuration Timeout attribute and
|
||||||
@ -2951,7 +2951,7 @@ static bool p2p_provision_disc_resp_cb(const struct mmpdu_header *mpdu,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Indended P2P Interface address is optional, we don't have the
|
* Intended P2P Interface address is optional, we don't have the
|
||||||
* BSSID of the group here.
|
* BSSID of the group here.
|
||||||
*
|
*
|
||||||
* We might want to make sure that Group Formation is false but the
|
* We might want to make sure that Group Formation is false but the
|
||||||
@ -4163,10 +4163,11 @@ static void p2p_device_discovery_start(struct p2p_device *dev)
|
|||||||
L_ARRAY_SIZE(channels_social)];
|
L_ARRAY_SIZE(channels_social)];
|
||||||
|
|
||||||
frame_watch_add(dev->wdev_id, FRAME_GROUP_P2P_LISTEN, 0x0040,
|
frame_watch_add(dev->wdev_id, FRAME_GROUP_P2P_LISTEN, 0x0040,
|
||||||
(uint8_t *) "", 0, p2p_device_probe_cb, dev, NULL);
|
(uint8_t *) "", 0, false,
|
||||||
|
p2p_device_probe_cb, dev, NULL);
|
||||||
frame_watch_add(dev->wdev_id, FRAME_GROUP_P2P_LISTEN, 0x00d0,
|
frame_watch_add(dev->wdev_id, FRAME_GROUP_P2P_LISTEN, 0x00d0,
|
||||||
p2p_frame_go_neg_req.data, p2p_frame_go_neg_req.len,
|
p2p_frame_go_neg_req.data, p2p_frame_go_neg_req.len,
|
||||||
p2p_device_go_negotiation_req_cb, dev, NULL);
|
false, p2p_device_go_negotiation_req_cb, dev, NULL);
|
||||||
|
|
||||||
p2p_device_scan_start(dev);
|
p2p_device_scan_start(dev);
|
||||||
}
|
}
|
||||||
|
269
src/pmksa.c
Normal file
269
src/pmksa.c
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Wireless daemon for Linux
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 Cruise LLC. 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
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <ell/ell.h>
|
||||||
|
#include "ell/useful.h"
|
||||||
|
|
||||||
|
#include "src/module.h"
|
||||||
|
#include "src/pmksa.h"
|
||||||
|
|
||||||
|
#define PMKID "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
|
||||||
|
#define PMKID_STR(x) x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], \
|
||||||
|
x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
|
||||||
|
|
||||||
|
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;
|
||||||
|
uint32_t capacity;
|
||||||
|
uint32_t used;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct min_heap cache;
|
||||||
|
|
||||||
|
static __always_inline void swap_ptr(void *l, void *r)
|
||||||
|
{
|
||||||
|
struct pmksa **lp = l;
|
||||||
|
struct pmksa **rp = r;
|
||||||
|
|
||||||
|
SWAP(*lp, *rp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline
|
||||||
|
bool pmksa_compare_expiration(const void *l, const void *r)
|
||||||
|
{
|
||||||
|
const struct pmksa * const *lp = l;
|
||||||
|
const struct pmksa * const *rp = r;
|
||||||
|
|
||||||
|
return (*lp)->expiration < (*rp)->expiration;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct l_minheap_ops ops = {
|
||||||
|
.elem_size = sizeof(struct pmksa *),
|
||||||
|
.swap = swap_ptr,
|
||||||
|
.less = pmksa_compare_expiration,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pmksa_cache_find(const uint8_t spa[static 6],
|
||||||
|
const uint8_t aa[static 6],
|
||||||
|
const uint8_t *ssid, size_t ssid_len,
|
||||||
|
uint32_t akm)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cache.used; i++) {
|
||||||
|
struct pmksa *pmksa = cache.data[i];
|
||||||
|
|
||||||
|
if (memcmp(pmksa->spa, spa, sizeof(pmksa->spa)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memcmp(pmksa->aa, aa, sizeof(pmksa->aa)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ssid_len != pmksa->ssid_len)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memcmp(pmksa->ssid, ssid, ssid_len))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (akm & pmksa->akm)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to obtain a PMKSA entry from the cache. If the the entry matching
|
||||||
|
* the parameters is found, it is removed from the cache and returned to the
|
||||||
|
* caller. The caller is responsible for managing the returned pmksa
|
||||||
|
* structure
|
||||||
|
*/
|
||||||
|
struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
|
||||||
|
const uint8_t aa[static 6],
|
||||||
|
const uint8_t *ssid, size_t ssid_len,
|
||||||
|
uint32_t akm)
|
||||||
|
{
|
||||||
|
struct pmksa *pmksa;
|
||||||
|
int r = pmksa_cache_find(spa, aa, ssid, ssid_len, akm);
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
cache.used -= 1;
|
||||||
|
if ((uint32_t) r == cache.used)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
SWAP(cache.data[r], cache.data[cache.used]);
|
||||||
|
__minheap_sift_down(cache.data, cache.used, r, &ops);
|
||||||
|
|
||||||
|
done:
|
||||||
|
pmksa = cache.data[cache.used];
|
||||||
|
|
||||||
|
l_debug("Returning entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
|
||||||
|
|
||||||
|
return pmksa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put a PMKSA into the cache. It will be sorted in soonest-to-expire order.
|
||||||
|
* If the cache is full, then soonest-to-expire entry is removed first.
|
||||||
|
*/
|
||||||
|
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]);
|
||||||
|
cache.data[0] = pmksa;
|
||||||
|
__minheap_sift_down(cache.data, cache.used, 0, &ops);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.data[cache.used] = pmksa;
|
||||||
|
__minheap_sift_up(cache.data, cache.used, &ops);
|
||||||
|
cache.used += 1;
|
||||||
|
|
||||||
|
if (driver_add)
|
||||||
|
driver_add(pmksa);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Expire all PMKSA entries with expiration time smaller or equal to the cutoff
|
||||||
|
* time.
|
||||||
|
*/
|
||||||
|
int pmksa_cache_expire(uint64_t cutoff)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int used = cache.used;
|
||||||
|
int remaining = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < used; i++) {
|
||||||
|
if (cache.data[i]->expiration <= cutoff) {
|
||||||
|
pmksa_cache_free(cache.data[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.data[remaining] = cache.data[i];
|
||||||
|
remaining += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.used = remaining;
|
||||||
|
|
||||||
|
for (i = cache.used >> 1; i >= 0; i--)
|
||||||
|
__minheap_sift_down(cache.data, cache.used, i, &ops);
|
||||||
|
|
||||||
|
return used - remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush all PMKSA entries from the cache, regardless of expiration time.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries)
|
||||||
|
{
|
||||||
|
if (out_n_entries)
|
||||||
|
*out_n_entries = cache.used;
|
||||||
|
|
||||||
|
return cache.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t pmksa_lifetime(void)
|
||||||
|
{
|
||||||
|
return dot11RSNAConfigPMKLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __pmksa_set_config(const struct l_settings *config)
|
||||||
|
{
|
||||||
|
l_settings_get_uint(config, "PMKSA", "Capacity",
|
||||||
|
&pmksa_cache_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __pmksa_set_driver_callbacks(pmksa_cache_add_func_t add,
|
||||||
|
pmksa_cache_remove_func_t remove,
|
||||||
|
pmksa_cache_flush_func_t flush)
|
||||||
|
{
|
||||||
|
driver_add = add;
|
||||||
|
driver_remove = remove;
|
||||||
|
driver_flush = flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pmksa_init(void)
|
||||||
|
{
|
||||||
|
cache.capacity = pmksa_cache_capacity;
|
||||||
|
cache.used = 0;
|
||||||
|
cache.data = l_new(struct pmksa *, cache.capacity);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pmksa_exit(void)
|
||||||
|
{
|
||||||
|
pmksa_cache_flush();
|
||||||
|
l_free(cache.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
IWD_MODULE(pmksa, pmksa_init, pmksa_exit);
|
55
src/pmksa.h
Normal file
55
src/pmksa.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Wireless daemon for Linux
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 Cruise, LLC. 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct pmksa {
|
||||||
|
uint64_t expiration;
|
||||||
|
uint8_t spa[6];
|
||||||
|
uint8_t aa[6];
|
||||||
|
uint8_t ssid[32];
|
||||||
|
size_t ssid_len;
|
||||||
|
uint32_t akm;
|
||||||
|
uint8_t pmkid[16];
|
||||||
|
uint8_t pmk[64];
|
||||||
|
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],
|
||||||
|
const uint8_t aa[static 6],
|
||||||
|
const uint8_t *ssid, size_t ssid_len,
|
||||||
|
uint32_t akm);
|
||||||
|
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);
|
@ -798,7 +798,7 @@ static void rrm_add_frame_watches(struct rrm_state *rrm)
|
|||||||
l_debug("");
|
l_debug("");
|
||||||
|
|
||||||
frame_watch_add(rrm->wdev_id, 0, frame_type, prefix, sizeof(prefix),
|
frame_watch_add(rrm->wdev_id, 0, frame_type, prefix, sizeof(prefix),
|
||||||
rrm_frame_watch_cb, rrm, NULL);
|
false, rrm_frame_watch_cb, rrm, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rrm_state *rrm_new_state(struct netdev *netdev)
|
static struct rrm_state *rrm_new_state(struct netdev *netdev)
|
||||||
|
22
src/sae.c
22
src/sae.c
@ -258,7 +258,7 @@ static struct l_ecc_scalar *sae_pwd_value(const struct l_ecc_curve *curve,
|
|||||||
is_in_range = util_secure_fill_with_msb(is_in_range);
|
is_in_range = util_secure_fill_with_msb(is_in_range);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* libell has public Legendre symbol only for l_ecc_scalar, but they
|
* ELL has public Legendre symbol only for l_ecc_scalar, but they
|
||||||
* cannot be created if the coordinate is greater than the p. Hence,
|
* cannot be created if the coordinate is greater than the p. Hence,
|
||||||
* to avoid control flow dependencies, we replace pwd_value by a dummy
|
* to avoid control flow dependencies, we replace pwd_value by a dummy
|
||||||
* quadratic non residue if we generate a value >= prime.
|
* quadratic non residue if we generate a value >= prime.
|
||||||
@ -1550,6 +1550,26 @@ struct auth_proto *sae_sm_new(struct handshake_state *hs,
|
|||||||
return &sm->ap;
|
return &sm->ap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sae_sm_force_hunt_and_peck(struct auth_proto *ap)
|
||||||
|
{
|
||||||
|
struct sae_sm *sm = l_container_of(ap, struct sae_sm, ap);
|
||||||
|
|
||||||
|
sae_debug("Forcing SAE Hunting and Pecking");
|
||||||
|
sm->sae_type = CRYPTO_SAE_LOOPING;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sae_sm_force_default_group(struct auth_proto *ap)
|
||||||
|
{
|
||||||
|
struct sae_sm *sm = l_container_of(ap, struct sae_sm, ap);
|
||||||
|
|
||||||
|
sae_debug("Forcing Default Group");
|
||||||
|
sm->force_default_group = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int sae_init(void)
|
static int sae_init(void)
|
||||||
{
|
{
|
||||||
if (getenv("IWD_SAE_DEBUG"))
|
if (getenv("IWD_SAE_DEBUG"))
|
||||||
|
@ -34,3 +34,6 @@ struct auth_proto *sae_sm_new(struct handshake_state *hs,
|
|||||||
sae_tx_authenticate_func_t tx_auth,
|
sae_tx_authenticate_func_t tx_auth,
|
||||||
sae_tx_associate_func_t tx_assoc,
|
sae_tx_associate_func_t tx_assoc,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
|
|
||||||
|
bool sae_sm_force_hunt_and_peck(struct auth_proto *ap);
|
||||||
|
bool sae_sm_force_default_group(struct auth_proto *ap);
|
||||||
|
124
src/scan.c
124
src/scan.c
@ -56,6 +56,8 @@
|
|||||||
static double RANK_2G_FACTOR;
|
static double RANK_2G_FACTOR;
|
||||||
static double RANK_5G_FACTOR;
|
static double RANK_5G_FACTOR;
|
||||||
static double RANK_6G_FACTOR;
|
static double RANK_6G_FACTOR;
|
||||||
|
static uint32_t RANK_HIGH_UTILIZATION;
|
||||||
|
static uint32_t RANK_HIGH_STATION_COUNT;
|
||||||
static uint32_t SCAN_MAX_INTERVAL;
|
static uint32_t SCAN_MAX_INTERVAL;
|
||||||
static uint32_t SCAN_INIT_INTERVAL;
|
static uint32_t SCAN_INIT_INTERVAL;
|
||||||
|
|
||||||
@ -141,9 +143,9 @@ struct scan_survey {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct scan_survey_results {
|
struct scan_survey_results {
|
||||||
struct scan_survey survey_2_4[14];
|
struct scan_survey survey_2_4[15];
|
||||||
struct scan_survey survey_5[196];
|
struct scan_survey survey_5[197];
|
||||||
struct scan_survey survey_6[233];
|
struct scan_survey survey_6[234];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct scan_results {
|
struct scan_results {
|
||||||
@ -1343,8 +1345,8 @@ static bool scan_parse_bss_information_elements(struct scan_bss *bss,
|
|||||||
iter.len + 2);
|
iter.len + 2);
|
||||||
break;
|
break;
|
||||||
case IE_TYPE_BSS_LOAD:
|
case IE_TYPE_BSS_LOAD:
|
||||||
if (ie_parse_bss_load(&iter, NULL, &bss->utilization,
|
if (ie_parse_bss_load(&iter, &bss->sta_count,
|
||||||
NULL) < 0)
|
&bss->utilization, NULL) < 0)
|
||||||
l_warn("Unable to parse BSS Load IE for "
|
l_warn("Unable to parse BSS Load IE for "
|
||||||
MAC, MAC_STR(bss->addr));
|
MAC, MAC_STR(bss->addr));
|
||||||
else
|
else
|
||||||
@ -1681,10 +1683,77 @@ static struct scan_bss *scan_parse_result(struct l_genl_msg *msg,
|
|||||||
return bss;
|
return bss;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void scan_bss_compute_rank(struct scan_bss *bss)
|
static double scan_legacy_utilization_factor(uint8_t utilization)
|
||||||
{
|
{
|
||||||
static const double RANK_HIGH_UTILIZATION_FACTOR = 0.8;
|
static const double RANK_HIGH_UTILIZATION_FACTOR = 0.8;
|
||||||
static const double RANK_LOW_UTILIZATION_FACTOR = 1.2;
|
static const double RANK_LOW_UTILIZATION_FACTOR = 1.2;
|
||||||
|
|
||||||
|
if (utilization >= 192)
|
||||||
|
return RANK_HIGH_UTILIZATION_FACTOR;
|
||||||
|
else if (utilization <= 63)
|
||||||
|
return RANK_LOW_UTILIZATION_FACTOR;
|
||||||
|
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double scan_get_load_factors(uint8_t utilization, uint16_t sta_count)
|
||||||
|
{
|
||||||
|
double n;
|
||||||
|
double factor = 1.0;
|
||||||
|
/*
|
||||||
|
* The exponential decay table has 64 entries (0 <= n <= 63) which range
|
||||||
|
* from 1.0 to 0.28. For the utilization and station count factors we
|
||||||
|
* likely don't want to adjust the rank so drastically (potentially a
|
||||||
|
* 78% reduction in the worse case) so cap the index at 30 which equates
|
||||||
|
* to ~64% at the worst case.
|
||||||
|
*/
|
||||||
|
static const uint8_t LOAD_DECAY_OFFSET = 30;
|
||||||
|
|
||||||
|
if (!RANK_HIGH_UTILIZATION) {
|
||||||
|
/*
|
||||||
|
* To maintain existing behavior, if the utilization factor is
|
||||||
|
* unset (default) fall back to the static thresholds and
|
||||||
|
* factor weights.
|
||||||
|
*/
|
||||||
|
factor = scan_legacy_utilization_factor(utilization);
|
||||||
|
goto sta_count;
|
||||||
|
} else if (utilization < RANK_HIGH_UTILIZATION)
|
||||||
|
goto sta_count;
|
||||||
|
|
||||||
|
/* Map the utilization threshold -> 255 to rankmod_table indexes */
|
||||||
|
if (L_WARN_ON(!util_linear_map(utilization, RANK_HIGH_UTILIZATION,
|
||||||
|
255, 0, LOAD_DECAY_OFFSET, &n)))
|
||||||
|
goto sta_count;
|
||||||
|
|
||||||
|
factor = util_exponential_decay(n);
|
||||||
|
|
||||||
|
sta_count:
|
||||||
|
if (!RANK_HIGH_STATION_COUNT || sta_count < RANK_HIGH_STATION_COUNT)
|
||||||
|
return factor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The station count is a uint16 so in theory it could be excessively
|
||||||
|
* large. In practice APs generally can't handle anywhere near this so
|
||||||
|
* put a cap at 255. If at some time in the future APs begin to handle
|
||||||
|
* this level of capacity we could increase this.
|
||||||
|
*
|
||||||
|
* TODO: A warning is used here to make this visible. If we see cases
|
||||||
|
* where this is happening we may need to give this another look.
|
||||||
|
*/
|
||||||
|
if (L_WARN_ON(sta_count > 255))
|
||||||
|
sta_count = 255;
|
||||||
|
|
||||||
|
if (L_WARN_ON(!util_linear_map(sta_count, RANK_HIGH_STATION_COUNT,
|
||||||
|
255, 0, LOAD_DECAY_OFFSET, &n)))
|
||||||
|
return factor;
|
||||||
|
|
||||||
|
factor *= util_exponential_decay(n);
|
||||||
|
|
||||||
|
return factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scan_bss_compute_rank(struct scan_bss *bss)
|
||||||
|
{
|
||||||
static const double RANK_HIGH_SNR_FACTOR = 1.2;
|
static const double RANK_HIGH_SNR_FACTOR = 1.2;
|
||||||
static const double RANK_LOW_SNR_FACTOR = 0.8;
|
static const double RANK_LOW_SNR_FACTOR = 0.8;
|
||||||
double rank;
|
double rank;
|
||||||
@ -1708,12 +1777,8 @@ static void scan_bss_compute_rank(struct scan_bss *bss)
|
|||||||
rank *= RANK_6G_FACTOR;
|
rank *= RANK_6G_FACTOR;
|
||||||
|
|
||||||
/* Rank loaded APs lower and lightly loaded APs higher */
|
/* Rank loaded APs lower and lightly loaded APs higher */
|
||||||
if (bss->have_utilization) {
|
if (bss->have_utilization)
|
||||||
if (bss->utilization >= 192)
|
rank *= scan_get_load_factors(bss->utilization, bss->sta_count);
|
||||||
rank *= RANK_HIGH_UTILIZATION_FACTOR;
|
|
||||||
else if (bss->utilization <= 63)
|
|
||||||
rank *= RANK_LOW_UTILIZATION_FACTOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bss->have_snr) {
|
if (bss->have_snr) {
|
||||||
if (bss->snr <= 15)
|
if (bss->snr <= 15)
|
||||||
@ -2089,9 +2154,10 @@ static void scan_get_results(struct scan_context *sc, struct scan_request *sr,
|
|||||||
results->bss_list = l_queue_new();
|
results->bss_list = l_queue_new();
|
||||||
results->freqs = freqs;
|
results->freqs = freqs;
|
||||||
|
|
||||||
if (scan_survey(results))
|
/* If there is no scan request (external scan), just get the results */
|
||||||
|
if (sr && scan_survey(results))
|
||||||
return;
|
return;
|
||||||
else
|
else if (sr)
|
||||||
l_warn("failed to start a scan survey");
|
l_warn("failed to start a scan survey");
|
||||||
|
|
||||||
get_results(results);
|
get_results(results);
|
||||||
@ -2120,6 +2186,22 @@ static void scan_wiphy_watch(struct wiphy *wiphy,
|
|||||||
if (!sr)
|
if (!sr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the regdom update finished with GET_SCAN/GET_SURVEY in flight
|
||||||
|
* don't try and get the results again and allow those calls to finish.
|
||||||
|
* For the non-6ghz case this has no downside as the results should not
|
||||||
|
* differ.
|
||||||
|
*
|
||||||
|
* If 6ghz was enabled by this regdom update there is still not much we
|
||||||
|
* can do since the scan itself is already completed. Appending to the
|
||||||
|
* command list won't do anything.
|
||||||
|
*
|
||||||
|
* TODO: Handle the 6ghz case by checking for this case in get_scan_done
|
||||||
|
* and continuing to iterate the sr->cmds array.
|
||||||
|
*/
|
||||||
|
if (sc->get_scan_cmd_id || sc->get_survey_cmd_id)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This update did not allow 6GHz, or the original request was
|
* This update did not allow 6GHz, or the original request was
|
||||||
* not expecting 6GHz. The periodic scan should now be ended.
|
* not expecting 6GHz. The periodic scan should now be ended.
|
||||||
@ -2582,6 +2664,20 @@ static int scan_init(void)
|
|||||||
if (SCAN_MAX_INTERVAL > UINT16_MAX)
|
if (SCAN_MAX_INTERVAL > UINT16_MAX)
|
||||||
SCAN_MAX_INTERVAL = UINT16_MAX;
|
SCAN_MAX_INTERVAL = UINT16_MAX;
|
||||||
|
|
||||||
|
if (!l_settings_get_uint(config, "Rank", "HighUtilizationThreshold",
|
||||||
|
&RANK_HIGH_UTILIZATION))
|
||||||
|
RANK_HIGH_UTILIZATION = 0;
|
||||||
|
|
||||||
|
if (L_WARN_ON(RANK_HIGH_UTILIZATION > 255))
|
||||||
|
RANK_HIGH_UTILIZATION = 255;
|
||||||
|
|
||||||
|
if (!l_settings_get_uint(config, "Rank", "HighStationCountThreshold",
|
||||||
|
&RANK_HIGH_STATION_COUNT))
|
||||||
|
RANK_HIGH_STATION_COUNT = 0;
|
||||||
|
|
||||||
|
if (L_WARN_ON(RANK_HIGH_STATION_COUNT > 255))
|
||||||
|
RANK_HIGH_STATION_COUNT = 255;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ struct scan_bss {
|
|||||||
uint8_t ssid[SSID_MAX_SIZE];
|
uint8_t ssid[SSID_MAX_SIZE];
|
||||||
uint8_t ssid_len;
|
uint8_t ssid_len;
|
||||||
uint8_t utilization;
|
uint8_t utilization;
|
||||||
|
uint16_t sta_count;
|
||||||
uint8_t cc[3];
|
uint8_t cc[3];
|
||||||
uint16_t rank;
|
uint16_t rank;
|
||||||
uint64_t time_stamp;
|
uint64_t time_stamp;
|
||||||
|
504
src/station.c
504
src/station.c
@ -63,6 +63,7 @@
|
|||||||
#include "src/eap.h"
|
#include "src/eap.h"
|
||||||
#include "src/eap-tls-common.h"
|
#include "src/eap-tls-common.h"
|
||||||
#include "src/storage.h"
|
#include "src/storage.h"
|
||||||
|
#include "src/pmksa.h"
|
||||||
|
|
||||||
#define STATION_RECENT_NETWORK_LIMIT 5
|
#define STATION_RECENT_NETWORK_LIMIT 5
|
||||||
#define STATION_RECENT_FREQS_LIMIT 5
|
#define STATION_RECENT_FREQS_LIMIT 5
|
||||||
@ -74,6 +75,11 @@ static uint32_t roam_retry_interval;
|
|||||||
static bool anqp_disabled;
|
static bool anqp_disabled;
|
||||||
static bool supports_arp_evict_nocarrier;
|
static bool supports_arp_evict_nocarrier;
|
||||||
static bool supports_ndisc_evict_nocarrier;
|
static bool supports_ndisc_evict_nocarrier;
|
||||||
|
static bool supports_drop_gratuitous_arp;
|
||||||
|
static bool supports_drop_unsolicited_na;
|
||||||
|
static bool supports_ipv4_drop_unicast_in_l2_multicast;
|
||||||
|
static bool supports_ipv6_drop_unicast_in_l2_multicast;
|
||||||
|
static bool pmksa_disabled;
|
||||||
static struct watchlist event_watches;
|
static struct watchlist event_watches;
|
||||||
static uint32_t known_networks_watch;
|
static uint32_t known_networks_watch;
|
||||||
static uint32_t allowed_bands;
|
static uint32_t allowed_bands;
|
||||||
@ -102,7 +108,6 @@ struct station {
|
|||||||
struct l_queue *owe_hidden_scan_ids;
|
struct l_queue *owe_hidden_scan_ids;
|
||||||
|
|
||||||
/* Roaming related members */
|
/* Roaming related members */
|
||||||
struct timespec roam_min_time;
|
|
||||||
struct l_timeout *roam_trigger_timeout;
|
struct l_timeout *roam_trigger_timeout;
|
||||||
uint32_t roam_scan_id;
|
uint32_t roam_scan_id;
|
||||||
uint8_t preauth_bssid[6];
|
uint8_t preauth_bssid[6];
|
||||||
@ -128,6 +133,12 @@ struct station {
|
|||||||
|
|
||||||
uint64_t last_roam_scan;
|
uint64_t last_roam_scan;
|
||||||
|
|
||||||
|
struct l_queue *affinities;
|
||||||
|
unsigned int affinity_watch;
|
||||||
|
char *affinity_client;
|
||||||
|
|
||||||
|
struct handshake_state *hs;
|
||||||
|
|
||||||
bool preparing_roam : 1;
|
bool preparing_roam : 1;
|
||||||
bool roam_scan_full : 1;
|
bool roam_scan_full : 1;
|
||||||
bool signal_low : 1;
|
bool signal_low : 1;
|
||||||
@ -144,6 +155,55 @@ struct anqp_entry {
|
|||||||
uint32_t pending;
|
uint32_t pending;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rather than sorting BSS's purely based on ranking a higher level grouping
|
||||||
|
* is used. The purpose of this higher order grouping is the consider the BSS's
|
||||||
|
* roam blacklist status. The roam blacklist is a "soft" blacklist in that we
|
||||||
|
* still should connect to these BSS's if they are the only "good" option.
|
||||||
|
* The question here is: what makes a BSS "good" vs "bad".
|
||||||
|
*
|
||||||
|
* For an initial (probably naive) approach here we can use the
|
||||||
|
* RoamThreshold[5G] which indicates the signal level that would not
|
||||||
|
* be of an acceptable connection quality. BSS can then be sorted either
|
||||||
|
* above or below this threshold. Within each of these groups a BSS may be
|
||||||
|
* blacklisted, meaning it should get sorted lower on the list compared to
|
||||||
|
* others within the same group.
|
||||||
|
*
|
||||||
|
* This sorting is achieved by extending rank to a uint32_t where the first 16
|
||||||
|
* bits are the standard rank calculated by scan.c. Above that bits can be
|
||||||
|
* reserved for this higher level grouping:
|
||||||
|
*
|
||||||
|
* Bit 16 indicates the BSS is not blacklisted
|
||||||
|
* Bit 17 indicates the BSS is above the critical signal threshold
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define ABOVE_THRESHOLD_BIT 17
|
||||||
|
#define NOT_BLACKLISTED_BIT 16
|
||||||
|
|
||||||
|
static uint32_t evaluate_bss_group_rank(const uint8_t *addr, uint32_t freq,
|
||||||
|
int16_t signal_strength, uint16_t rank)
|
||||||
|
{
|
||||||
|
int signal = signal_strength / 100;
|
||||||
|
bool roam_blacklist;
|
||||||
|
bool good_signal;
|
||||||
|
uint32_t rank_out = (uint32_t) rank;
|
||||||
|
|
||||||
|
if (blacklist_contains_bss(addr, BLACKLIST_REASON_CONNECT_FAILED))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
roam_blacklist = blacklist_contains_bss(addr,
|
||||||
|
BLACKLIST_REASON_ROAM_REQUESTED);
|
||||||
|
good_signal = signal >= netdev_get_low_signal_threshold(freq);
|
||||||
|
|
||||||
|
if (good_signal)
|
||||||
|
set_bit(&rank_out, ABOVE_THRESHOLD_BIT);
|
||||||
|
|
||||||
|
if (!roam_blacklist)
|
||||||
|
set_bit(&rank_out, NOT_BLACKLISTED_BIT);
|
||||||
|
|
||||||
|
return rank_out;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used as entries for the roam list since holding scan_bss pointers directly
|
* Used as entries for the roam list since holding scan_bss pointers directly
|
||||||
* from station->bss_list is not 100% safe due to the possibility of the
|
* from station->bss_list is not 100% safe due to the possibility of the
|
||||||
@ -151,13 +211,13 @@ struct anqp_entry {
|
|||||||
*/
|
*/
|
||||||
struct roam_bss {
|
struct roam_bss {
|
||||||
uint8_t addr[6];
|
uint8_t addr[6];
|
||||||
uint16_t rank;
|
uint32_t rank;
|
||||||
int32_t signal_strength;
|
int32_t signal_strength;
|
||||||
bool ft_failed: 1;
|
bool ft_failed: 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct roam_bss *roam_bss_from_scan_bss(const struct scan_bss *bss,
|
static struct roam_bss *roam_bss_from_scan_bss(const struct scan_bss *bss,
|
||||||
uint16_t rank)
|
uint32_t rank)
|
||||||
{
|
{
|
||||||
struct roam_bss *rbss = l_new(struct roam_bss, 1);
|
struct roam_bss *rbss = l_new(struct roam_bss, 1);
|
||||||
|
|
||||||
@ -418,7 +478,8 @@ static void station_print_scan_bss(const struct scan_bss *bss)
|
|||||||
ptr += sprintf(ptr, ", snr: %d", bss->snr);
|
ptr += sprintf(ptr, ", snr: %d", bss->snr);
|
||||||
|
|
||||||
if (bss->have_utilization)
|
if (bss->have_utilization)
|
||||||
ptr += sprintf(ptr, ", load: %u/255", bss->utilization);
|
ptr += sprintf(ptr, ", load: %u/255, clients: %u",
|
||||||
|
bss->utilization, bss->sta_count);
|
||||||
|
|
||||||
l_debug("Processing BSS '%s' with SSID: %s, freq: %u, rank: %u, "
|
l_debug("Processing BSS '%s' with SSID: %s, freq: %u, rank: %u, "
|
||||||
"strength: %i, data_rate: %u.%u%s",
|
"strength: %i, data_rate: %u.%u%s",
|
||||||
@ -449,6 +510,14 @@ static const char *station_get_bss_path(struct station *station,
|
|||||||
return __network_path_append_bss(network_path, bss);
|
return __network_path_append_bss(network_path, bss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool match_bss_path(const void *data, const void *user_data)
|
||||||
|
{
|
||||||
|
const char *path1 = data;
|
||||||
|
const char *path2 = user_data;
|
||||||
|
|
||||||
|
return !strcmp(path1, path2);
|
||||||
|
}
|
||||||
|
|
||||||
static bool station_unregister_bss(struct station *station,
|
static bool station_unregister_bss(struct station *station,
|
||||||
struct scan_bss *bss)
|
struct scan_bss *bss)
|
||||||
{
|
{
|
||||||
@ -457,6 +526,8 @@ static bool station_unregister_bss(struct station *station,
|
|||||||
if (L_WARN_ON(!path))
|
if (L_WARN_ON(!path))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
l_queue_remove_if(station->affinities, match_bss_path, path);
|
||||||
|
|
||||||
return l_dbus_unregister_object(dbus_get_bus(), path);
|
return l_dbus_unregister_object(dbus_get_bus(), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1137,6 +1208,7 @@ static int station_build_handshake_rsn(struct handshake_state *hs,
|
|||||||
struct network *network,
|
struct network *network,
|
||||||
struct scan_bss *bss)
|
struct scan_bss *bss)
|
||||||
{
|
{
|
||||||
|
struct netdev *netdev = netdev_find(hs->ifindex);
|
||||||
const struct l_settings *settings = iwd_get_config();
|
const struct l_settings *settings = iwd_get_config();
|
||||||
enum security security = network_get_security(network);
|
enum security security = network_get_security(network);
|
||||||
bool add_mde = false;
|
bool add_mde = false;
|
||||||
@ -1147,6 +1219,7 @@ static int station_build_handshake_rsn(struct handshake_state *hs,
|
|||||||
uint8_t *ap_ie;
|
uint8_t *ap_ie;
|
||||||
bool disable_ocv;
|
bool disable_ocv;
|
||||||
enum band_freq band;
|
enum band_freq band;
|
||||||
|
struct pmksa *pmksa;
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
memset(&info, 0, sizeof(info));
|
||||||
|
|
||||||
@ -1280,6 +1353,17 @@ build_ie:
|
|||||||
IE_CIPHER_IS_GCMP_CCMP(info.pairwise_ciphers))
|
IE_CIPHER_IS_GCMP_CCMP(info.pairwise_ciphers))
|
||||||
info.extended_key_id = true;
|
info.extended_key_id = true;
|
||||||
|
|
||||||
|
if (IE_AKM_IS_SAE(info.akm_suites) && !pmksa_disabled) {
|
||||||
|
pmksa = pmksa_cache_get(netdev_get_address(netdev), bss->addr,
|
||||||
|
bss->ssid, bss->ssid_len,
|
||||||
|
info.akm_suites);
|
||||||
|
if (pmksa) {
|
||||||
|
handshake_state_set_pmksa(hs, pmksa);
|
||||||
|
info.num_pmkids = 1;
|
||||||
|
info.pmkids = hs->pmksa->pmkid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* RSN takes priority */
|
/* RSN takes priority */
|
||||||
if (bss->rsne) {
|
if (bss->rsne) {
|
||||||
ap_ie = bss->rsne;
|
ap_ie = bss->rsne;
|
||||||
@ -1364,7 +1448,7 @@ static struct handshake_state *station_handshake_setup(struct station *station,
|
|||||||
handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
|
handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* It can't hurt to try the FILS IP Address Assigment independent of
|
* It can't hurt to try the FILS IP Address Assignment independent of
|
||||||
* which auth-proto is actually used.
|
* which auth-proto is actually used.
|
||||||
*/
|
*/
|
||||||
if (station->netconfig && netconfig_get_fils_ip_req(station->netconfig,
|
if (station->netconfig && netconfig_get_fils_ip_req(station->netconfig,
|
||||||
@ -1376,7 +1460,7 @@ static struct handshake_state *station_handshake_setup(struct station *station,
|
|||||||
return hs;
|
return hs;
|
||||||
|
|
||||||
not_supported:
|
not_supported:
|
||||||
handshake_state_free(hs);
|
handshake_state_unref(hs);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1628,8 +1712,11 @@ static void station_set_drop_neighbor_discovery(struct station *station,
|
|||||||
{
|
{
|
||||||
char *v = value ? "1" : "0";
|
char *v = value ? "1" : "0";
|
||||||
|
|
||||||
|
if (supports_drop_gratuitous_arp)
|
||||||
sysfs_write_ipv4_setting(netdev_get_name(station->netdev),
|
sysfs_write_ipv4_setting(netdev_get_name(station->netdev),
|
||||||
"drop_gratuitous_arp", v);
|
"drop_gratuitous_arp", v);
|
||||||
|
|
||||||
|
if (supports_drop_unsolicited_na)
|
||||||
sysfs_write_ipv6_setting(netdev_get_name(station->netdev),
|
sysfs_write_ipv6_setting(netdev_get_name(station->netdev),
|
||||||
"drop_unsolicited_na", v);
|
"drop_unsolicited_na", v);
|
||||||
}
|
}
|
||||||
@ -1639,8 +1726,11 @@ static void station_set_drop_unicast_l2_multicast(struct station *station,
|
|||||||
{
|
{
|
||||||
char *v = value ? "1" : "0";
|
char *v = value ? "1" : "0";
|
||||||
|
|
||||||
|
if (supports_ipv4_drop_unicast_in_l2_multicast)
|
||||||
sysfs_write_ipv4_setting(netdev_get_name(station->netdev),
|
sysfs_write_ipv4_setting(netdev_get_name(station->netdev),
|
||||||
"drop_unicast_in_l2_multicast", v);
|
"drop_unicast_in_l2_multicast", v);
|
||||||
|
|
||||||
|
if (supports_ipv6_drop_unicast_in_l2_multicast)
|
||||||
sysfs_write_ipv6_setting(netdev_get_name(station->netdev),
|
sysfs_write_ipv6_setting(netdev_get_name(station->netdev),
|
||||||
"drop_unicast_in_l2_multicast", v);
|
"drop_unicast_in_l2_multicast", v);
|
||||||
}
|
}
|
||||||
@ -1740,6 +1830,18 @@ static void station_enter_state(struct station *station,
|
|||||||
station_set_evict_nocarrier(station, true);
|
station_set_evict_nocarrier(station, true);
|
||||||
station_set_drop_neighbor_discovery(station, false);
|
station_set_drop_neighbor_discovery(station, false);
|
||||||
station_set_drop_unicast_l2_multicast(station, false);
|
station_set_drop_unicast_l2_multicast(station, false);
|
||||||
|
|
||||||
|
if (station->affinity_watch) {
|
||||||
|
l_dbus_remove_watch(dbus_get_bus(),
|
||||||
|
station->affinity_watch);
|
||||||
|
station->affinity_watch = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (station->hs) {
|
||||||
|
handshake_state_unref(station->hs);
|
||||||
|
station->hs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case STATION_STATE_DISCONNECTING:
|
case STATION_STATE_DISCONNECTING:
|
||||||
case STATION_STATE_NETCONFIG:
|
case STATION_STATE_NETCONFIG:
|
||||||
@ -1747,6 +1849,15 @@ static void station_enter_state(struct station *station,
|
|||||||
case STATION_STATE_ROAMING:
|
case STATION_STATE_ROAMING:
|
||||||
case STATION_STATE_FT_ROAMING:
|
case STATION_STATE_FT_ROAMING:
|
||||||
case STATION_STATE_FW_ROAMING:
|
case STATION_STATE_FW_ROAMING:
|
||||||
|
l_dbus_property_changed(dbus, netdev_get_path(station->netdev),
|
||||||
|
IWD_STATION_INTERFACE, "ConnectedAccessPoint");
|
||||||
|
|
||||||
|
if (station->affinity_watch) {
|
||||||
|
l_dbus_remove_watch(dbus_get_bus(),
|
||||||
|
station->affinity_watch);
|
||||||
|
station->affinity_watch = 0;
|
||||||
|
}
|
||||||
|
|
||||||
station_set_evict_nocarrier(station, false);
|
station_set_evict_nocarrier(station, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1820,7 +1931,6 @@ static void station_roam_state_clear(struct station *station)
|
|||||||
station->preparing_roam = false;
|
station->preparing_roam = false;
|
||||||
station->roam_scan_full = false;
|
station->roam_scan_full = false;
|
||||||
station->signal_low = false;
|
station->signal_low = false;
|
||||||
station->roam_min_time.tv_sec = 0;
|
|
||||||
station->netconfig_after_roam = false;
|
station->netconfig_after_roam = false;
|
||||||
station->last_roam_scan = 0;
|
station->last_roam_scan = 0;
|
||||||
|
|
||||||
@ -1877,7 +1987,7 @@ static void station_reset_connection_state(struct station *station)
|
|||||||
/*
|
/*
|
||||||
* Perform this step last since calling network_disconnected() might
|
* Perform this step last since calling network_disconnected() might
|
||||||
* result in the removal of the network (for example if provisioning
|
* result in the removal of the network (for example if provisioning
|
||||||
* a new hidden network fails with an incorrect pasword).
|
* a new hidden network fails with an incorrect password).
|
||||||
*/
|
*/
|
||||||
if (station->state == STATION_STATE_CONNECTED ||
|
if (station->state == STATION_STATE_CONNECTED ||
|
||||||
station->state == STATION_STATE_CONNECTING ||
|
station->state == STATION_STATE_CONNECTING ||
|
||||||
@ -2105,7 +2215,8 @@ static void station_early_neighbor_report_cb(struct netdev *netdev, int err,
|
|||||||
&station->roam_freqs);
|
&station->roam_freqs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool station_can_fast_transition(struct handshake_state *hs,
|
static bool station_can_fast_transition(struct station *station,
|
||||||
|
struct handshake_state *hs,
|
||||||
struct scan_bss *bss)
|
struct scan_bss *bss)
|
||||||
{
|
{
|
||||||
uint16_t mdid;
|
uint16_t mdid;
|
||||||
@ -2133,6 +2244,16 @@ static bool station_can_fast_transition(struct handshake_state *hs,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FT-over-Air in its current form relies on CMD_REMAIN_ON_CHANNEL. Some
|
||||||
|
* drivers do not support this so only allow over-DS if this is the case
|
||||||
|
*/
|
||||||
|
if (!(hs->mde[4] & 1) &&
|
||||||
|
!wiphy_supports_cmd_offchannel(station->wiphy)) {
|
||||||
|
l_debug("FT-over-Air needs offchannel, using reassociation");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2434,9 +2555,12 @@ static void station_preauthenticate_cb(struct netdev *netdev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (station_transition_reassociate(station, bss, new_hs) < 0) {
|
if (station_transition_reassociate(station, bss, new_hs) < 0) {
|
||||||
handshake_state_free(new_hs);
|
handshake_state_unref(new_hs);
|
||||||
station_roam_failed(station);
|
station_roam_failed(station);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handshake_state_unref(station->hs);
|
||||||
|
station->hs = handshake_state_ref(new_hs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void station_transition_start(struct station *station);
|
static void station_transition_start(struct station *station);
|
||||||
@ -2596,7 +2720,7 @@ static bool station_try_next_transition(struct station *station,
|
|||||||
station->ap_directed_roaming = false;
|
station->ap_directed_roaming = false;
|
||||||
|
|
||||||
/* Can we use Fast Transition? */
|
/* Can we use Fast Transition? */
|
||||||
if (station_can_fast_transition(hs, bss) && !no_ft)
|
if (station_can_fast_transition(station, hs, bss) && !no_ft)
|
||||||
return station_fast_transition(station, bss);
|
return station_fast_transition(station, bss);
|
||||||
|
|
||||||
/* Non-FT transition */
|
/* Non-FT transition */
|
||||||
@ -2637,10 +2761,13 @@ static bool station_try_next_transition(struct station *station,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (station_transition_reassociate(station, bss, new_hs) < 0) {
|
if (station_transition_reassociate(station, bss, new_hs) < 0) {
|
||||||
handshake_state_free(new_hs);
|
handshake_state_unref(new_hs);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handshake_state_unref(station->hs);
|
||||||
|
station->hs = handshake_state_ref(new_hs);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2727,7 +2854,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
struct handshake_state *hs = netdev_get_handshake(station->netdev);
|
struct handshake_state *hs = netdev_get_handshake(station->netdev);
|
||||||
struct scan_bss *current_bss = station->connected_bss;
|
struct scan_bss *current_bss = station->connected_bss;
|
||||||
struct scan_bss *bss;
|
struct scan_bss *bss;
|
||||||
double cur_bss_rank = 0.0;
|
uint32_t cur_bss_group_rank = 0;
|
||||||
static const double RANK_FT_FACTOR = 1.3;
|
static const double RANK_FT_FACTOR = 1.3;
|
||||||
uint16_t mdid;
|
uint16_t mdid;
|
||||||
enum security orig_security, security;
|
enum security orig_security, security;
|
||||||
@ -2756,10 +2883,15 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
*/
|
*/
|
||||||
bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr);
|
bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr);
|
||||||
if (bss && !station->ap_directed_roaming) {
|
if (bss && !station->ap_directed_roaming) {
|
||||||
cur_bss_rank = bss->rank;
|
double cur_bss_rank = bss->rank;
|
||||||
|
|
||||||
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
|
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
|
||||||
cur_bss_rank *= RANK_FT_FACTOR;
|
cur_bss_rank *= RANK_FT_FACTOR;
|
||||||
|
|
||||||
|
cur_bss_group_rank = evaluate_bss_group_rank(bss->addr,
|
||||||
|
bss->frequency,
|
||||||
|
bss->signal_strength,
|
||||||
|
(uint16_t) cur_bss_rank);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2781,6 +2913,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
while ((bss = l_queue_pop_head(bss_list))) {
|
while ((bss = l_queue_pop_head(bss_list))) {
|
||||||
double rank;
|
double rank;
|
||||||
struct roam_bss *rbss;
|
struct roam_bss *rbss;
|
||||||
|
uint32_t group_rank;
|
||||||
|
|
||||||
station_print_scan_bss(bss);
|
station_print_scan_bss(bss);
|
||||||
|
|
||||||
@ -2802,7 +2935,8 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
if (network_can_connect_bss(network, bss) < 0)
|
if (network_can_connect_bss(network, bss) < 0)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
if (blacklist_contains_bss(bss->addr))
|
if (blacklist_contains_bss(bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED))
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
rank = bss->rank;
|
rank = bss->rank;
|
||||||
@ -2810,7 +2944,11 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
|
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
|
||||||
rank *= RANK_FT_FACTOR;
|
rank *= RANK_FT_FACTOR;
|
||||||
|
|
||||||
if (rank <= cur_bss_rank)
|
group_rank = evaluate_bss_group_rank(bss->addr, bss->frequency,
|
||||||
|
bss->signal_strength,
|
||||||
|
(uint16_t) rank);
|
||||||
|
|
||||||
|
if (group_rank <= cur_bss_group_rank)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2819,7 +2957,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
|
|||||||
*/
|
*/
|
||||||
station_update_roam_bss(station, bss);
|
station_update_roam_bss(station, bss);
|
||||||
|
|
||||||
rbss = roam_bss_from_scan_bss(bss, rank);
|
rbss = roam_bss_from_scan_bss(bss, group_rank);
|
||||||
|
|
||||||
l_queue_insert(station->roam_bss_list, rbss,
|
l_queue_insert(station->roam_bss_list, rbss,
|
||||||
roam_bss_rank_compare, NULL);
|
roam_bss_rank_compare, NULL);
|
||||||
@ -3041,20 +3179,33 @@ static void station_roam_trigger_cb(struct l_timeout *timeout, void *user_data)
|
|||||||
|
|
||||||
static void station_roam_timeout_rearm(struct station *station, int seconds)
|
static void station_roam_timeout_rearm(struct station *station, int seconds)
|
||||||
{
|
{
|
||||||
struct timespec now, min_timeout;
|
uint64_t remaining;
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
if (!station->roam_trigger_timeout)
|
||||||
|
goto new_timeout;
|
||||||
|
|
||||||
min_timeout = now;
|
/* If we can't get the remaining time just create a new timer */
|
||||||
min_timeout.tv_sec += seconds;
|
if (L_WARN_ON(!l_timeout_remaining(station->roam_trigger_timeout,
|
||||||
|
&remaining))) {
|
||||||
|
l_timeout_remove(station->roam_trigger_timeout);
|
||||||
|
goto new_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
if (station->roam_min_time.tv_sec < min_timeout.tv_sec ||
|
/* Our current timeout is less than the rearm, keep current */
|
||||||
(station->roam_min_time.tv_sec == min_timeout.tv_sec &&
|
if (l_time_before(remaining, seconds * L_USEC_PER_SEC)) {
|
||||||
station->roam_min_time.tv_nsec < min_timeout.tv_nsec))
|
l_debug("Keeping current roam timeout of %" PRIx64 " seconds",
|
||||||
station->roam_min_time = min_timeout;
|
l_time_to_secs(remaining));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
seconds = station->roam_min_time.tv_sec - now.tv_sec +
|
l_debug("Rescheduling roam timeout from %" PRIx64" to %d seconds",
|
||||||
(station->roam_min_time.tv_nsec > now.tv_nsec ? 1 : 0);
|
l_time_to_secs(remaining), seconds);
|
||||||
|
l_timeout_modify(station->roam_trigger_timeout, seconds);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
new_timeout:
|
||||||
|
l_debug("Arming new roam timer for %d seconds", seconds);
|
||||||
|
|
||||||
station->roam_trigger_timeout =
|
station->roam_trigger_timeout =
|
||||||
l_timeout_create(seconds, station_roam_trigger_cb,
|
l_timeout_create(seconds, station_roam_trigger_cb,
|
||||||
@ -3074,12 +3225,10 @@ static void station_ap_directed_roam(struct station *station,
|
|||||||
uint8_t req_mode;
|
uint8_t req_mode;
|
||||||
uint16_t dtimer;
|
uint16_t dtimer;
|
||||||
uint8_t valid_interval;
|
uint8_t valid_interval;
|
||||||
|
bool can_roam = !station_cannot_roam(station);
|
||||||
|
|
||||||
l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
|
l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
|
||||||
|
|
||||||
if (station_cannot_roam(station))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (station->state != STATION_STATE_CONNECTED) {
|
if (station->state != STATION_STATE_CONNECTED) {
|
||||||
l_debug("roam: unexpected AP directed roam -- ignore");
|
l_debug("roam: unexpected AP directed roam -- ignore");
|
||||||
return;
|
return;
|
||||||
@ -3143,8 +3292,13 @@ static void station_ap_directed_roam(struct station *station,
|
|||||||
* disassociating us. If either of these bits are set, set the
|
* disassociating us. If either of these bits are set, set the
|
||||||
* ap_directed_roaming flag. Otherwise still try roaming but don't
|
* ap_directed_roaming flag. Otherwise still try roaming but don't
|
||||||
* treat it any different than a normal roam.
|
* treat it any different than a normal roam.
|
||||||
|
*
|
||||||
|
* The only exception here is if we are in the middle of roaming
|
||||||
|
* (can_roam == false) since we cannot reliably know if the roam scan
|
||||||
|
* included frequencies from potential candidates in this request,
|
||||||
|
* forcing a roam in this case might result in unintended behavior.
|
||||||
*/
|
*/
|
||||||
if (req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT |
|
if (can_roam && req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT |
|
||||||
WNM_REQUEST_MODE_TERMINATION_IMMINENT |
|
WNM_REQUEST_MODE_TERMINATION_IMMINENT |
|
||||||
WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT))
|
WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT))
|
||||||
station->ap_directed_roaming = true;
|
station->ap_directed_roaming = true;
|
||||||
@ -3171,6 +3325,19 @@ static void station_ap_directed_roam(struct station *station,
|
|||||||
pos += url_len;
|
pos += url_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blacklist_add_bss(station->connected_bss->addr,
|
||||||
|
BLACKLIST_REASON_ROAM_REQUESTED);
|
||||||
|
station_debug_event(station, "ap-roam-blacklist-added");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validating the frame and blacklisting should still be done even if
|
||||||
|
* we are mid-roam. Its important to track the BSS requesting the
|
||||||
|
* transition so when the current roam completes IWD will be less likely
|
||||||
|
* to roam back to the current BSS.
|
||||||
|
*/
|
||||||
|
if (!can_roam)
|
||||||
|
return;
|
||||||
|
|
||||||
station->preparing_roam = true;
|
station->preparing_roam = true;
|
||||||
|
|
||||||
l_timeout_remove(station->roam_trigger_timeout);
|
l_timeout_remove(station->roam_trigger_timeout);
|
||||||
@ -3212,7 +3379,6 @@ static void station_ok_rssi(struct station *station)
|
|||||||
station->roam_trigger_timeout = NULL;
|
station->roam_trigger_timeout = NULL;
|
||||||
|
|
||||||
station->signal_low = false;
|
station->signal_low = false;
|
||||||
station->roam_min_time.tv_sec = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void station_event_roamed(struct station *station, struct scan_bss *new)
|
static void station_event_roamed(struct station *station, struct scan_bss *new)
|
||||||
@ -3310,12 +3476,53 @@ static bool station_retry_with_reason(struct station *station,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
blacklist_add_bss(station->connected_bss->addr);
|
blacklist_add_bss(station->connected_bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Network blacklist the BSS as well, since the timeout blacklist could
|
||||||
|
* be disabled
|
||||||
|
*/
|
||||||
|
network_blacklist_add(station->connected_network,
|
||||||
|
station->connected_bss);
|
||||||
|
|
||||||
try_next:
|
try_next:
|
||||||
return station_try_next_bss(station);
|
return station_try_next_bss(station);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool station_pmksa_fallback(struct station *station, uint16_t status)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* IEEE 802.11-2020 12.6.10.3 Cached PMKSAs and RSNA key management
|
||||||
|
*
|
||||||
|
* "If the Authenticator does not have a PMKSA for the PMKIDs in the
|
||||||
|
* (re)association request or the AKM does not match, its behavior
|
||||||
|
* depends on how the PMKSA was established. If SAE authentication was
|
||||||
|
* used to establish the PMKSA, then the AP shall reject (re)association
|
||||||
|
* by sending a (Re)Association Response frame with status code
|
||||||
|
* STATUS_INVALID_PMKID. Note that this allows the non-AP STA to fall
|
||||||
|
* back to full SAE authentication to establish another PMKSA"
|
||||||
|
*/
|
||||||
|
if (status != MMPDU_STATUS_CODE_INVALID_PMKID)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (L_WARN_ON(!station->hs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IE_AKM_IS_SAE(station->hs->akm_suite) || !station->hs->have_pmksa)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove the PMKSA from the handshake and return true to re-try the
|
||||||
|
* same BSS without PMKSA.
|
||||||
|
*/
|
||||||
|
handshake_state_remove_pmksa(station->hs);
|
||||||
|
|
||||||
|
station_debug_event(station, "pmksa-invalid-pmkid");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* A bit more concise for trying to fit these into 80 characters */
|
/* A bit more concise for trying to fit these into 80 characters */
|
||||||
#define IS_TEMPORARY_STATUS(code) \
|
#define IS_TEMPORARY_STATUS(code) \
|
||||||
((code) == MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH || \
|
((code) == MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH || \
|
||||||
@ -3326,6 +3533,10 @@ try_next:
|
|||||||
static bool station_retry_with_status(struct station *station,
|
static bool station_retry_with_status(struct station *station,
|
||||||
uint16_t status_code)
|
uint16_t status_code)
|
||||||
{
|
{
|
||||||
|
/* If PMKSA failed don't blacklist so we can retry this BSS */
|
||||||
|
if (station_pmksa_fallback(station, status_code))
|
||||||
|
goto try_next;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Certain Auth/Assoc failures should not cause a timeout blacklist.
|
* Certain Auth/Assoc failures should not cause a timeout blacklist.
|
||||||
* In these cases we want to only temporarily blacklist the BSS until
|
* In these cases we want to only temporarily blacklist the BSS until
|
||||||
@ -3336,12 +3547,19 @@ static bool station_retry_with_status(struct station *station,
|
|||||||
* specific BSS on our next attempt. There is currently no way to
|
* specific BSS on our next attempt. There is currently no way to
|
||||||
* obtain that IE, but this should be done in the future.
|
* obtain that IE, but this should be done in the future.
|
||||||
*/
|
*/
|
||||||
if (IS_TEMPORARY_STATUS(status_code))
|
if (!IS_TEMPORARY_STATUS(status_code))
|
||||||
|
blacklist_add_bss(station->connected_bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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,
|
network_blacklist_add(station->connected_network,
|
||||||
station->connected_bss);
|
station->connected_bss);
|
||||||
else
|
|
||||||
blacklist_add_bss(station->connected_bss->addr);
|
|
||||||
|
|
||||||
|
try_next:
|
||||||
iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code);
|
iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code);
|
||||||
|
|
||||||
return station_try_next_bss(station);
|
return station_try_next_bss(station);
|
||||||
@ -3426,7 +3644,8 @@ static void station_connect_cb(struct netdev *netdev, enum netdev_result result,
|
|||||||
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case NETDEV_RESULT_OK:
|
case NETDEV_RESULT_OK:
|
||||||
blacklist_remove_bss(station->connected_bss->addr);
|
blacklist_remove_bss(station->connected_bss->addr,
|
||||||
|
BLACKLIST_REASON_CONNECT_FAILED);
|
||||||
station_connect_ok(station);
|
station_connect_ok(station);
|
||||||
return;
|
return;
|
||||||
case NETDEV_RESULT_DISCONNECTED:
|
case NETDEV_RESULT_DISCONNECTED:
|
||||||
@ -3558,14 +3777,17 @@ static void station_packets_lost(struct station *station, uint32_t num_pkts)
|
|||||||
l_debug("Too many roam attempts in %u second timeframe, "
|
l_debug("Too many roam attempts in %u second timeframe, "
|
||||||
"delaying roam", LOSS_ROAM_RATE_LIMIT);
|
"delaying roam", LOSS_ROAM_RATE_LIMIT);
|
||||||
|
|
||||||
if (station->roam_trigger_timeout)
|
|
||||||
return;
|
|
||||||
|
|
||||||
station_roam_timeout_rearm(station, LOSS_ROAM_RATE_LIMIT);
|
station_roam_timeout_rearm(station, LOSS_ROAM_RATE_LIMIT);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (station->roam_trigger_timeout) {
|
||||||
|
l_debug("canceling roam timer to roam immediately");
|
||||||
|
l_timeout_remove(station->roam_trigger_timeout);
|
||||||
|
station->roam_trigger_timeout = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
station_start_roam(station);
|
station_start_roam(station);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3578,9 +3800,6 @@ static void station_beacon_lost(struct station *station)
|
|||||||
|
|
||||||
station_debug_event(station, "beacon-loss-roam");
|
station_debug_event(station, "beacon-loss-roam");
|
||||||
|
|
||||||
if (station->roam_trigger_timeout)
|
|
||||||
return;
|
|
||||||
|
|
||||||
station_roam_timeout_rearm(station, LOSS_ROAM_RATE_LIMIT);
|
station_roam_timeout_rearm(station, LOSS_ROAM_RATE_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3659,6 +3878,15 @@ int __station_connect_network(struct station *station, struct network *network,
|
|||||||
struct handshake_state *hs;
|
struct handshake_state *hs;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we already have a handshake_state ref this is due to a retry,
|
||||||
|
* unref that now
|
||||||
|
*/
|
||||||
|
if (station->hs) {
|
||||||
|
handshake_state_unref(station->hs);
|
||||||
|
station->hs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (station->netconfig && !netconfig_load_settings(
|
if (station->netconfig && !netconfig_load_settings(
|
||||||
station->netconfig,
|
station->netconfig,
|
||||||
network_get_settings(network)))
|
network_get_settings(network)))
|
||||||
@ -3672,7 +3900,7 @@ int __station_connect_network(struct station *station, struct network *network,
|
|||||||
station_netdev_event,
|
station_netdev_event,
|
||||||
station_connect_cb, station);
|
station_connect_cb, station);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
handshake_state_free(hs);
|
handshake_state_unref(hs);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3685,6 +3913,7 @@ int __station_connect_network(struct station *station, struct network *network,
|
|||||||
|
|
||||||
station->connected_bss = bss;
|
station->connected_bss = bss;
|
||||||
station->connected_network = network;
|
station->connected_network = network;
|
||||||
|
station->hs = handshake_state_ref(hs);
|
||||||
|
|
||||||
if (station->state != state)
|
if (station->state != state)
|
||||||
station_enter_state(station, state);
|
station_enter_state(station, state);
|
||||||
@ -4520,6 +4749,163 @@ static bool station_property_get_state(struct l_dbus *dbus,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool station_property_get_affinities(struct l_dbus *dbus,
|
||||||
|
struct l_dbus_message *message,
|
||||||
|
struct l_dbus_message_builder *builder,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct station *station = user_data;
|
||||||
|
const struct l_queue_entry *e;
|
||||||
|
|
||||||
|
if (!station->connected_network)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
l_dbus_message_builder_enter_array(builder, "o");
|
||||||
|
|
||||||
|
for (e = l_queue_get_entries(station->affinities); e; e = e->next) {
|
||||||
|
const char *path = e->data;
|
||||||
|
|
||||||
|
l_dbus_message_builder_append_basic(builder, 'o', path);
|
||||||
|
}
|
||||||
|
|
||||||
|
l_dbus_message_builder_leave_array(builder);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void station_affinity_disconnected_cb(struct l_dbus *dbus,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct station *station = user_data;
|
||||||
|
|
||||||
|
l_dbus_remove_watch(dbus_get_bus(), station->affinity_watch);
|
||||||
|
|
||||||
|
l_debug("client that set affinity has disconnected");
|
||||||
|
|
||||||
|
/* The client who set the affinity disconnected, raise the threshold */
|
||||||
|
netdev_raise_signal_threshold(station->netdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void station_affinity_watch_destroy(void *user_data)
|
||||||
|
{
|
||||||
|
struct station *station = user_data;
|
||||||
|
bool empty = l_queue_length(station->affinities) == 0;
|
||||||
|
|
||||||
|
station->affinity_watch = 0;
|
||||||
|
|
||||||
|
l_free(station->affinity_client);
|
||||||
|
station->affinity_client = NULL;
|
||||||
|
|
||||||
|
l_queue_clear(station->affinities, l_free);
|
||||||
|
|
||||||
|
if (!empty)
|
||||||
|
l_dbus_property_changed(dbus_get_bus(),
|
||||||
|
netdev_get_path(station->netdev),
|
||||||
|
IWD_STATION_INTERFACE, "Affinities");
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct l_dbus_message *station_property_set_affinities(
|
||||||
|
struct l_dbus *dbus,
|
||||||
|
struct l_dbus_message *message,
|
||||||
|
struct l_dbus_message_iter *new_value,
|
||||||
|
l_dbus_property_complete_cb_t complete,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct station *station = user_data;
|
||||||
|
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 = NULL;
|
||||||
|
struct scan_bss *new_bss = NULL;
|
||||||
|
struct scan_bss *old_bss = NULL;
|
||||||
|
bool lower_threshold = false;
|
||||||
|
|
||||||
|
if (!station->connected_network)
|
||||||
|
return dbus_error_not_connected(message);
|
||||||
|
|
||||||
|
if (wiphy_supports_firmware_roam(station->wiphy))
|
||||||
|
return dbus_error_not_supported(message);
|
||||||
|
|
||||||
|
if (station->affinity_watch &&
|
||||||
|
strcmp(station->affinity_client, sender)) {
|
||||||
|
l_warn("Only one client may manage Affinities property");
|
||||||
|
return dbus_error_permission_denied(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!l_dbus_message_iter_get_variant(new_value, "ao", &array))
|
||||||
|
return dbus_error_invalid_args(message);
|
||||||
|
|
||||||
|
/* Get first entry, there should be only one */
|
||||||
|
if (!l_dbus_message_iter_next_entry(&array, &new_path))
|
||||||
|
return dbus_error_invalid_args(message);
|
||||||
|
|
||||||
|
if (l_dbus_message_iter_next_entry(&array, &new_path))
|
||||||
|
return dbus_error_invalid_args(message);
|
||||||
|
|
||||||
|
old_path = l_queue_peek_head(station->affinities);
|
||||||
|
if (old_path)
|
||||||
|
old_bss = l_dbus_object_get_data(dbus_get_bus(),
|
||||||
|
old_path, IWD_BSS_INTERFACE);
|
||||||
|
if (new_path)
|
||||||
|
new_bss = l_dbus_object_get_data(dbus,
|
||||||
|
new_path, IWD_BSS_INTERFACE);
|
||||||
|
|
||||||
|
/* Either the same path, or both arrays are empty */
|
||||||
|
if (old_bss == new_bss) {
|
||||||
|
complete(dbus, message, NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: For now only allow the affinities array to contain a single
|
||||||
|
* value, the connected BSS path. Any other values will be
|
||||||
|
* rejected. This could change in the future.
|
||||||
|
*/
|
||||||
|
if (new_bss && new_bss != station->connected_bss)
|
||||||
|
return dbus_error_invalid_args(message);
|
||||||
|
|
||||||
|
l_queue_clear(station->affinities, l_free);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adding a new BSS, create a watch for this DBus client so if it
|
||||||
|
* disconnects we can clear the affinities list
|
||||||
|
*/
|
||||||
|
if (new_path) {
|
||||||
|
l_queue_push_head(station->affinities, l_strdup(new_path));
|
||||||
|
|
||||||
|
lower_threshold = true;
|
||||||
|
|
||||||
|
if (!station->affinity_watch) {
|
||||||
|
station->affinity_client = l_strdup(sender);
|
||||||
|
station->affinity_watch = l_dbus_add_disconnect_watch(
|
||||||
|
dbus, sender,
|
||||||
|
station_affinity_disconnected_cb,
|
||||||
|
station,
|
||||||
|
station_affinity_watch_destroy);
|
||||||
|
}
|
||||||
|
/* The list was cleared, remove the watch */
|
||||||
|
} else if (station->affinity_watch)
|
||||||
|
l_dbus_remove_watch(dbus, station->affinity_watch);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If affinity was set to the current BSS, lower the roam threshold. If
|
||||||
|
* the connected BSS was not in the list raise the signal threshold.
|
||||||
|
* The threshold may already be raised, in which case netdev will detect
|
||||||
|
* this and do nothing.
|
||||||
|
*/
|
||||||
|
if (lower_threshold)
|
||||||
|
netdev_lower_signal_threshold(station->netdev);
|
||||||
|
else
|
||||||
|
netdev_raise_signal_threshold(station->netdev);
|
||||||
|
|
||||||
|
complete(dbus, message, NULL);
|
||||||
|
|
||||||
|
l_dbus_property_changed(dbus, netdev_get_path(station->netdev),
|
||||||
|
IWD_STATION_INTERFACE, "Affinities");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void station_foreach(station_foreach_func_t func, void *user_data)
|
void station_foreach(station_foreach_func_t func, void *user_data)
|
||||||
{
|
{
|
||||||
const struct l_queue_entry *entry;
|
const struct l_queue_entry *entry;
|
||||||
@ -4750,6 +5136,7 @@ static struct station *station_create(struct netdev *netdev)
|
|||||||
station_set_autoconnect(station, autoconnect);
|
station_set_autoconnect(station, autoconnect);
|
||||||
|
|
||||||
station->roam_bss_list = l_queue_new();
|
station->roam_bss_list = l_queue_new();
|
||||||
|
station->affinities = l_queue_new();
|
||||||
|
|
||||||
return station;
|
return station;
|
||||||
}
|
}
|
||||||
@ -4820,6 +5207,11 @@ static void station_free(struct station *station)
|
|||||||
l_queue_destroy(station->owe_hidden_scan_ids, NULL);
|
l_queue_destroy(station->owe_hidden_scan_ids, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (station->hs) {
|
||||||
|
handshake_state_unref(station->hs);
|
||||||
|
station->hs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
station_roam_state_clear(station);
|
station_roam_state_clear(station);
|
||||||
|
|
||||||
l_queue_destroy(station->networks_sorted, NULL);
|
l_queue_destroy(station->networks_sorted, NULL);
|
||||||
@ -4842,6 +5234,11 @@ static void station_free(struct station *station)
|
|||||||
|
|
||||||
l_queue_destroy(station->roam_bss_list, l_free);
|
l_queue_destroy(station->roam_bss_list, l_free);
|
||||||
|
|
||||||
|
if (station->affinity_watch)
|
||||||
|
l_dbus_remove_watch(dbus_get_bus(), station->affinity_watch);
|
||||||
|
|
||||||
|
l_queue_destroy(station->affinities, l_free);
|
||||||
|
|
||||||
l_free(station);
|
l_free(station);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4878,6 +5275,9 @@ static void station_setup_interface(struct l_dbus_interface *interface)
|
|||||||
station_property_get_scanning, NULL);
|
station_property_get_scanning, NULL);
|
||||||
l_dbus_interface_property(interface, "State", 0, "s",
|
l_dbus_interface_property(interface, "State", 0, "s",
|
||||||
station_property_get_state, NULL);
|
station_property_get_state, NULL);
|
||||||
|
l_dbus_interface_property(interface, "Affinities", 0, "ao",
|
||||||
|
station_property_get_affinities,
|
||||||
|
station_property_set_affinities);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void station_destroy_interface(void *user_data)
|
static void station_destroy_interface(void *user_data)
|
||||||
@ -5467,7 +5867,7 @@ static void add_frame_watches(struct netdev *netdev)
|
|||||||
*/
|
*/
|
||||||
frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
|
frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
|
||||||
action_ap_roam_prefix, sizeof(action_ap_roam_prefix),
|
action_ap_roam_prefix, sizeof(action_ap_roam_prefix),
|
||||||
ap_roam_frame_event,
|
false, ap_roam_frame_event,
|
||||||
L_UINT_TO_PTR(netdev_get_ifindex(netdev)), NULL);
|
L_UINT_TO_PTR(netdev_get_ifindex(netdev)), NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5575,6 +5975,10 @@ static int station_init(void)
|
|||||||
&anqp_disabled))
|
&anqp_disabled))
|
||||||
anqp_disabled = true;
|
anqp_disabled = true;
|
||||||
|
|
||||||
|
if (!l_settings_get_bool(iwd_get_config(), "General", "DisablePMKSA",
|
||||||
|
&pmksa_disabled))
|
||||||
|
pmksa_disabled = false;
|
||||||
|
|
||||||
if (!netconfig_enabled())
|
if (!netconfig_enabled())
|
||||||
l_info("station: Network configuration is disabled.");
|
l_info("station: Network configuration is disabled.");
|
||||||
|
|
||||||
@ -5582,6 +5986,16 @@ static int station_init(void)
|
|||||||
"arp_evict_nocarrier");
|
"arp_evict_nocarrier");
|
||||||
supports_ndisc_evict_nocarrier = sysfs_supports_ipv6_setting("all",
|
supports_ndisc_evict_nocarrier = sysfs_supports_ipv6_setting("all",
|
||||||
"ndisc_evict_nocarrier");
|
"ndisc_evict_nocarrier");
|
||||||
|
supports_drop_gratuitous_arp = sysfs_supports_ipv4_setting("all",
|
||||||
|
"drop_gratuitous_arp");
|
||||||
|
supports_drop_unsolicited_na = sysfs_supports_ipv6_setting("all",
|
||||||
|
"drop_unsolicited_na");
|
||||||
|
supports_ipv4_drop_unicast_in_l2_multicast =
|
||||||
|
sysfs_supports_ipv4_setting("all",
|
||||||
|
"drop_unicast_in_l2_multicast");
|
||||||
|
supports_ipv6_drop_unicast_in_l2_multicast =
|
||||||
|
sysfs_supports_ipv6_setting("all",
|
||||||
|
"drop_unicast_in_l2_multicast");
|
||||||
|
|
||||||
watchlist_init(&event_watches, NULL);
|
watchlist_init(&event_watches, NULL);
|
||||||
|
|
||||||
|
@ -500,6 +500,13 @@ int __storage_decrypt(struct l_settings *settings, const char *ssid,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* It should likely be far larger than this, but that will get caught
|
||||||
|
* later when reloading the decrypted data.
|
||||||
|
*/
|
||||||
|
if (elen < 16)
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AES-SIV automatically verifies the IV (16 bytes) and returns only
|
* AES-SIV automatically verifies the IV (16 bytes) and returns only
|
||||||
* the decrypted data portion. We add one here for the NULL terminator
|
* the decrypted data portion. We add one here for the NULL terminator
|
||||||
@ -535,7 +542,7 @@ int __storage_decrypt(struct l_settings *settings, const char *ssid,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Load decrypted data into existing settings. This is not how the API
|
* Load decrypted data into existing settings. This is not how the API
|
||||||
* is indended to be used (since this could result in duplicate groups)
|
* is intended to be used (since this could result in duplicate groups)
|
||||||
* but since the Security group was just removed and EncryptedSecurity
|
* but since the Security group was just removed and EncryptedSecurity
|
||||||
* should only contain a Security group its safe to use it this way.
|
* should only contain a Security group its safe to use it this way.
|
||||||
*/
|
*/
|
||||||
@ -596,7 +603,7 @@ struct l_settings *storage_network_open(enum security type, const char *ssid)
|
|||||||
struct l_settings *settings;
|
struct l_settings *settings;
|
||||||
_auto_(l_free) char *path = NULL;
|
_auto_(l_free) char *path = NULL;
|
||||||
|
|
||||||
if (ssid == NULL)
|
if (!ssid)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
path = storage_get_network_file_path(type, ssid);
|
path = storage_get_network_file_path(type, ssid);
|
||||||
@ -623,7 +630,7 @@ int storage_network_touch(enum security type, const char *ssid)
|
|||||||
char *path;
|
char *path;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (ssid == NULL)
|
if (!ssid)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
path = storage_get_network_file_path(type, ssid);
|
path = storage_get_network_file_path(type, ssid);
|
||||||
|
@ -234,8 +234,7 @@ static bool can_read_data(struct l_io *io, void *user_data)
|
|||||||
if (len < 0)
|
if (len < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
|
||||||
struct nl_pktinfo *pktinfo;
|
struct nl_pktinfo *pktinfo;
|
||||||
|
|
||||||
if (cmsg->cmsg_level != SOL_NETLINK)
|
if (cmsg->cmsg_level != SOL_NETLINK)
|
||||||
|
65
src/util.c
65
src/util.c
@ -276,7 +276,7 @@ bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix_out,
|
|||||||
/* 'i' will be at most INET_ADDRSTRLEN - 1 */
|
/* 'i' will be at most INET_ADDRSTRLEN - 1 */
|
||||||
l_strlcpy(no_prefix, ip, i + 1);
|
l_strlcpy(no_prefix, ip, i + 1);
|
||||||
|
|
||||||
/* Check if IP preceeding prefix is valid */
|
/* Check if IP preceding prefix is valid */
|
||||||
if (inet_pton(AF_INET, no_prefix, &ia) != 1 || ia.s_addr == 0)
|
if (inet_pton(AF_INET, no_prefix, &ia) != 1 || ia.s_addr == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -312,6 +312,36 @@ bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix_out,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Linearly maps @value (expected to be within range @a_start and @a_end) to
|
||||||
|
* a new value between @b_start and @b_end.
|
||||||
|
*
|
||||||
|
* Returns: false if
|
||||||
|
* @value is not between @a_start and @a_end
|
||||||
|
* @a_start/@a_end or @b_start/@b_end are equal.
|
||||||
|
*/
|
||||||
|
bool util_linear_map(double value, double a_start, double a_end,
|
||||||
|
double b_start, double b_end, double *mapped_value)
|
||||||
|
{
|
||||||
|
/* Check value is within a's range */
|
||||||
|
if (a_start < a_end) {
|
||||||
|
if (value < a_start || value > a_end)
|
||||||
|
return false;
|
||||||
|
} else if (a_start > a_end) {
|
||||||
|
if (value > a_start || value < a_end)
|
||||||
|
return false;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (b_start == b_end)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*mapped_value = b_start + (((b_end - b_start) / (a_end - a_start)) *
|
||||||
|
(value - a_start));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct scan_freq_set {
|
struct scan_freq_set {
|
||||||
uint16_t channels_2ghz;
|
uint16_t channels_2ghz;
|
||||||
struct l_uintset *channels_5ghz;
|
struct l_uintset *channels_5ghz;
|
||||||
@ -464,6 +494,11 @@ static void scan_channels_foreach(uint32_t channel, void *user_data)
|
|||||||
uint32_t freq;
|
uint32_t freq;
|
||||||
|
|
||||||
freq = band_channel_to_freq(channel, channels_data->band);
|
freq = band_channel_to_freq(channel, channels_data->band);
|
||||||
|
if (!freq) {
|
||||||
|
l_warn("invalid channel %u for band %u", channel,
|
||||||
|
channels_data->band);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
channels_data->func(freq, channels_data->user_data);
|
channels_data->func(freq, channels_data->user_data);
|
||||||
}
|
}
|
||||||
@ -601,3 +636,31 @@ struct scan_freq_set *scan_freq_set_clone(const struct scan_freq_set *set,
|
|||||||
|
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* First 64 entries calculated by 1 / pow(n, 0.3) for n >= 1 */
|
||||||
|
static const double rankmod_table[] = {
|
||||||
|
1.0000000000, 0.8122523964, 0.7192230933, 0.6597539554,
|
||||||
|
0.6170338627, 0.5841906811, 0.5577898253, 0.5358867313,
|
||||||
|
0.5172818580, 0.5011872336, 0.4870596972, 0.4745102806,
|
||||||
|
0.4632516708, 0.4530661223, 0.4437850034, 0.4352752816,
|
||||||
|
0.4274303178, 0.4201634287, 0.4134032816, 0.4070905315,
|
||||||
|
0.4011753236, 0.3956154062, 0.3903746872, 0.3854221125,
|
||||||
|
0.3807307877, 0.3762772797, 0.3720410580, 0.3680040435,
|
||||||
|
0.3641502401, 0.3604654325, 0.3569369365, 0.3535533906,
|
||||||
|
0.3503045821, 0.3471812999, 0.3441752105, 0.3412787518,
|
||||||
|
0.3384850430, 0.3357878061, 0.3331812996, 0.3306602598,
|
||||||
|
0.3282198502, 0.3258556179, 0.3235634544, 0.3213395618,
|
||||||
|
0.3191804229, 0.3170827751, 0.3150435863, 0.3130600345,
|
||||||
|
0.3111294892, 0.3092494947, 0.3074177553, 0.3056321221,
|
||||||
|
0.3038905808, 0.3021912409, 0.3005323264, 0.2989121662,
|
||||||
|
0.2973291870, 0.2957819051, 0.2942689208, 0.2927889114,
|
||||||
|
0.2913406263, 0.2899228820, 0.2885345572, 0.2871745887,
|
||||||
|
};
|
||||||
|
|
||||||
|
double util_exponential_decay(unsigned int n)
|
||||||
|
{
|
||||||
|
if (n >= L_ARRAY_SIZE(rankmod_table))
|
||||||
|
return rankmod_table[L_ARRAY_SIZE(rankmod_table) - 1];
|
||||||
|
|
||||||
|
return rankmod_table[n];
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user