3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2025-07-05 23:17:25 +02:00

Compare commits

..

54 Commits
3.5 ... master

Author SHA1 Message Date
Marcel Holtmann
601d9b0e02 Release 3.9 2025-06-14 12:10:16 +02:00
James Prestwood
f209e00dde doc: add note about timeouts to Network.Connect()
Since netconfig is now part of the Connect() call from a DBus
perspective add a note indicating that this method has the potential
to take a very long time if there are issues with DHCP.
2025-06-05 10:02:28 -05:00
James Prestwood
86523b0597 auto-t: update several tests to work with netconfig refactor
Since the method return to Connect() and ConnectBssid() come after
netconfig some tests needed to be updated since they were waiting
for the method return before continuing. For timeout-based tests
specifically this caused them to fail since before they expected
the return to come before the connection was actually completed.
2025-06-05 10:02:23 -05:00
James Prestwood
85a2637fc5 auto-t: allow configurable DBus timeout/callbacks on connect{_bssid}
Let the caller specify the method timeout if there is an expectation
that it could take a long time.

For the conventional connect call (not the "bssid" debug variant) let
them pass their own callback handlers. This is useful if we don't
want to wait for the connect call to finish, but later get some
indication that it did finish either successfully or not.
2025-06-05 10:02:13 -05:00
James Prestwood
2f991918b1 station: include netconfig as part of the BSS retry logic
A netconfig failure results in a failed connection which restarts
autoconnect and prevents IWD from retrying the connection on any
other BSS's within the network as a whole. When autoconnect restarts
IWD will scan and choose the "best" BSS which is likely the same as
the prior attempt. If that BSS is somehow misconfigured as far as
DHCP goes, it will likely fail indefinitely and in turn cause IWD to
retry indefinitely.

To improve this netconfig has been adopted into the IWD's BSS retry
logic. If netconfig fails this will not result in IWD transitioning
to a disconnected state, and instead the BSS will be network
blacklisted and the next will be tried. Only once all BSS's have been
tried will IWD go into a disconnected state and start autoconnect
over.
2025-06-05 10:02:02 -05:00
James Prestwood
5b5a9b60fb station: fix DBus reply for Connect() with netconfig
When netconfig is enabled the DBus reply was being sent in
station_connect_ok(), before netconfig had even started. This would
result in a call to Connect() succeeding from a DBus perspective but
really netconfig still needed to complete before IWD transitioned
to a connected state.

Fixes: 72e7d3ceb83d ("station: Handle NETCONFIG_EVENT_FAILED")
2025-06-05 10:01:45 -05:00
James Prestwood
5287809043 network: make clearing network blacklist a separate operation
This adds a new API network_clear_blacklist() and removes this
functionality from network_connected(). This is done to support BSS
iteration when netconfig is enabled. Since a call to
network_connected() will happen prior to netconfig completing we
cannot clear the blacklist until netconfig has either passed or
failed.
2025-06-05 10:01:42 -05:00
James Prestwood
ea9ff2dcaf sae: prevent groups 21, 25, and 26 from being used
These groups are not working reliably and until that is fixed they
should be disabled.
2025-06-05 09:59:34 -05:00
James Prestwood
9dce36fe3d sae: check return on sae_send_commit()
If this fails, in some cases, -EAGAIN would be returned up to netdev
which would then assume a retry would be done automatically. This
would not in fact happen since it was an internal SAE failure which
would result in the connect method return to never get sent.

Now if sae_send_commit() fails, return -EPROTO which will cause
netdev to fail the connection.
2025-06-05 09:59:16 -05:00
Marcel Holtmann
c4718a5355 unit: Update precheck for WSC PBC test cases 2025-05-28 19:29:47 +02:00
James Prestwood
c9c8790ff2 netdev: support handling NL80211_CMD_ASSOC_COMEBACK
A BSS can temporarily reject associations and provide a delay that
the station should wait for before retrying. This is useful when
sane values are used, but taking it to the extreme an AP could
potentially request the client wait UINT32_MAX TU's which equates
to 49 days.

Either due to a bug, or worse by design, the kernel will wait for
however long that timeout is. Luckily the kernel also sends an event
to userspace with the amount of time it will be waiting. To guard
against excessive timeouts IWD will now handle this event and enforce
a maximum allowed value. If the timeout exceeds this IWD will
deauthenticate.
2025-05-28 12:06:43 -05:00
James Prestwood
d135bfc4b8 nl80211util: support parsing NL80211_ATTR_TIMEOUT 2025-05-28 12:06:36 -05:00
James Prestwood
e269beadba nl80211cmd: add NL80211_CMD_ASSOC_COMEBACK 2025-05-28 12:06:28 -05:00
James Prestwood
3e55fc855a auto-t: use renamed InitialAccessPointBusyTimeout 2025-05-19 16:38:54 -05:00
James Prestwood
c8d9936f9d station: utilize the AP_BUSY blacklist for denied auth/assoc
Specifically for the NO_MORE_STAS reason code, add the BSS to the
(now renamed) AP_BUSY blacklist to avoid roaming to this BSS for
the near future.

Since we are now handling individual reason codes differently the
whole IS_TEMPORARY_STATUS macro was removed and replaced with a
case statement.
2025-05-19 16:38:28 -05:00
James Prestwood
79940956ef docs: replace/deprecate InitialRoamRequestedTimeout
This is being replaced by InitialAccessPointBusyTimeout but will
still be supported until full removal.
2025-05-19 16:38:13 -05:00
James Prestwood
9f98c6c3c8 blacklist: rename ROAM_REQUESTED to AP_BUSY
The initial pass of this feature only envisioned BSS transition
management frames as the trigger to "roam blacklist" a BSS, hence
the original name. But some APs actually utilize status codes that
also indicate to the stations that they are busy, or not able to
handle more connections. This directly aligns with the original
motivation of the "roam blacklist" series and these events should
also trigger this type of blacklist.

First, since we will be applying this blacklist to cases other
than being told to roam, rename this reason code internally to
BLACKLIST_REASON_AP_BUSY. The config option is also being renamed
to [Blacklist].InitialAccessPointBusyTimeout while also supporting
the old config option, but warning that it is deprecated.
2025-05-19 16:37:13 -05:00
James Prestwood
93eef7b02d ap: implement pre-scanning to "unlock" frequencies
Some drivers/hardware are more strict about limiting use of
certain frequencies on startup until the regulatory domain has
been set. For most cards the only way to set the regulatory domain
is to scan and see BSS's nearby that advertise the country they
reside in.

This is particularly important for AP mode since AP's are always
emitting radiation from beacons and will not start until the desired
frequency is both enabled and allows IR. To make this process
seamless in IWD we will first check that the desired frequency is
enabled/IR and if not issue a scan to (hopefully) get the regulatory
domain set.
2025-05-19 16:36:45 -05:00
Marcel Holtmann
26ed1f8b9f Release 3.8 2025-05-07 13:41:49 +02:00
Marcel Holtmann
243db1d256 build: Require at least version 0.77 when building with external ELL 2025-05-07 12:47:42 +02:00
Marcel Holtmann
5224b0b0e7 unit: Use test precheck feature to check for kernel capabilities 2025-05-07 09:54:22 +02:00
Marcel Holtmann
3267d356d2 unit: The precheck function also takes test data as parameter 2025-05-05 20:41:56 +02:00
Marcel Holtmann
78f4e6240e unit: Use new precheck feature for storage encryption test 2025-05-05 19:37:34 +02:00
Marcel Holtmann
36b1086f60 Release 3.7 2025-05-04 19:34:32 +02:00
Marcel Holtmann
266eb405f2 build: Add test-storage to ignore list 2025-05-04 19:34:15 +02:00
James Prestwood
0a8e646231 eap: initialize vendor_id/vendor_type to zero
This fixes a compiler warning, specifically on ARM/GCC 12.2.0

src/eap.c: In function ‘eap_rx_packet’:
src/eap.c:419:57: error: ‘vendor_type’ may be used uninitialized [-Werror=maybe-uninitialized]
  419 |         (type == EAP_TYPE_EXPANDED && vendor_id == (id) && vendor_type == (t))
      |                                                         ^~
src/eap.c:429:18: note: ‘vendor_type’ was declared here
  429 |         uint32_t vendor_type;
      |                  ^~~~~~~~~~~
src/eap.c:419:49: error: ‘vendor_id’ may be used uninitialized [-Werror=maybe-uninitialized]
  419 |         (type == EAP_TYPE_EXPANDED && vendor_id == (id) && vendor_type == (t))
      |                                                 ^~
src/eap.c:428:18: note: ‘vendor_id’ was declared here
  428 |         uint32_t vendor_id;
      |                  ^~~~~~~~~
2025-05-02 12:14:37 -05:00
James Prestwood
8ebc4780ea station: fix setting an empty affinities list
A prior patch broke this by checking the return of
l_dbus_message_iter_next_entry. This was really subtle but the logic
actually relied on _not_ checking that return in order to handle
empty lists.

Instead of reverting the logic was adapted/commented to make it more
clear what the API expects from DBus. If list contains at least one
value the first element path will get set, if it contains zero
values "new_path" will be set to NULL which will then cause the
list to be cleared later on.

This both fixes the regression, and makes it clear that a zero
element list is supported and handled.
2025-04-23 09:42:48 -05:00
Marcel Holtmann
4ded663e68 unit: Fix country code assignment for test case
CC       unit/test-p2p.o
unit/test-p2p.c:344:36: error: initializer-string for array of ‘char’ truncates NUL terminator but destination lacks ‘nonstring’ attribute (4 chars into 3 available) [-Werror=unterminated-string-initialization]
  344 |                         .country = "XX\x04",
      |                                    ^~~~~~~~
2025-04-19 22:49:53 +02:00
James Prestwood
c00bc3a065 eap-mschapv2: Fix leak of state->user on error path
Fixes: 6dc5d2c3ecb6 ("eap-mschapv2: Load credentials obtained from agent")
2025-04-16 14:58:00 -05:00
James Prestwood
f469db8a95 station: check return when advancing iterator
Fixes: f4ec1ee509fc ("station: add Affinities DBus property")
2025-04-16 14:58:00 -05:00
James Prestwood
c3a27354ff unit: add test-storage
For now, a single test for __storage_decrypt that ensures an
invalid length fails as expected.
2025-04-16 14:58:00 -05:00
James Prestwood
d927fd07c1 storage: add length check in __storage_decrypt
The length of EncryptedSecurity was assumed to be at least 16 bytes
and anything less would underflow the length to l_malloc.

Fixes: 01cd8587606b ("storage: implement network profile encryption")
2025-04-16 14:58:00 -05:00
James Prestwood
8dff156eb6 monitor: add size check for interworking IE parsing
Fixes: e0c9b68467fa ("monitor: parse/print HS2.0/WFA IEs")
2025-04-16 14:58:00 -05:00
James Prestwood
e5c41a8024 unit: add test for duplicate URI elements 2025-04-16 14:58:00 -05:00
James Prestwood
603d6b2881 dpp-util: fail on duplicate values in URI
The MAC and version elements weren't super critical but the channel
and bootstrapping key elements would result in memory leaks if there
were duplicates.

This patch now will not allow duplicate elements in the URI.

Fixes: f7f602e1b1e7 ("dpp-util: add URI parsing")
2025-04-16 14:58:00 -05:00
James Prestwood
d1aa4009bc scan: fix out of bound array access for survey results
The survey arrays were exactly the number of valid channels for a
given band (e.g. 14 for 2.4GHz) but since channels start at 1 this
means that the last channel for a band would overflow the array.

Fixes: 35808debaefd ("scan: use GET_SURVEY for SNR calculation in ranking")
2025-04-16 14:58:00 -05:00
James Prestwood
3c5081c7a6 monitor: fix spelling Exausted -> Exhausted
Caught by codespell

Fixes: 83a2457550e7 ("monitor: add support for limiting PCAP size/count")
2025-04-16 14:58:00 -05:00
Marcel Holtmann
7d5bcd738b Release 3.6 2025-04-02 15:05:43 +02:00
James Prestwood
0a93c55552 netdev: implement PMKSA for fullmac drivers
Supporting PMKSA on fullmac drivers requires that we set the PMKSA
into the kernel as well as remove it. This can now be triggered
via the new PMKSA driver callbacks which are implemented and set
with this patch.
2025-04-01 11:15:03 -05:00
James Prestwood
7f9ea7640d handshake: use pmksa_cache_free 2025-04-01 11:14:40 -05:00
James Prestwood
c52d913f20 pmksa: add driver callbacks and pmksa_cache_free
In order to support fullmac drivers the PMKSA entries must be added
and removed from the kernel. To accomplish this a set of driver
callbacks will be added to the PMKSA module. In addition a new
pmksa_cache_free API will be added whos only purpose is to handle
the removal from the kernel.
2025-04-01 11:11:08 -05:00
James Prestwood
651b647570 netdev: remove/update some iwd_notice logs
The iwd_notice function was more meant for special purpose events
not general debug prints. For these error conditions we should be
using l_warn. For the informational "External Auth to SSID" log
we already print this information when connecting from station. In
addition there are logs when performing external auth so it should
be very obvious external auth is being used without this log.
2025-04-01 11:10:39 -05:00
James Prestwood
8cf9734d2b netdev: don't set CQM thresholds for fullmac cards
Since roaming is handled by the firmware setting CQM thresholds for
roaming is pointless.
2025-04-01 11:10:26 -05:00
James Prestwood
d70fbade44 netdev: fix invalid read after netdev_free
The netdev frame watches got cleaned up upon the interface going down
which works if the interface is simply being toggled but when IWD
shuts down it first shuts down the interface, then immediately frees
netdev. If a watched frame arrives immediately after that before the
interface shutdown callback it will reference netdev, which has been
freed.

Fix this by clearing out the frame watches in netdev_free.

==147== Invalid read of size 8
==147==    at 0x408ADB: netdev_neighbor_report_frame_event (netdev.c:4772)
==147==    by 0x467C75: frame_watch_unicast_notify (frame-xchg.c:234)
==147==    by 0x4E28F8: __notifylist_notify (notifylist.c:91)
==147==    by 0x4E2D37: l_notifylist_notify_matches (notifylist.c:204)
==147==    by 0x4A1388: process_unicast (genl.c:844)
==147==    by 0x4A1388: received_data (genl.c:972)
==147==    by 0x49D82F: io_callback (io.c:105)
==147==    by 0x49C93C: l_main_iterate (main.c:461)
==147==    by 0x49CA0B: l_main_run (main.c:508)
==147==    by 0x49CA0B: l_main_run (main.c:490)
==147==    by 0x49CC3F: l_main_run_with_signal (main.c:630)
==147==    by 0x4049EC: main (main.c:614)
2025-04-01 11:06:40 -05:00
James Prestwood
f0e515b6ff doc: document InitialRoamRequestedTimeout 2025-04-01 11:06:29 -05:00
James Prestwood
47ef40d645 auto-t: add tests for AP roam blacklisting 2025-04-01 11:06:21 -05:00
James Prestwood
9e10efbef5 station: roam blacklist AP even mid-roam
If an AP directed roam frame comes in while IWD is roaming its
still valuable to parse that frame and blacklist the BSS that
sent it.

This can happen most frequently during a roam scan while connected
to an overloaded BSS that is requesting IWD roams elsewhere.
2025-04-01 11:06:02 -05:00
James Prestwood
224afbb9ca station: roam blacklist BSS's, and consider when roaming
If the BSS is requesting IWD roam elsewhere add this BSS to the
blacklist using BLACKLIST_REASON_ROAM_REQUESTED. This will lower
the chances of IWD roaming/connecting back to this BSS in the
future.

This then allows IWD to consider this blacklist state when picking
a roam candidate. Its undesireable to fully ban a roam blacklisted
BSS, so some additional sorting logic has been added. Prior to
comparing based on rank, BSS's will be sorted into two higher level
groups:

Above Threshold - BSS is above the RoamThreshold
Below Threshold - BSS is below the RoamThreshold

Within each of these groups the BSS may be roam blacklisted which
will position it at the bottom of the list within its respecitve
group.
2025-04-01 11:05:22 -05:00
James Prestwood
bf69e6210c netdev: add netdev_get_low_signal_threshold 2025-04-01 10:55:10 -05:00
James Prestwood
258482d509 blacklist: add new blacklist reason, ROAM_REQUESTED
This adds a new (less severe) blacklist reason as well as an option
to configure the timeout. This blacklist reason will be used in cases
where a BSS has requested IWD roam elsewhere. At that time a new
blacklist entry will be added which will be used along with some
other criteria to determine if IWD should connect/roam to that BSS
again.

Now that we have multiple blacklist reasons there may be situations
where a blacklist entry already exists but with a different reason.
This is going to be handled by the reason severity. Since we have
just two reasons we will treat a connection failure as most severe
and a roam requested as less severe. This leaves us with two
possible situations:

1. BSS is roam blacklisted, then gets connection blacklisted:
   The reason will be "promoted" to connection blacklisted.

2. BSS is connection blacklisted, then gets roam blacklisted:
   The blacklist request will be ignored
2025-04-01 10:54:04 -05:00
James Prestwood
1caad4ca88 blacklist: fix pruning to remove the entry if its expired
When pruning the list check_if_expired was comparing to the maximum
amount of time a BSS can be blacklisted, not if the current time had
exceeded the expirationt time. This results in blacklist entries
hanging around longer than they should, which would result in them
poentially being blacklisted even longer if there was another reason
to blacklist in the future.

Instead on prune check the actual expiration and remove the entry if
its expired. Doing this removes the need to check any of the times
in blacklist_contains_bss since prune will remove any expired entries
correctly.
2025-04-01 10:53:18 -05:00
James Prestwood
59464a0ca4 blacklist: include a blacklist reason
To both prepare for some new blacklisting behavior and allow for
easier consolidation of the network-specific blacklist include a
reason enum for each entry. This allows IWD to differentiate
between multiple blacklist types. For now only the existing
"permanent" type is being added which prevents connections to that
BSS via autoconnect until it expires.
2025-04-01 10:52:25 -05:00
James Prestwood
93b25c87d6 auto-t: add test for disabling the timeout blacklist 2025-04-01 10:27:42 -05:00
James Prestwood
e971ef71d5 station: always add BSS to network blacklist on failure
Allowing the timeout blacklist to be disabled has introduced a bug
where a failed connection will not result in the BSS list to be
traversed. This causes IWD to retry the same BSS over and over which
be either a) have some issue preventing a connection or b) may simply
be unreachable/out of range.

This is because IWD was inherently relying on the timeout blacklist
to flag BSS's on failures. With it disabled there was nothing to tell
network_bss_select that we should skip the BSS and it would return
the same BSS indefinitely.

To fix this some of the blacklisting logic was re-worked in station.
Now, a BSS will always get network blacklisted upon a failure. This
allows network.c to traverse to the next BSS upon failure.

For auth/assoc failures we will then only timeout blacklist under
certain conditions, i.e. the status code was not in the temporary
list.

Fixes: 77639d2d452e ("blacklist: allow configuration to disable the blacklist")
2025-04-01 10:27:13 -05:00
52 changed files with 1443 additions and 467 deletions

1
.gitignore vendored
View File

@ -68,6 +68,7 @@ unit/test-dpp
unit/test-json unit/test-json
unit/test-nl80211util unit/test-nl80211util
unit/test-pmksa unit/test-pmksa
unit/test-storage
unit/cert-*.pem unit/cert-*.pem
unit/cert-*.csr unit/cert-*.csr
unit/cert-*.srl unit/cert-*.srl

View File

@ -1,3 +1,23 @@
ver 3.9:
Fix issue with Access Point mode and frequency unlocking.
Fix issue with network configuration and BSS retry logic.
Fix issue with handling busy notification from Access Point.
Fix issue with handling P-192, P-224 and P-521 for SAE.
ver 3.8:
Fix issue with handling unit tests and missing kernel features.
ver 3.7:
Fix issue with handling length of EncryptedSecurity.
Fix issue with handling empty affinities lists.
Fix issue with handling survey scanning results.
Fix issue with handling duplicate values in DPP URI.
ver 3.6:
Fix issue with handling blacklisting and roaming requests.
Fix issue with handling CQM thresholds for FullMAC devices.
Add support for PMKSA when using FullMAC devices.
ver 3.5: ver 3.5:
Add support for option to disable blacklist handling. Add support for option to disable blacklist handling.
Add support for option to disable SAE for broken drivers. Add support for option to disable SAE for broken drivers.

View File

@ -441,7 +441,7 @@ unit_tests += unit/test-cmac-aes \
unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \ unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \
unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \ unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \
unit/test-dpp unit/test-json unit/test-nl80211util \ unit/test-dpp unit/test-json unit/test-nl80211util \
unit/test-pmksa unit/test-pmksa unit/test-storage
endif endif
if CLIENT if CLIENT
@ -605,6 +605,11 @@ unit_test_nl80211util_LDADD = $(ell_ldadd)
unit_test_pmksa_SOURCES = unit/test-pmksa.c src/pmksa.c src/pmksa.h \ unit_test_pmksa_SOURCES = unit/test-pmksa.c src/pmksa.c src/pmksa.h \
src/module.h src/util.h src/module.h src/util.h
unit_test_pmksa_LDADD = $(ell_ldadd) unit_test_pmksa_LDADD = $(ell_ldadd)
unit_test_storage_SOURCES = unit/test-storage.c src/storage.c src/storage.h \
src/crypto.c src/crypto.h \
src/common.c src/common.h
unit_test_storage_LDADD = $(ell_ldadd)
endif endif
if CLIENT if CLIENT

View File

@ -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):

View File

@ -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

View File

@ -0,0 +1,6 @@
[General]
RoamThreshold=-72
CriticalRoamThreshold=-72
[Blacklist]
InitialAccessPointBusyTimeout=20

View 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)

View File

@ -0,0 +1,2 @@
[Security]
Passphrase=secret123

View File

@ -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):

View File

@ -0,0 +1,2 @@
[Blacklist]
InitialTimeout=0

View File

@ -91,16 +91,11 @@ class Test(unittest.TestCase):
# using the same static config. The new client's ACD client should # using the same static config. The new client's ACD client should
# detect an IP conflict and not allow the device to reach the # detect an IP conflict and not allow the device to reach the
# "connected" state although the DBus .Connect call will succeed. # "connected" state although the DBus .Connect call will succeed.
ordered_network.network_object.connect() with self.assertRaises(iwd.FailedEx):
self.assertEqual(dev2.state, iwd.DeviceState.connecting) ordered_network.network_object.connect(timeout=500)
try:
# We should either stay in "connecting" indefinitely or move to condition = 'obj.state == DeviceState.disconnected'
# "disconnecting" iwd_ns0_1.wait_for_object_condition(dev2, condition, max_wait=21)
condition = 'obj.state != DeviceState.connecting'
iwd_ns0_1.wait_for_object_condition(dev2, condition, max_wait=21)
self.assertEqual(dev2.state, iwd.DeviceState.disconnecting)
except TimeoutError:
dev2.disconnect()
iwd_ns0_1.unregister_psk_agent(psk_agent_ns0_1) iwd_ns0_1.unregister_psk_agent(psk_agent_ns0_1)
del dev2 del dev2

View File

@ -8,6 +8,8 @@ from iwd import PSKAgent
from iwd import NetworkType from iwd import NetworkType
class Test(unittest.TestCase): class Test(unittest.TestCase):
def connect_failure(self, ex):
self.failure_triggered = True
def test_netconfig_timeout(self): def test_netconfig_timeout(self):
IWD.copy_to_storage('autoconnect.psk', name='ap-ns1.psk') IWD.copy_to_storage('autoconnect.psk', name='ap-ns1.psk')
@ -27,23 +29,34 @@ class Test(unittest.TestCase):
condition = 'not obj.connected' condition = 'not obj.connected'
wd.wait_for_object_condition(ordered_network.network_object, condition) wd.wait_for_object_condition(ordered_network.network_object, condition)
ordered_network.network_object.connect() self.failure_triggered = False
condition = 'obj.state == DeviceState.connecting' # Set our error handler here so we can check if it fails
ordered_network.network_object.connect(
wait=False,
timeout=1000,
error_handler=self.connect_failure
)
# IWD should attempt to try both BSS's with both failing netconfig.
# Then the autoconnect list should be exhausted, and IWD should
# transition to a disconnected state, then proceed to full autoconnect.
device.wait_for_event("netconfig-failed", timeout=1000)
device.wait_for_event("netconfig-failed", timeout=1000)
device.wait_for_event("disconnected")
device.wait_for_event("autoconnect_full")
# The connect call should have failed
self.assertTrue(self.failure_triggered)
condition = "obj.scanning"
wd.wait_for_object_condition(device, condition)
condition = "not obj.scanning"
wd.wait_for_object_condition(device, condition) wd.wait_for_object_condition(device, condition)
device.wait_for_event("connecting (netconfig)") # IWD should attempt to connect, but it will of course fail again.
device.wait_for_event("netconfig-failed", timeout=1000)
# Netconfig should fail, and IWD should disconnect
from_condition = 'obj.state == DeviceState.connecting'
to_condition = 'obj.state == DeviceState.disconnecting'
wd.wait_for_object_change(device, from_condition, to_condition, max_wait=60)
# Autoconnect should then try again
condition = 'obj.state == DeviceState.connecting'
wd.wait_for_object_condition(device, condition)
device.wait_for_event("connecting (netconfig)")
device.disconnect() device.disconnect()
condition = 'obj.state == DeviceState.disconnected' condition = 'obj.state == DeviceState.disconnected'

View File

@ -19,7 +19,7 @@ class Test(unittest.TestCase):
device = wd.list_devices(1)[0] device = wd.list_devices(1)[0]
device.get_ordered_network('TestFT', full_scan=True) device.get_ordered_network('TestFT', full_scan=True)
device.connect_bssid(self.bss_hostapd[1].bssid) device.connect_bssid(self.bss_hostapd[1].bssid, wait=False)
self.bss_hostapd[1].wait_for_event(f'AP-STA-CONNECTED {device.address}') self.bss_hostapd[1].wait_for_event(f'AP-STA-CONNECTED {device.address}')
device.wait_for_event("connecting (netconfig)") device.wait_for_event("connecting (netconfig)")

View File

@ -112,8 +112,8 @@ class AsyncOpAbstract(object):
self._is_completed = True self._is_completed = True
self._exception = _convert_dbus_ex(ex) self._exception = _convert_dbus_ex(ex)
def _wait_for_async_op(self): def _wait_for_async_op(self, timeout=50):
ctx.non_block_wait(lambda s: s._is_completed, 30, self, exception=None) ctx.non_block_wait(lambda s: s._is_completed, timeout, self, exception=None)
self._is_completed = False self._is_completed = False
if self._exception is not None: if self._exception is not None:
@ -280,8 +280,15 @@ class StationDebug(IWDDBusAbstract):
def autoconnect(self): def autoconnect(self):
return self._properties['AutoConnect'] return self._properties['AutoConnect']
def connect_bssid(self, address): def connect_bssid(self, address, wait=True):
self._iface.ConnectBssid(dbus.ByteArray.fromhex(address.replace(':', ''))) self._iface.ConnectBssid(
dbus.ByteArray.fromhex(address.replace(':', '')),
reply_handler=self._success,
error_handler=self._failure
)
if wait:
self._wait_for_async_op()
def roam(self, address): def roam(self, address):
self._iface.Roam(dbus.ByteArray.fromhex(address.replace(':', ''))) self._iface.Roam(dbus.ByteArray.fromhex(address.replace(':', '')))
@ -870,8 +877,8 @@ class Device(IWDDBusAbstract):
def stop_adhoc(self): def stop_adhoc(self):
self._prop_proxy.Set(IWD_DEVICE_INTERFACE, 'Mode', 'station') self._prop_proxy.Set(IWD_DEVICE_INTERFACE, 'Mode', 'station')
def connect_bssid(self, address): def connect_bssid(self, address, wait=True):
self._station_debug.connect_bssid(address) self._station_debug.connect_bssid(address, wait=wait)
def roam(self, address): def roam(self, address):
self._station_debug.roam(address) self._station_debug.roam(address)
@ -999,7 +1006,7 @@ class Network(IWDDBusAbstract):
def extended_service_set(self): def extended_service_set(self):
return self._properties['ExtendedServiceSet'] return self._properties['ExtendedServiceSet']
def connect(self, wait=True): def connect(self, wait=True, timeout=50, reply_handler=None, error_handler=None):
''' '''
Connect to the network. Request the device implied by the object Connect to the network. Request the device implied by the object
path to connect to specified network. path to connect to specified network.
@ -1014,12 +1021,19 @@ class Network(IWDDBusAbstract):
@rtype: void @rtype: void
''' '''
if not reply_handler:
reply_handler = self._success
if not error_handler:
error_handler = self._failure
self._iface.Connect(dbus_interface=self._iface_name, self._iface.Connect(dbus_interface=self._iface_name,
reply_handler=self._success, reply_handler=reply_handler,
error_handler=self._failure) error_handler=error_handler,
timeout=timeout)
if wait: if wait:
self._wait_for_async_op() self._wait_for_async_op(timeout=timeout)
def __str__(self, prefix = ''): def __str__(self, prefix = ''):
return prefix + 'Network:\n' \ return prefix + 'Network:\n' \

View File

@ -1,5 +1,5 @@
AC_PREREQ([2.69]) AC_PREREQ([2.69])
AC_INIT([iwd],[3.5]) AC_INIT([iwd],[3.9])
AC_CONFIG_HEADERS(config.h) AC_CONFIG_HEADERS(config.h)
AC_CONFIG_AUX_DIR(build-aux) AC_CONFIG_AUX_DIR(build-aux)
@ -300,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.72" ell_min_version="0.77"
else else
ell_min_version="0.5" ell_min_version="0.5"
fi fi

View File

@ -11,6 +11,12 @@ Methods void Connect()
the object path to connect to specified network. the object path to connect to specified network.
Connecting to WEP networks is not supported. Connecting to WEP networks is not supported.
Note: When [General].EnableNetworkConfiguration is set
to true a call to Connect() has the potential to take
a significant amount of time. Specifically if DHCP is
either slow, or is unable to complete. The timeout for
DHCP is roughly 30 seconds per BSS.
Possible errors: net.connman.iwd.Aborted Possible errors: net.connman.iwd.Aborted
net.connman.iwd.Busy net.connman.iwd.Busy
net.connman.iwd.Failed net.connman.iwd.Failed

View File

@ -1915,7 +1915,7 @@ static void print_ie_interworking(unsigned int level,
size--; size--;
ptr++; ptr++;
if (!size) if (size < 2)
return; return;
/* /*
@ -7433,7 +7433,7 @@ static bool check_pcap(struct nlmon *nlmon, size_t next_size)
pcap_close(nlmon->pcap); pcap_close(nlmon->pcap);
/* Exausted the single PCAP file */ /* Exhausted the single PCAP file */
if (nlmon->max_files < 2) { if (nlmon->max_files < 2) {
printf("Reached maximum size of PCAP, exiting\n"); printf("Reached maximum size of PCAP, exiting\n");
nlmon->pcap = NULL; nlmon->pcap = NULL;

View File

@ -109,6 +109,8 @@ struct ap_state {
struct l_timeout *rekey_timeout; struct l_timeout *rekey_timeout;
unsigned int rekey_time; unsigned int rekey_time;
uint32_t pre_scan_cmd_id;
bool started : 1; bool started : 1;
bool gtk_set : 1; bool gtk_set : 1;
bool netconfig_set_addr4 : 1; bool netconfig_set_addr4 : 1;
@ -354,6 +356,12 @@ static void ap_reset(struct ap_state *ap)
l_timeout_remove(ap->rekey_timeout); l_timeout_remove(ap->rekey_timeout);
ap->rekey_timeout = NULL; ap->rekey_timeout = NULL;
} }
if (ap->pre_scan_cmd_id) {
scan_cancel(netdev_get_wdev_id(ap->netdev),
ap->pre_scan_cmd_id);
ap->pre_scan_cmd_id = 0;
}
} }
static bool ap_event_done(struct ap_state *ap, bool prev_in_event) static bool ap_event_done(struct ap_state *ap, bool prev_in_event)
@ -3852,6 +3860,70 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
return 0; return 0;
} }
static void ap_pre_scan_trigger(int err, void *user_data)
{
struct ap_state *ap = user_data;
if (err < 0) {
l_error("AP pre-scan failed: %i", err);
ap_start_failed(ap, err);
return;
}
}
static bool ap_check_channel(struct ap_state *ap)
{
const struct band_freq_attrs *freq_attr;
freq_attr = wiphy_get_frequency_info(netdev_get_wiphy(ap->netdev),
band_channel_to_freq(ap->channel, ap->band));
if (L_WARN_ON(!freq_attr))
return false;
/* Check if disabled/no-IR */
if (freq_attr->disabled || freq_attr->no_ir)
return false;
return true;
}
static bool ap_pre_scan_notify(int err, struct l_queue *bss_list,
const struct scan_freq_set *freqs,
void *user_data)
{
struct ap_state *ap = user_data;
if (!ap_check_channel(ap)) {
l_error("Unable to channel %u even after pre-scan",
ap->channel);
goto error;
}
if (ap_start_send(ap))
return false;
error:
ap_start_failed(ap, -ENOTSUP);
return false;
}
static void ap_pre_scan_destroy(void *user_data)
{
struct ap_state *ap = user_data;
ap->pre_scan_cmd_id = 0;
}
static bool ap_pre_scan(struct ap_state *ap)
{
ap->pre_scan_cmd_id = scan_passive(netdev_get_wdev_id(ap->netdev),
NULL, ap_pre_scan_trigger,
ap_pre_scan_notify,
ap, ap_pre_scan_destroy);
return ap->pre_scan_cmd_id != 0;
}
/* /*
* Start a simple independent WPA2 AP on given netdev. * Start a simple independent WPA2 AP on given netdev.
* *
@ -3962,6 +4034,20 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
return ap; return ap;
} }
if (!ap_check_channel(ap)) {
l_debug("Channel %u is disabled/no-IR, pre-scanning",
ap->channel);
if (ap_pre_scan(ap)) {
if (err_out)
*err_out = 0;
return ap;
}
goto error;
}
if (ap_start_send(ap)) { if (ap_start_send(ap)) {
if (err_out) if (err_out)
*err_out = 0; *err_out = 0;

View File

@ -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_ap_busy_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_AP_BUSY:
return blacklist_ap_busy_initial_timeout;
default:
l_warn("Unhandled blacklist reason: %u", reason);
return 0;
}
}
static bool check_if_expired(void *data, void *user_data) static bool check_if_expired(void *data, void *user_data)
{ {
struct blacklist_entry *entry = data; struct blacklist_entry *entry = data;
uint64_t now = l_get_u64(user_data); uint64_t now = l_get_u64(user_data);
if (l_time_diff(now, entry->added_time) > blacklist_max_timeout) { if (l_time_after(now, entry->expire_time)) {
l_debug("Removing entry "MAC" on prune", MAC_STR(entry->addr)); l_debug("Removing entry "MAC" on prune", MAC_STR(entry->addr));
l_free(entry); l_free(entry);
return true; return true;
@ -87,20 +107,53 @@ static bool match_addr(const void *a, const void *b)
return false; return false;
} }
void blacklist_add_bss(const uint8_t *addr) static bool match_addr_and_reason(const void *a, const void *b)
{
const struct blacklist_entry *entry = a;
const struct blacklist_search *search = b;
if (entry->reason != search->reason)
return false;
if (!memcmp(entry->addr, search->addr, 6))
return true;
return false;
}
void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason)
{ {
struct blacklist_entry *entry; struct blacklist_entry *entry;
uint64_t timeout;
if (!blacklist_initial_timeout)
return;
blacklist_prune(); blacklist_prune();
timeout = get_reason_timeout(reason);
if (!timeout)
return;
entry = l_queue_find(blacklist, match_addr, addr); entry = l_queue_find(blacklist, match_addr, addr);
if (entry) { if (entry) {
uint64_t offset = l_time_diff(entry->added_time, uint64_t offset;
entry->expire_time);
if (reason < entry->reason) {
l_debug("Promoting "MAC" blacklist to reason %u",
MAC_STR(addr), reason);
/* Reset this to the new timeout and reason */
entry->reason = reason;
entry->added_time = l_time_now();
entry->expire_time = l_time_offset(entry->added_time,
timeout);
return;
} else if (reason > entry->reason) {
l_debug("Ignoring blacklist extension of "MAC", "
"current blacklist status is more severe!",
MAC_STR(addr));
return;
}
offset = l_time_diff(entry->added_time, entry->expire_time);
offset *= blacklist_multiplier; offset *= blacklist_multiplier;
@ -115,40 +168,36 @@ void blacklist_add_bss(const uint8_t *addr)
entry = l_new(struct blacklist_entry, 1); entry = l_new(struct blacklist_entry, 1);
entry->added_time = l_time_now(); entry->added_time = l_time_now();
entry->expire_time = l_time_offset(entry->added_time, entry->expire_time = l_time_offset(entry->added_time, timeout);
blacklist_initial_timeout); entry->reason = reason;
memcpy(entry->addr, addr, 6); memcpy(entry->addr, addr, 6);
l_queue_push_tail(blacklist, entry); l_queue_push_tail(blacklist, entry);
} }
bool blacklist_contains_bss(const uint8_t *addr) bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason)
{ {
bool ret; struct blacklist_search search = {
uint64_t time_now; .addr = addr,
struct blacklist_entry *entry; .reason = reason
};
blacklist_prune(); blacklist_prune();
entry = l_queue_find(blacklist, match_addr, addr); return l_queue_find(blacklist, match_addr_and_reason, &search) != NULL;
if (!entry)
return false;
time_now = l_time_now();
ret = l_time_after(time_now, entry->expire_time) ? false : true;
return ret;
} }
void blacklist_remove_bss(const uint8_t *addr) void blacklist_remove_bss(const uint8_t *addr, enum blacklist_reason reason)
{ {
struct blacklist_entry *entry; struct blacklist_entry *entry;
struct blacklist_search search = {
.addr = addr,
.reason = reason
};
blacklist_prune(); blacklist_prune();
entry = l_queue_remove_if(blacklist, match_addr, addr); entry = l_queue_remove_if(blacklist, match_addr_and_reason, &search);
if (!entry) if (!entry)
return; return;
@ -167,6 +216,22 @@ static int blacklist_init(void)
/* For easier user configuration the timeout values are in seconds */ /* For easier user configuration the timeout values are in seconds */
blacklist_initial_timeout *= L_USEC_PER_SEC; blacklist_initial_timeout *= L_USEC_PER_SEC;
if (!l_settings_get_uint64(config, "Blacklist",
"InitialRoamRequestedTimeout",
&blacklist_ap_busy_initial_timeout))
blacklist_ap_busy_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
else
l_warn("[Blacklist].InitialRoamRequestedTimeout is deprecated, "
"use [Blacklist].InitialAccessPointBusyTimeout");
if (!l_settings_get_uint64(config, "Blacklist",
"InitialAccessPointBusyTimeout",
&blacklist_ap_busy_initial_timeout))
blacklist_ap_busy_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
/* For easier user configuration the timeout values are in seconds */
blacklist_ap_busy_initial_timeout *= L_USEC_PER_SEC;
if (!l_settings_get_uint64(config, "Blacklist", if (!l_settings_get_uint64(config, "Blacklist",
"Multiplier", "Multiplier",
&blacklist_multiplier)) &blacklist_multiplier))

View File

@ -20,6 +20,23 @@
* *
*/ */
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 an AP indicates that its unable
* to handle more connections. This is done via BSS-TM requests or
* denied authentications/associations with certain status codes.
*
* Once this type of blacklist is applied to a BSS IWD will attempt to
* avoid roaming to it for a configured period of time.
*/
BLACKLIST_REASON_AP_BUSY,
};
void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason);
bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason);
void blacklist_remove_bss(const uint8_t *addr, enum blacklist_reason reason);

View File

@ -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;

View File

@ -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;
} }

View File

@ -425,8 +425,8 @@ static void eap_handle_response(struct eap_state *eap, const uint8_t *pkt,
size_t len) size_t len)
{ {
enum eap_type type; enum eap_type type;
uint32_t vendor_id; uint32_t vendor_id = 0;
uint32_t vendor_type; uint32_t vendor_type = 0;
enum eap_type our_type = eap->method->request_type; enum eap_type our_type = eap->method->request_type;
uint32_t our_vendor_id = (eap->method->vendor_id[0] << 16) | uint32_t our_vendor_id = (eap->method->vendor_id[0] << 16) |
(eap->method->vendor_id[1] << 8) | (eap->method->vendor_id[1] << 8) |

View File

@ -1239,7 +1239,7 @@ static struct pmksa *handshake_state_steal_pmksa(struct handshake_state *s)
s->have_pmksa = false; s->have_pmksa = false;
if (l_time_after(now, pmksa->expiration)) { if (l_time_after(now, pmksa->expiration)) {
l_free(pmksa); pmksa_cache_free(pmksa);
pmksa = NULL; pmksa = NULL;
} }
@ -1280,7 +1280,7 @@ void handshake_state_cache_pmksa(struct handshake_state *s)
l_debug("Caching PMKSA for "MAC, MAC_STR(s->aa)); l_debug("Caching PMKSA for "MAC, MAC_STR(s->aa));
if (L_WARN_ON(pmksa_cache_put(pmksa) < 0)) if (L_WARN_ON(pmksa_cache_put(pmksa) < 0))
l_free(pmksa); pmksa_cache_free(pmksa);
} }
bool handshake_state_remove_pmksa(struct handshake_state *s) bool handshake_state_remove_pmksa(struct handshake_state *s)
@ -1294,7 +1294,7 @@ bool handshake_state_remove_pmksa(struct handshake_state *s)
if (!pmksa) if (!pmksa)
return false; return false;
l_free(pmksa); pmksa_cache_free(pmksa);
return true; return true;
} }

View File

@ -292,6 +292,22 @@ control how long a misbehaved BSS spends on the blacklist.
The initial time that a BSS spends on the blacklist. Setting this to zero The initial time that a BSS spends on the blacklist. Setting this to zero
will disable blacklisting functionality in IWD. will disable blacklisting functionality in IWD.
* - InitialRoamRequestedTimeout (**deprecated**)
- Values: uint64 value in seconds (default: **30**)
This setting is deprecated, please use
[Blacklist].InitialAccessPointBusyTimeout instead.
* - InitialAccessPointBusyTimeout
- Values: uint64 value in seconds (default: **30**)
The initial time that a BSS will be blacklisted after indicating it
cannot handle more connections. This is triggered by either a BSS
transition management request (telling IWD to roam elsewhere) or by a
denied authentication or association with the NO_MORE_STAS status code.
Once a BSS is blacklisted in this manor IWD will attempt to avoid it for
the configured amount of time.
* - Multiplier * - Multiplier
- Values: unsigned int value greater than zero, in seconds - Values: unsigned int value greater than zero, in seconds
(default: **30**) (default: **30**)

View File

@ -463,6 +463,14 @@ uint8_t netdev_get_rssi_level_idx(struct netdev *netdev)
return netdev->cur_rssi_level_idx; return netdev->cur_rssi_level_idx;
} }
int netdev_get_low_signal_threshold(uint32_t frequency)
{
if (frequency > 4000)
return LOW_SIGNAL_THRESHOLD_5GHZ;
return LOW_SIGNAL_THRESHOLD;
}
static void netdev_set_powered_result(int error, uint16_t type, static void netdev_set_powered_result(int error, uint16_t type,
const void *data, const void *data,
uint32_t len, void *user_data) uint32_t len, void *user_data)
@ -1101,6 +1109,7 @@ static void netdev_free(void *data)
l_timeout_remove(netdev->rssi_poll_timeout); l_timeout_remove(netdev->rssi_poll_timeout);
scan_wdev_remove(netdev->wdev_id); scan_wdev_remove(netdev->wdev_id);
frame_watch_wdev_remove(netdev->wdev_id);
watchlist_destroy(&netdev->station_watches); watchlist_destroy(&netdev->station_watches);
@ -1498,6 +1507,105 @@ static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err); handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err);
} }
static bool netdev_match_addr(const void *a, const void *b)
{
const struct netdev *netdev = a;
const uint8_t *addr = b;
return memcmp(netdev->addr, addr, ETH_ALEN) == 0;
}
static struct netdev *netdev_find_by_address(const uint8_t *addr)
{
return l_queue_find(netdev_list, netdev_match_addr, addr);
}
static void netdev_pmksa_driver_add(const struct pmksa *pmksa)
{
struct l_genl_msg *msg;
struct netdev *netdev = netdev_find_by_address(pmksa->spa);
uint32_t expiration = (uint32_t)pmksa->expiration;
if (!netdev)
return;
/* Only need to set the PMKSA into the kernel for fullmac drivers */
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
return;
l_debug("Adding PMKSA to kernel");
msg = l_genl_msg_new(NL80211_CMD_SET_PMKSA);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
l_genl_msg_append_attr(msg, NL80211_ATTR_PMKID, 16, pmksa->pmkid);
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, pmksa->aa);
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
pmksa->ssid_len, pmksa->ssid);
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK_LIFETIME, 4, &expiration);
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK,
pmksa->pmk_len, pmksa->pmk);
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
l_error("error sending SET_PMKSA");
}
static void netdev_pmksa_driver_remove(const struct pmksa *pmksa)
{
struct l_genl_msg *msg;
struct netdev *netdev = netdev_find_by_address(pmksa->spa);
if (!netdev)
return;
/* Only need to set the PMKSA into the kernel for fullmac drivers */
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
return;
l_debug("Removing PMKSA from kernel");
msg = l_genl_msg_new(NL80211_CMD_DEL_PMKSA);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
l_genl_msg_append_attr(msg, NL80211_ATTR_PMKID, 16, pmksa->pmkid);
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, pmksa->aa);
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
pmksa->ssid_len, pmksa->ssid);
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
l_error("error sending DEL_PMKSA");
}
static void netdev_flush_pmksa(struct netdev *netdev)
{
struct l_genl_msg *msg;
/*
* We only utilize the kernel's PMKSA cache for fullmac cards,
* so no need to flush if this is a softmac.
*/
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
return;
msg = l_genl_msg_new(NL80211_CMD_FLUSH_PMKSA);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
l_error("Failed to flush PMKSA for %u", netdev->index);
}
static void netdev_pmksa_driver_flush(void)
{
const struct l_queue_entry *e;
for (e = l_queue_get_entries(netdev_list); e; e = e->next) {
struct netdev *netdev = e->data;
netdev_flush_pmksa(netdev);
}
}
static void try_handshake_complete(struct netdev_handshake_state *nhs) static void try_handshake_complete(struct netdev_handshake_state *nhs)
{ {
l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u", l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u",
@ -3816,6 +3924,15 @@ static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
static int netdev_cqm_rssi_update(struct netdev *netdev) static int netdev_cqm_rssi_update(struct netdev *netdev)
{ {
struct l_genl_msg *msg; struct l_genl_msg *msg;
struct netdev_handshake_state *nhs = l_container_of(netdev->handshake,
struct netdev_handshake_state, super);
/*
* Fullmac cards handle roaming in firmware, there is no need to set
* CQM thresholds
*/
if (nhs->type == CONNECTION_TYPE_FULLMAC)
return 0;
l_debug(""); l_debug("");
@ -5334,6 +5451,39 @@ static void netdev_michael_mic_failure(struct l_genl_msg *msg,
l_debug("ifindex=%u key_idx=%u type=%u", netdev->index, idx, type); l_debug("ifindex=%u key_idx=%u type=%u", netdev->index, idx, type);
} }
#define MAX_COMEBACK_DELAY 1200
static void netdev_assoc_comeback(struct l_genl_msg *msg,
struct netdev *netdev)
{
const uint8_t *mac;
uint32_t timeout;
if (L_WARN_ON(!netdev->connected))
return;
if (nl80211_parse_attrs(msg, NL80211_ATTR_MAC, &mac,
NL80211_ATTR_TIMEOUT, &timeout,
NL80211_ATTR_UNSPEC) < 0)
return;
if (L_WARN_ON(memcmp(mac, netdev->handshake->aa, ETH_ALEN)))
return;
if (timeout <= MAX_COMEBACK_DELAY) {
l_debug(MAC" requested an association comeback delay of %u TU",
MAC_STR(netdev->handshake->aa), timeout);
return;
}
l_debug("Comeback delay of %u exceeded maximum of %u, deauthenticating",
timeout, MAX_COMEBACK_DELAY);
netdev_deauth_and_fail_connection(netdev,
NETDEV_RESULT_ASSOCIATION_FAILED,
MMPDU_STATUS_CODE_REFUSED_TEMPORARILY);
}
static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data) static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data)
{ {
struct netdev *netdev = NULL; struct netdev *netdev = NULL;
@ -5387,6 +5537,9 @@ static void netdev_mlme_notify(struct l_genl_msg *msg, void *user_data)
case NL80211_CMD_MICHAEL_MIC_FAILURE: case NL80211_CMD_MICHAEL_MIC_FAILURE:
netdev_michael_mic_failure(msg, netdev); netdev_michael_mic_failure(msg, netdev);
break; break;
case NL80211_CMD_ASSOC_COMEBACK:
netdev_assoc_comeback(msg, netdev);
break;
} }
} }
@ -5504,23 +5657,18 @@ static void netdev_external_auth_event(struct l_genl_msg *msg,
} }
if (action == NL80211_EXTERNAL_AUTH_ABORT) { if (action == NL80211_EXTERNAL_AUTH_ABORT) {
iwd_notice(IWD_NOTICE_CONNECT_INFO, "External Auth Aborted"); l_warn("External Auth Aborted");
goto error; goto error;
} }
iwd_notice(IWD_NOTICE_CONNECT_INFO,
"External Auth to SSID: %s, bssid: "MAC,
util_ssid_to_utf8(ssid.iov_len, ssid.iov_base),
MAC_STR(bssid));
if (hs->ssid_len != ssid.iov_len || if (hs->ssid_len != ssid.iov_len ||
memcmp(hs->ssid, ssid.iov_base, hs->ssid_len)) { memcmp(hs->ssid, ssid.iov_base, hs->ssid_len)) {
iwd_notice(IWD_NOTICE_CONNECT_INFO, "Target SSID mismatch"); l_warn("Target SSID mismatch");
goto error; goto error;
} }
if (memcmp(hs->aa, bssid, ETH_ALEN)) { if (memcmp(hs->aa, bssid, ETH_ALEN)) {
iwd_notice(IWD_NOTICE_CONNECT_INFO, "Target BSSID mismatch"); l_warn("Target BSSID mismatch");
goto error; goto error;
} }
@ -6529,6 +6677,16 @@ struct netdev *netdev_create_from_genl(struct l_genl_msg *msg,
netdev_get_link(netdev); netdev_get_link(netdev);
/*
* Call the netdev-specific variant to flush only this devices PMKSA
* cache in the kernel. This will make IWD's cache and the kernel's
* cache consistent, i.e. no entries
*
* TODO: If we ever are storing PMKSA's on disk we would first need to
* flush, then add all the PMKSA entries at this time.
*/
netdev_flush_pmksa(netdev);
return netdev; return netdev;
} }
@ -6644,6 +6802,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);

View File

@ -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);

View File

@ -168,6 +168,11 @@ static bool network_secret_check_cacheable(void *data, void *user_data)
return false; return false;
} }
void network_clear_blacklist(struct network *network)
{
l_queue_clear(network->blacklist, NULL);
}
void network_connected(struct network *network) void network_connected(struct network *network)
{ {
enum security security = network_get_security(network); enum security security = network_get_security(network);
@ -198,8 +203,6 @@ void network_connected(struct network *network)
l_queue_foreach_remove(network->secrets, l_queue_foreach_remove(network->secrets,
network_secret_check_cacheable, network); network_secret_check_cacheable, network);
l_queue_clear(network->blacklist, NULL);
network->provisioning_hidden = false; network->provisioning_hidden = false;
} }
@ -207,7 +210,7 @@ void network_disconnected(struct network *network)
{ {
network_settings_close(network); network_settings_close(network);
l_queue_clear(network->blacklist, NULL); network_clear_blacklist(network);
if (network->provisioning_hidden) if (network->provisioning_hidden)
station_hide_network(network->station, network); station_hide_network(network->station, network);
@ -1280,7 +1283,8 @@ struct scan_bss *network_bss_select(struct network *network,
if (l_queue_find(network->blacklist, match_bss, bss)) if (l_queue_find(network->blacklist, match_bss, bss))
continue; continue;
if (blacklist_contains_bss(bss->addr)) if (blacklist_contains_bss(bss->addr,
BLACKLIST_REASON_CONNECT_FAILED))
continue; continue;
/* OWE Transition BSS */ /* OWE Transition BSS */

View File

@ -74,6 +74,7 @@ bool network_bss_update(struct network *network, struct scan_bss *bss);
const char *network_bss_get_path(const struct network *network, const char *network_bss_get_path(const struct network *network,
const struct scan_bss *bss); const struct scan_bss *bss);
bool network_bss_list_isempty(struct network *network); bool network_bss_list_isempty(struct network *network);
void network_clear_blacklist(struct network *network);
const char *__network_path_append_bss(const char *network_path, const char *__network_path_append_bss(const char *network_path,
const struct scan_bss *bss); const struct scan_bss *bss);

View File

@ -177,6 +177,7 @@ static const struct {
{ NL80211_CMD_UNPROT_BEACON, "Unprotected Beacon" }, { NL80211_CMD_UNPROT_BEACON, "Unprotected Beacon" },
{ NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS, { NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS,
"Control Port TX Status" }, "Control Port TX Status" },
{ NL80211_CMD_ASSOC_COMEBACK, "Association comeback delay" },
{ } { }
}; };

View File

@ -190,6 +190,7 @@ static attr_handler handler_for_nl80211(int type)
case NL80211_ATTR_CENTER_FREQ2: case NL80211_ATTR_CENTER_FREQ2:
case NL80211_ATTR_AKM_SUITES: case NL80211_ATTR_AKM_SUITES:
case NL80211_ATTR_EXTERNAL_AUTH_ACTION: case NL80211_ATTR_EXTERNAL_AUTH_ACTION:
case NL80211_ATTR_TIMEOUT:
return extract_uint32; return extract_uint32;
case NL80211_ATTR_FRAME: case NL80211_ATTR_FRAME:
return extract_iovec; return extract_iovec;

View File

@ -40,6 +40,9 @@
static uint64_t dot11RSNAConfigPMKLifetime = 43200ULL * L_USEC_PER_SEC; static uint64_t dot11RSNAConfigPMKLifetime = 43200ULL * L_USEC_PER_SEC;
static uint32_t pmksa_cache_capacity = 255; static uint32_t pmksa_cache_capacity = 255;
static pmksa_cache_add_func_t driver_add;
static pmksa_cache_remove_func_t driver_remove;
static pmksa_cache_flush_func_t driver_flush;
struct min_heap { struct min_heap {
struct pmksa **data; struct pmksa **data;
@ -142,7 +145,7 @@ int pmksa_cache_put(struct pmksa *pmksa)
l_debug("Adding entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid)); l_debug("Adding entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
if (cache.used == cache.capacity) { if (cache.used == cache.capacity) {
l_free(cache.data[0]); pmksa_cache_free(cache.data[0]);
cache.data[0] = pmksa; cache.data[0] = pmksa;
__minheap_sift_down(cache.data, cache.used, 0, &ops); __minheap_sift_down(cache.data, cache.used, 0, &ops);
return 0; return 0;
@ -152,6 +155,9 @@ int pmksa_cache_put(struct pmksa *pmksa)
__minheap_sift_up(cache.data, cache.used, &ops); __minheap_sift_up(cache.data, cache.used, &ops);
cache.used += 1; cache.used += 1;
if (driver_add)
driver_add(pmksa);
return 0; return 0;
} }
@ -167,7 +173,7 @@ int pmksa_cache_expire(uint64_t cutoff)
for (i = 0; i < used; i++) { for (i = 0; i < used; i++) {
if (cache.data[i]->expiration <= cutoff) { if (cache.data[i]->expiration <= cutoff) {
l_free(cache.data[i]); pmksa_cache_free(cache.data[i]);
continue; continue;
} }
@ -190,11 +196,30 @@ int pmksa_cache_flush(void)
{ {
uint32_t i; uint32_t i;
/*
* The driver flush operation is done via a single kernel API call which
* is why below we use l_free instead of pmksa_cache_free as to not
* induce a DEL_PMKSA kernel call for each entry.
*/
if (driver_flush)
driver_flush();
for (i = 0; i < cache.used; i++) for (i = 0; i < cache.used; i++)
l_free(cache.data[i]); l_free(cache.data[i]);
memset(cache.data, 0, cache.capacity * sizeof(struct pmksa *)); memset(cache.data, 0, cache.capacity * sizeof(struct pmksa *));
cache.used = 0; cache.used = 0;
return 0;
}
int pmksa_cache_free(struct pmksa *pmksa)
{
if (driver_remove)
driver_remove(pmksa);
l_free(pmksa);
return 0; return 0;
} }
@ -217,6 +242,15 @@ void __pmksa_set_config(const struct l_settings *config)
&pmksa_cache_capacity); &pmksa_cache_capacity);
} }
void __pmksa_set_driver_callbacks(pmksa_cache_add_func_t add,
pmksa_cache_remove_func_t remove,
pmksa_cache_flush_func_t flush)
{
driver_add = add;
driver_remove = remove;
driver_flush = flush;
}
static int pmksa_init(void) static int pmksa_init(void)
{ {
cache.capacity = pmksa_cache_capacity; cache.capacity = pmksa_cache_capacity;

View File

@ -32,6 +32,10 @@ struct pmksa {
size_t pmk_len; size_t pmk_len;
}; };
typedef void (*pmksa_cache_add_func_t)(const struct pmksa *pmksa);
typedef void (*pmksa_cache_remove_func_t)(const struct pmksa *pmksa);
typedef void (*pmksa_cache_flush_func_t)(void);
struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries); struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries);
struct pmksa *pmksa_cache_get(const uint8_t spa[static 6], struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
@ -41,6 +45,11 @@ struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
int pmksa_cache_put(struct pmksa *pmksa); int pmksa_cache_put(struct pmksa *pmksa);
int pmksa_cache_expire(uint64_t cutoff); int pmksa_cache_expire(uint64_t cutoff);
int pmksa_cache_flush(void); int pmksa_cache_flush(void);
int pmksa_cache_free(struct pmksa *pmksa);
uint64_t pmksa_lifetime(void); uint64_t pmksa_lifetime(void);
void __pmksa_set_config(const struct l_settings *config); void __pmksa_set_config(const struct l_settings *config);
void __pmksa_set_driver_callbacks(pmksa_cache_add_func_t add,
pmksa_cache_remove_func_t remove,
pmksa_cache_flush_func_t flush);

View File

@ -169,6 +169,14 @@ static int sae_choose_next_group(struct sae_sm *sm)
!sm->handshake->ecc_sae_pts[sm->group_retry]) !sm->handshake->ecc_sae_pts[sm->group_retry])
continue; continue;
/*
* TODO: Groups for P192, P224 and P521 are currently
* non-functional with SAE. Until this is fixed we need to
* avoid these groups from being used.
*/
if (group == 21 || group == 25 || group == 26)
continue;
break; break;
} }
@ -994,7 +1002,8 @@ static int sae_process_anti_clogging(struct sae_sm *sm, const uint8_t *ptr,
sm->token_len = len; sm->token_len = len;
sm->sync = 0; sm->sync = 0;
sae_send_commit(sm, true); if (L_WARN_ON(!sae_send_commit(sm, true)))
return -EPROTO;
return -EAGAIN; return -EAGAIN;
} }
@ -1074,7 +1083,9 @@ static int sae_verify_committed(struct sae_sm *sm, uint16_t transaction,
return -ETIMEDOUT; return -ETIMEDOUT;
sm->sync++; sm->sync++;
sae_send_commit(sm, true);
if (L_WARN_ON(!sae_send_commit(sm, true)))
return -EPROTO;
return -EAGAIN; return -EAGAIN;
} }
@ -1129,7 +1140,9 @@ static int sae_verify_committed(struct sae_sm *sm, uint16_t transaction,
sm->group); sm->group);
sm->sync = 0; sm->sync = 0;
sae_send_commit(sm, false);
if (L_WARN_ON(!sae_send_commit(sm, false)))
return -EPROTO;
return -EAGAIN; return -EAGAIN;
} }
@ -1294,7 +1307,8 @@ static int sae_verify_confirmed(struct sae_sm *sm, uint16_t trans,
sm->sync++; sm->sync++;
sm->sc++; sm->sc++;
sae_send_commit(sm, true); if (L_WARN_ON(!sae_send_commit(sm, true)))
return -EPROTO;
if (!sae_send_confirm(sm)) if (!sae_send_confirm(sm))
return -EPROTO; return -EPROTO;

View File

@ -143,9 +143,9 @@ struct scan_survey {
}; };
struct scan_survey_results { struct scan_survey_results {
struct scan_survey survey_2_4[14]; struct scan_survey survey_2_4[15];
struct scan_survey survey_5[196]; struct scan_survey survey_5[197];
struct scan_survey survey_6[233]; struct scan_survey survey_6[234];
}; };
struct scan_results { struct scan_results {

View File

@ -155,6 +155,54 @@ 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_AP_BUSY);
good_signal = signal >= netdev_get_low_signal_threshold(freq);
if (good_signal)
set_bit(&rank_out, ABOVE_THRESHOLD_BIT);
if (!roam_blacklist)
set_bit(&rank_out, NOT_BLACKLISTED_BIT);
return rank_out;
}
/* /*
* Used as entries for the roam list since holding scan_bss pointers directly * Used as entries for the roam list since holding scan_bss pointers directly
* from station->bss_list is not 100% safe due to the possibility of the * from station->bss_list is not 100% safe due to the possibility of the
@ -162,13 +210,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);
@ -1747,6 +1795,15 @@ static void station_enter_state(struct station *station,
periodic_scan_stop(station); periodic_scan_stop(station);
break; break;
case STATION_STATE_CONNECTED: case STATION_STATE_CONNECTED:
network_clear_blacklist(station->connected_network);
if (station->connect_pending) {
struct l_dbus_message *reply =
l_dbus_message_new_method_return(
station->connect_pending);
dbus_pending_reply(&station->connect_pending, reply);
}
l_dbus_object_add_interface(dbus, l_dbus_object_add_interface(dbus,
netdev_get_path(station->netdev), netdev_get_path(station->netdev),
IWD_STATION_DIAGNOSTIC_INTERFACE, IWD_STATION_DIAGNOSTIC_INTERFACE,
@ -2166,6 +2223,26 @@ static void station_early_neighbor_report_cb(struct netdev *netdev, int err,
&station->roam_freqs); &station->roam_freqs);
} }
static bool station_try_next_bss(struct station *station)
{
struct scan_bss *next;
int ret;
next = network_bss_select(station->connected_network, false);
if (!next)
return false;
ret = __station_connect_network(station, station->connected_network,
next, station->state);
if (ret < 0)
return false;
l_debug("Attempting to connect to next BSS "MAC, MAC_STR(next->addr));
return true;
}
static bool station_can_fast_transition(struct station *station, static bool station_can_fast_transition(struct station *station,
struct handshake_state *hs, struct handshake_state *hs,
struct scan_bss *bss) struct scan_bss *bss)
@ -2208,28 +2285,26 @@ static bool station_can_fast_transition(struct station *station,
return true; return true;
} }
static void station_disconnect_on_error_cb(struct netdev *netdev, bool success, static void station_disconnect_on_netconfig_failed(struct netdev *netdev,
void *user_data) bool success,
void *user_data)
{ {
struct station *station = user_data; struct station *station = user_data;
bool continue_autoconnect;
station_enter_state(station, STATION_STATE_DISCONNECTED);
continue_autoconnect = station->state == STATION_STATE_CONNECTING_AUTO;
if (continue_autoconnect) {
if (station_autoconnect_next(station) < 0) {
l_debug("Nothing left on autoconnect list");
station_enter_state(station,
STATION_STATE_AUTOCONNECT_FULL);
}
if (station_try_next_bss(station))
return; return;
if (station->connect_pending) {
struct l_dbus_message *reply = dbus_error_failed(
station->connect_pending);
dbus_pending_reply(&station->connect_pending, reply);
} }
if (station->autoconnect) station_reset_connection_state(station);
station_enter_state(station, STATION_STATE_AUTOCONNECT_QUICK);
station_enter_state(station, STATION_STATE_DISCONNECTED);
station_enter_state(station, STATION_STATE_AUTOCONNECT_FULL);
} }
static void station_netconfig_event_handler(enum netconfig_event event, static void station_netconfig_event_handler(enum netconfig_event event,
@ -2242,23 +2317,20 @@ static void station_netconfig_event_handler(enum netconfig_event event,
station_enter_state(station, STATION_STATE_CONNECTED); station_enter_state(station, STATION_STATE_CONNECTED);
break; break;
case NETCONFIG_EVENT_FAILED: case NETCONFIG_EVENT_FAILED:
if (station->connect_pending) { station_debug_event(station, "netconfig-failed");
struct l_dbus_message *reply = dbus_error_failed(
station->connect_pending);
dbus_pending_reply(&station->connect_pending, reply); netconfig_reset(station->netconfig);
}
if (station->state == STATION_STATE_NETCONFIG) if (station->state == STATION_STATE_NETCONFIG)
network_connect_failed(station->connected_network, network_connect_failed(station->connected_network,
false); false);
netdev_disconnect(station->netdev, network_blacklist_add(station->connected_network,
station_disconnect_on_error_cb, station->connected_bss);
station);
station_reset_connection_state(station);
station_enter_state(station, STATION_STATE_DISCONNECTING); netdev_disconnect(station->netdev,
station_disconnect_on_netconfig_failed,
station);
break; break;
default: default:
l_error("station: Unsupported netconfig event: %d.", event); l_error("station: Unsupported netconfig event: %d.", event);
@ -2805,7 +2877,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
struct handshake_state *hs = netdev_get_handshake(station->netdev); struct handshake_state *hs = netdev_get_handshake(station->netdev);
struct scan_bss *current_bss = station->connected_bss; struct scan_bss *current_bss = station->connected_bss;
struct scan_bss *bss; struct scan_bss *bss;
double cur_bss_rank = 0.0; uint32_t cur_bss_group_rank = 0;
static const double RANK_FT_FACTOR = 1.3; static const double RANK_FT_FACTOR = 1.3;
uint16_t mdid; uint16_t mdid;
enum security orig_security, security; enum security orig_security, security;
@ -2834,10 +2906,15 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
*/ */
bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr); bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr);
if (bss && !station->ap_directed_roaming) { if (bss && !station->ap_directed_roaming) {
cur_bss_rank = bss->rank; double cur_bss_rank = bss->rank;
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid) if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
cur_bss_rank *= RANK_FT_FACTOR; cur_bss_rank *= RANK_FT_FACTOR;
cur_bss_group_rank = evaluate_bss_group_rank(bss->addr,
bss->frequency,
bss->signal_strength,
(uint16_t) cur_bss_rank);
} }
/* /*
@ -2859,6 +2936,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
while ((bss = l_queue_pop_head(bss_list))) { while ((bss = l_queue_pop_head(bss_list))) {
double rank; double rank;
struct roam_bss *rbss; struct roam_bss *rbss;
uint32_t group_rank;
station_print_scan_bss(bss); station_print_scan_bss(bss);
@ -2880,7 +2958,8 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
if (network_can_connect_bss(network, bss) < 0) if (network_can_connect_bss(network, bss) < 0)
goto next; goto next;
if (blacklist_contains_bss(bss->addr)) if (blacklist_contains_bss(bss->addr,
BLACKLIST_REASON_CONNECT_FAILED))
goto next; goto next;
rank = bss->rank; rank = bss->rank;
@ -2888,7 +2967,11 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid) if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
rank *= RANK_FT_FACTOR; rank *= RANK_FT_FACTOR;
if (rank <= cur_bss_rank) group_rank = evaluate_bss_group_rank(bss->addr, bss->frequency,
bss->signal_strength,
(uint16_t) rank);
if (group_rank <= cur_bss_group_rank)
goto next; goto next;
/* /*
@ -2897,7 +2980,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
*/ */
station_update_roam_bss(station, bss); station_update_roam_bss(station, bss);
rbss = roam_bss_from_scan_bss(bss, rank); rbss = roam_bss_from_scan_bss(bss, group_rank);
l_queue_insert(station->roam_bss_list, rbss, l_queue_insert(station->roam_bss_list, rbss,
roam_bss_rank_compare, NULL); roam_bss_rank_compare, NULL);
@ -3165,12 +3248,10 @@ static void station_ap_directed_roam(struct station *station,
uint8_t req_mode; uint8_t req_mode;
uint16_t dtimer; uint16_t dtimer;
uint8_t valid_interval; uint8_t valid_interval;
bool can_roam = !station_cannot_roam(station);
l_debug("ifindex: %u", netdev_get_ifindex(station->netdev)); l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
if (station_cannot_roam(station))
return;
if (station->state != STATION_STATE_CONNECTED) { if (station->state != STATION_STATE_CONNECTED) {
l_debug("roam: unexpected AP directed roam -- ignore"); l_debug("roam: unexpected AP directed roam -- ignore");
return; return;
@ -3234,8 +3315,13 @@ static void station_ap_directed_roam(struct station *station,
* disassociating us. If either of these bits are set, set the * disassociating us. If either of these bits are set, set the
* ap_directed_roaming flag. Otherwise still try roaming but don't * ap_directed_roaming flag. Otherwise still try roaming but don't
* treat it any different than a normal roam. * treat it any different than a normal roam.
*
* The only exception here is if we are in the middle of roaming
* (can_roam == false) since we cannot reliably know if the roam scan
* included frequencies from potential candidates in this request,
* forcing a roam in this case might result in unintended behavior.
*/ */
if (req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT | if (can_roam && req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT |
WNM_REQUEST_MODE_TERMINATION_IMMINENT | WNM_REQUEST_MODE_TERMINATION_IMMINENT |
WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT)) WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT))
station->ap_directed_roaming = true; station->ap_directed_roaming = true;
@ -3262,6 +3348,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_AP_BUSY);
station_debug_event(station, "ap-roam-blacklist-added");
/*
* Validating the frame and blacklisting should still be done even if
* we are mid-roam. Its important to track the BSS requesting the
* transition so when the current roam completes IWD will be less likely
* to roam back to the current BSS.
*/
if (!can_roam)
return;
station->preparing_roam = true; station->preparing_roam = true;
l_timeout_remove(station->roam_trigger_timeout); l_timeout_remove(station->roam_trigger_timeout);
@ -3334,26 +3433,6 @@ static void station_event_channel_switched(struct station *station,
network_bss_update(network, station->connected_bss); network_bss_update(network, station->connected_bss);
} }
static bool station_try_next_bss(struct station *station)
{
struct scan_bss *next;
int ret;
next = network_bss_select(station->connected_network, false);
if (!next)
return false;
ret = __station_connect_network(station, station->connected_network,
next, station->state);
if (ret < 0)
return false;
l_debug("Attempting to connect to next BSS "MAC, MAC_STR(next->addr));
return true;
}
static bool station_retry_owe_default_group(struct station *station) static bool station_retry_owe_default_group(struct station *station)
{ {
/* /*
@ -3400,7 +3479,15 @@ static bool station_retry_with_reason(struct station *station,
break; break;
} }
blacklist_add_bss(station->connected_bss->addr); blacklist_add_bss(station->connected_bss->addr,
BLACKLIST_REASON_CONNECT_FAILED);
/*
* Network blacklist the BSS as well, since the timeout blacklist could
* be disabled
*/
network_blacklist_add(station->connected_network,
station->connected_bss);
try_next: try_next:
return station_try_next_bss(station); return station_try_next_bss(station);
@ -3439,32 +3526,54 @@ static bool station_pmksa_fallback(struct station *station, uint16_t status)
return true; return true;
} }
/* A bit more concise for trying to fit these into 80 characters */
#define IS_TEMPORARY_STATUS(code) \
((code) == MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH || \
(code) == MMPDU_STATUS_CODE_DENIED_POOR_CHAN_CONDITIONS || \
(code) == MMPDU_STATUS_CODE_REJECTED_WITH_SUGG_BSS_TRANS || \
(code) == MMPDU_STATUS_CODE_DENIED_NO_MORE_STAS)
static bool station_retry_with_status(struct station *station, 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;
switch (status_code) {
/* /*
* 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
* the connection is complete. * the connection is complete.
* */
case MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH:
case MMPDU_STATUS_CODE_DENIED_POOR_CHAN_CONDITIONS:
/*
* TODO: The WITH_SUGG_BSS_TRANS case should also include a neighbor * TODO: The WITH_SUGG_BSS_TRANS case should also include a neighbor
* report IE in the frame. This would allow us to target a * report IE in the frame. This would allow us to target a
* 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.
*/
case MMPDU_STATUS_CODE_REJECTED_WITH_SUGG_BSS_TRANS:
break;
/*
* If a BSS is indicating its unable to handle more connections we will
* blacklist this the same way we do for BSS's issuing BSS-TM requests
* thereby avoiding roams to this BSS for the configured timeout.
*/ */
if (IS_TEMPORARY_STATUS(status_code)) case MMPDU_STATUS_CODE_DENIED_NO_MORE_STAS:
network_blacklist_add(station->connected_network, blacklist_add_bss(station->connected_bss->addr,
station->connected_bss); BLACKLIST_REASON_AP_BUSY);
else if (!station_pmksa_fallback(station, status_code)) break;
blacklist_add_bss(station->connected_bss->addr); /* For any other status codes, blacklist the BSS */
default:
blacklist_add_bss(station->connected_bss->addr,
BLACKLIST_REASON_CONNECT_FAILED);
break;
}
/*
* Unconditionally network blacklist the BSS if we are retrying. This
* will allow network_bss_select to traverse the BSS list and ignore
* BSS's which have previously failed
*/
network_blacklist_add(station->connected_network,
station->connected_bss);
try_next:
iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code); iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code);
return station_try_next_bss(station); return station_try_next_bss(station);
@ -3476,13 +3585,6 @@ static void station_connect_ok(struct station *station)
l_debug(""); l_debug("");
if (station->connect_pending) {
struct l_dbus_message *reply =
l_dbus_message_new_method_return(
station->connect_pending);
dbus_pending_reply(&station->connect_pending, reply);
}
/* /*
* Get a neighbor report now so future roams can avoid waiting for * Get a neighbor report now so future roams can avoid waiting for
* a report at that time * a report at that time
@ -3549,7 +3651,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:
@ -4719,7 +4822,7 @@ static struct l_dbus_message *station_property_set_affinities(
struct l_dbus_message_iter array; struct l_dbus_message_iter array;
const char *sender = l_dbus_message_get_sender(message); const char *sender = l_dbus_message_get_sender(message);
char *old_path = l_queue_peek_head(station->affinities); char *old_path = l_queue_peek_head(station->affinities);
const char *new_path = NULL; const char *new_path;
struct scan_bss *new_bss = NULL; struct scan_bss *new_bss = NULL;
struct scan_bss *old_bss = NULL; struct scan_bss *old_bss = NULL;
bool lower_threshold = false; bool lower_threshold = false;
@ -4739,10 +4842,15 @@ static struct l_dbus_message *station_property_set_affinities(
if (!l_dbus_message_iter_get_variant(new_value, "ao", &array)) if (!l_dbus_message_iter_get_variant(new_value, "ao", &array))
return dbus_error_invalid_args(message); return dbus_error_invalid_args(message);
/* Get first entry, there should be only one */ /* Get first entry, or if an empty array set the path to NULL */
l_dbus_message_iter_next_entry(&array, &new_path); if (!l_dbus_message_iter_next_entry(&array, &new_path))
new_path = NULL;
if (l_dbus_message_iter_next_entry(&array, &new_path)) /*
* Only allowing single values for now. If there is more than a single
* value, fail
*/
if (new_path && l_dbus_message_iter_next_entry(&array, &new_path))
return dbus_error_invalid_args(message); return dbus_error_invalid_args(message);
old_path = l_queue_peek_head(station->affinities); old_path = l_queue_peek_head(station->affinities);

View File

@ -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

View File

@ -138,20 +138,22 @@ static const struct cmac_data example_4 = {
.tag_len = sizeof(tag_4), .tag_len = sizeof(tag_4),
}; };
static bool test_precheck(const void *data)
{
return l_checksum_cmac_aes_supported();
}
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
func, test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_checksum_cmac_aes_supported()) { add_test("/cmac-aes/Example 1", cmac_test, &example_1);
printf("AES-CMAC support missing, skipping...\n"); add_test("/cmac-aes/Example 2", cmac_test, &example_2);
goto done; add_test("/cmac-aes/Example 3", cmac_test, &example_3);
} add_test("/cmac-aes/Example 4", cmac_test, &example_4);
l_test_add("/cmac-aes/Example 1", cmac_test, &example_1);
l_test_add("/cmac-aes/Example 2", cmac_test, &example_2);
l_test_add("/cmac-aes/Example 3", cmac_test, &example_3);
l_test_add("/cmac-aes/Example 4", cmac_test, &example_4);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -403,40 +403,32 @@ static void aes_siv_test(const void *data)
assert(memcmp(decrypted, plaintext, sizeof(decrypted)) == 0); assert(memcmp(decrypted, plaintext, sizeof(decrypted)) == 0);
} }
static bool test_precheck(const void *data)
{
return (l_cipher_is_supported(L_CIPHER_AES) &&
l_checksum_is_supported(L_CHECKSUM_SHA1, true));
}
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
func, test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_checksum_is_supported(L_CHECKSUM_SHA1, true)) { add_test("/Passphrase Generator/PSK Test Case 1",
printf("SHA1 support missing, skipping...\n"); psk_test, &psk_test_case_1);
goto done; add_test("/Passphrase Generator/PSK Test Case 2",
} psk_test, &psk_test_case_2);
add_test("/Passphrase Generator/PSK Test Case 3",
psk_test, &psk_test_case_3);
if (!l_cipher_is_supported(L_CIPHER_AES)) { add_test("/PTK Derivation/PTK Test Case 1", ptk_test, &ptk_test_1);
printf("AES support missing, skipping...\n"); add_test("/PTK Derivation/PTK Test Case 2", ptk_test, &ptk_test_2);
goto done; add_test("/PTK Derivation/PTK Test Case 3", ptk_test, &ptk_test_3);
} add_test("/PTK Derivation/PTK Test Case 4", ptk_test, &ptk_test_4);
l_test_add("/Passphrase Generator/PSK Test Case 1", add_test("/AES Key-wrap/Wrap & unwrap", aes_wrap_test, NULL);
psk_test, &psk_test_case_1); add_test("/AES-SIV", aes_siv_test, NULL);
l_test_add("/Passphrase Generator/PSK Test Case 2",
psk_test, &psk_test_case_2);
l_test_add("/Passphrase Generator/PSK Test Case 3",
psk_test, &psk_test_case_3);
l_test_add("/PTK Derivation/PTK Test Case 1",
ptk_test, &ptk_test_1);
l_test_add("/PTK Derivation/PTK Test Case 2",
ptk_test, &ptk_test_2);
l_test_add("/PTK Derivation/PTK Test Case 3",
ptk_test, &ptk_test_3);
l_test_add("/PTK Derivation/PTK Test Case 4",
ptk_test, &ptk_test_4);
l_test_add("/AES Key-wrap/Wrap & unwrap",
aes_wrap_test, NULL);
l_test_add("/AES-SIV", aes_siv_test, NULL);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -116,6 +116,29 @@ struct dpp_test_info bad_channels[] = {
}, },
}; };
struct dpp_test_info duplicates[] = {
/* Duplicate key */
{
.uri = "DPP:K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;",
.expect_fail = true
},
/* Duplicate frequencies*/
{
.uri = "DPP:C:81/1,115/36;C:81/1,115/36;;",
.expect_fail = true
},
/* Duplicate MACs*/
{
.uri = "DPP:M:5254005828e5;M:5254005828e5;;",
.expect_fail = true
},
/* Duplicate versions */
{
.uri = "DPP:V:2;V:2;;",
.expect_fail = true
},
};
static bool verify_info(const struct dpp_uri_info *parsed, static bool verify_info(const struct dpp_uri_info *parsed,
const struct dpp_test_info *result) const struct dpp_test_info *result)
{ {
@ -158,6 +181,14 @@ static void test_bad_channels(const void *data)
test_uri_parse(&bad_channels[i]); test_uri_parse(&bad_channels[i]);
} }
static void test_duplicate_in_uri(const void *data)
{
unsigned int i;
for (i = 0; i < L_ARRAY_SIZE(duplicates); i++)
test_uri_parse(&duplicates[i]);
}
struct dpp_test_vector { struct dpp_test_vector {
/* Initiator values */ /* Initiator values */
const char *i_proto_public; const char *i_proto_public;
@ -546,22 +577,25 @@ static void test_pkex_key_derivation(const void *user_data)
CHECK_FROM_STR(vector->v, tmp, 32); CHECK_FROM_STR(vector->v, tmp, 32);
} }
static bool test_precheck(const void *data)
{
return (l_getrandom_is_supported() &&
l_checksum_is_supported(L_CHECKSUM_SHA256, true));
}
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
func, test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (l_checksum_is_supported(L_CHECKSUM_SHA256, true) && add_test("DPP test responder-only key derivation", test_key_derivation,
l_getrandom_is_supported()) { &responder_only_p256);
l_test_add("DPP test responder-only key derivation", add_test("DPP test mutual key derivation", test_key_derivation,
test_key_derivation, &mutual_p256);
&responder_only_p256); add_test("DPP test PKEX key derivation", test_pkex_key_derivation,
l_test_add("DPP test mutual key derivation", &pkex_vector);
test_key_derivation,
&mutual_p256);
l_test_add("DPP test PKEX key derivation",
test_pkex_key_derivation,
&pkex_vector);
}
l_test_add("DPP URI parse", test_uri_parse, &all_values); l_test_add("DPP URI parse", test_uri_parse, &all_values);
l_test_add("DPP URI no type", test_uri_parse, &no_type); l_test_add("DPP URI no type", test_uri_parse, &no_type);
@ -576,6 +610,7 @@ int main(int argc, char *argv[])
l_test_add("DPP URI bad key", test_uri_parse, &bad_key); l_test_add("DPP URI bad key", test_uri_parse, &bad_key);
l_test_add("DPP URI bad channels", test_bad_channels, &bad_channels); l_test_add("DPP URI bad channels", test_bad_channels, &bad_channels);
l_test_add("DPP URI unexpected ID", test_uri_parse, &unexpected_id); l_test_add("DPP URI unexpected ID", test_uri_parse, &unexpected_id);
l_test_add("DPP URI duplicates", test_duplicate_in_uri, &duplicates);
return l_test_run(); return l_test_run();
} }

View File

@ -131,27 +131,23 @@ static void test_get_asym_key(const void *data)
assert(!memcmp(msk, m_session_key, sizeof(m_session_key))); assert(!memcmp(msk, m_session_key, sizeof(m_session_key)));
} }
static bool test_precheck(const void *data)
{
return l_checksum_is_supported(L_CHECKSUM_MD4, false);
}
#define add_test(name, func) l_test_add_func_precheck(name, func, \
test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_checksum_is_supported(L_CHECKSUM_MD4, false)) { add_test("MSHAPv2 nt_password-hash", test_nt_password_hash);
printf("MD4 support missing, skipping...\n"); add_test("MSHAPv2 generate_nt_response", test_generate_nt_response);
goto done; add_test("MSHAPv2 get_master_key", test_get_master_key);
} add_test("MSHAPv2 get_asym_state_key", test_get_asym_key);
add_test("MSHAPv2 authenticator_response", test_authenticator_response);
l_test_add("MSHAPv2 nt_password-hash",
test_nt_password_hash, NULL);
l_test_add("MSHAPv2 generate_nt_response",
test_generate_nt_response, NULL);
l_test_add("MSHAPv2 get_master_key",
test_get_master_key, NULL);
l_test_add("MSHAPv2 get_asym_state_key",
test_get_asym_key, NULL);
l_test_add("MSHAPv2 authenticator_response",
test_authenticator_response, NULL);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -3908,6 +3908,38 @@ static void eapol_ap_sta_handshake_ip_alloc_no_req_test(const void *data)
#define _IS_ENABLED2(one_or_two_args) _IS_ENABLED3(one_or_two_args true, false) #define _IS_ENABLED2(one_or_two_args) _IS_ENABLED3(one_or_two_args true, false)
#define _IS_ENABLED3(ignore_this, val, ...) val #define _IS_ENABLED3(ignore_this, val, ...) val
static bool hash_precheck(const void *data)
{
return (l_checksum_is_supported(L_CHECKSUM_MD5, true) &&
l_checksum_is_supported(L_CHECKSUM_SHA1, true));
}
static bool aes_precheck(const void *data)
{
return (l_cipher_is_supported(L_CIPHER_AES) &&
l_checksum_is_supported(L_CHECKSUM_MD5, true) &&
l_checksum_is_supported(L_CHECKSUM_SHA1, true));
}
static bool pkcs8_precheck(const void *data)
{
return (IS_ENABLED(HAVE_PKCS8_SUPPORT) &&
l_cipher_is_supported(L_CIPHER_AES) &&
l_cipher_is_supported(L_CIPHER_AES_CBC) &&
l_cipher_is_supported(L_CIPHER_DES3_EDE_CBC) &&
l_checksum_is_supported(L_CHECKSUM_MD5, true) &&
l_checksum_is_supported(L_CHECKSUM_SHA1, true) &&
l_key_is_supported(L_KEY_FEATURE_CRYPTO) &&
l_key_is_supported(L_KEY_FEATURE_RESTRICT));
}
#define add_hash_test(name, func, data) l_test_add_data_func_precheck(name, \
data, func, hash_precheck, 0)
#define add_aes_test(name, func, data) l_test_add_data_func_precheck(name, \
data, func, aes_precheck, 0)
#define add_pkcs8_test(name, func, data) l_test_add_data_func_precheck(name, \
data, func, pkcs8_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
@ -3977,80 +4009,55 @@ int main(int argc, char *argv[])
l_test_add("/EAPoL Key/Key Frame 32", l_test_add("/EAPoL Key/Key Frame 32",
eapol_key_test, &eapol_key_test_32); eapol_key_test, &eapol_key_test_32);
if (!l_checksum_is_supported(L_CHECKSUM_MD5, true) || add_hash_test("/EAPoL Key/MIC Test 1",
!l_checksum_is_supported(L_CHECKSUM_SHA1, true))
goto done;
l_test_add("/EAPoL Key/MIC Test 1",
eapol_key_mic_test, &eapol_key_mic_test_1); eapol_key_mic_test, &eapol_key_mic_test_1);
l_test_add("/EAPoL Key/MIC Test 2", add_hash_test("/EAPoL Key/MIC Test 2",
eapol_key_mic_test, &eapol_key_mic_test_2); eapol_key_mic_test, &eapol_key_mic_test_2);
add_hash_test("/EAPoL Key/Calculate MIC Test 1",
l_test_add("/EAPoL Key/Calculate MIC Test 1",
eapol_calculate_mic_test, &eapol_calculate_mic_test_1); eapol_calculate_mic_test, &eapol_calculate_mic_test_1);
if (!l_cipher_is_supported(L_CIPHER_AES)) add_aes_test("EAPoL/WPA2 4-Way Handshake",
goto done; eapol_4way_test, NULL);
add_aes_test("EAPoL/WPA2 4-Way & GTK Handshake",
eapol_wpa2_handshake_test, NULL);
add_aes_test("EAPoL/WPA 4-Way & GTK Handshake",
eapol_wpa_handshake_test, NULL);
add_aes_test("EAPoL/WPA2 PTK State Machine",
eapol_sm_test_ptk, NULL);
add_aes_test("EAPoL IGTK & 4-Way Handshake",
eapol_sm_test_igtk, NULL);
add_aes_test("EAPoL/WPA2 PTK & GTK State Machine",
eapol_sm_test_wpa2_ptk_gtk, NULL);
add_aes_test("EAPoL/WPA PTK & GTK State Machine Test 1",
eapol_sm_test_wpa_ptk_gtk, NULL);
add_aes_test("EAPoL/WPA PTK & GTK State Machine Test 2",
eapol_sm_test_wpa_ptk_gtk_2, NULL);
add_aes_test("EAPoL/WPA2 Retransmit Test",
eapol_sm_wpa2_retransmit_test, NULL);
l_test_add("EAPoL/WPA2 4-Way Handshake", add_pkcs8_test("EAPoL/8021x EAP-TLS & 4-Way Handshake",
&eapol_4way_test, NULL); eapol_sm_test_eap_tls, NULL);
add_pkcs8_test("EAPoL/8021x EAP-TTLS+EAP-MD5 & 4-Way Handshake",
eapol_sm_test_eap_ttls_md5, NULL);
add_pkcs8_test("EAPoL/8021x EAP NAK",
eapol_sm_test_eap_nak, NULL);
add_pkcs8_test("EAPoL/8021x EAP-TLS subject name match",
eapol_sm_test_eap_tls_subject_good, NULL);
add_pkcs8_test("EAPoL/8021x EAP-TLS subject name mismatch",
eapol_sm_test_eap_tls_subject_bad, NULL);
add_pkcs8_test("EAPoL/8021x EAP-TLS embedded certs",
eapol_sm_test_eap_tls_embedded, NULL);
l_test_add("EAPoL/WPA2 4-Way & GTK Handshake", add_aes_test("EAPoL/FT-Using-PSK 4-Way Handshake",
&eapol_wpa2_handshake_test, NULL); eapol_ft_handshake_test, NULL);
add_aes_test("EAPoL/Supplicant+Authenticator 4-Way Handshake",
eapol_ap_sta_handshake_test, NULL);
add_aes_test("EAPoL/Supplicant+Authenticator 4-Way Handshake Bad PSK",
eapol_ap_sta_handshake_bad_psk_test, NULL);
add_aes_test("EAPoL/Supplicant+Authenticator IP Allocation OK",
eapol_ap_sta_handshake_ip_alloc_ok_test, NULL);
add_aes_test("EAPoL/Supplicant+Authenticator IP Allocation no request",
eapol_ap_sta_handshake_ip_alloc_no_req_test, NULL);
l_test_add("EAPoL/WPA 4-Way & GTK Handshake",
&eapol_wpa_handshake_test, NULL);
l_test_add("EAPoL/WPA2 PTK State Machine", &eapol_sm_test_ptk, NULL);
l_test_add("EAPoL IGTK & 4-Way Handshake",
&eapol_sm_test_igtk, NULL);
l_test_add("EAPoL/WPA2 PTK & GTK State Machine",
&eapol_sm_test_wpa2_ptk_gtk, NULL);
l_test_add("EAPoL/WPA PTK & GTK State Machine Test 1",
&eapol_sm_test_wpa_ptk_gtk, NULL);
l_test_add("EAPoL/WPA PTK & GTK State Machine Test 2",
&eapol_sm_test_wpa_ptk_gtk_2, NULL);
l_test_add("EAPoL/WPA2 Retransmit Test",
&eapol_sm_wpa2_retransmit_test, NULL);
if (IS_ENABLED(HAVE_PKCS8_SUPPORT) &&
l_cipher_is_supported(L_CIPHER_DES3_EDE_CBC) &&
l_cipher_is_supported(L_CIPHER_AES_CBC) &&
l_key_is_supported(L_KEY_FEATURE_RESTRICT |
L_KEY_FEATURE_CRYPTO)) {
l_test_add("EAPoL/8021x EAP-TLS & 4-Way Handshake",
&eapol_sm_test_eap_tls, NULL);
l_test_add("EAPoL/8021x EAP-TTLS+EAP-MD5 & 4-Way Handshake",
&eapol_sm_test_eap_ttls_md5, NULL);
l_test_add("EAPoL/8021x EAP NAK",
&eapol_sm_test_eap_nak, NULL);
l_test_add("EAPoL/8021x EAP-TLS subject name match",
&eapol_sm_test_eap_tls_subject_good, NULL);
l_test_add("EAPoL/8021x EAP-TLS subject name mismatch",
&eapol_sm_test_eap_tls_subject_bad, NULL);
l_test_add("EAPoL/8021x EAP-TLS embedded certs",
&eapol_sm_test_eap_tls_embedded, NULL);
}
l_test_add("EAPoL/FT-Using-PSK 4-Way Handshake",
&eapol_ft_handshake_test, NULL);
l_test_add("EAPoL/Supplicant+Authenticator 4-Way Handshake",
&eapol_ap_sta_handshake_test, NULL);
l_test_add("EAPoL/Supplicant+Authenticator 4-Way Handshake Bad PSK",
&eapol_ap_sta_handshake_bad_psk_test, NULL);
l_test_add("EAPoL/Supplicant+Authenticator IP Allocation OK",
&eapol_ap_sta_handshake_ip_alloc_ok_test, NULL);
l_test_add("EAPoL/Supplicant+Authenticator IP Allocation no request",
&eapol_ap_sta_handshake_ip_alloc_no_req_test, NULL);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -81,18 +81,20 @@ static const struct hmac_data test_case_2 = {
.hmac = "80070713463e7749b90c2dc24911e275", .hmac = "80070713463e7749b90c2dc24911e275",
}; };
static bool test_precheck(const void *data)
{
return l_checksum_is_supported(L_CHECKSUM_MD5, true);
}
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
func, test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_checksum_is_supported(L_CHECKSUM_MD5, true)) { add_test("/hmac-md5/Test case 1", hmac_test, &test_case_1);
printf("MD5 support missing, skipping...\n"); add_test("/hmac-md5/Test case 2", hmac_test, &test_case_2);
goto done;
}
l_test_add("/hmac-md5/Test case 1", hmac_test, &test_case_1);
l_test_add("/hmac-md5/Test case 2", hmac_test, &test_case_2);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -81,18 +81,20 @@ static const struct hmac_data test_case_2 = {
.hmac = "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", .hmac = "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9",
}; };
static bool test_precheck(const void *data)
{
return l_checksum_is_supported(L_CHECKSUM_SHA1, true);
}
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
func, test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_checksum_is_supported(L_CHECKSUM_SHA1, true)) { add_test("/hmac-sha1/Test case 1", hmac_test, &test_case_1);
printf("SHA1 support missing, skipping...\n"); add_test("/hmac-sha1/Test case 2", hmac_test, &test_case_2);
goto done;
}
l_test_add("/hmac-sha1/Test case 1", hmac_test, &test_case_1);
l_test_add("/hmac-sha1/Test case 2", hmac_test, &test_case_2);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -83,18 +83,20 @@ static const struct hmac_data test_case_2 = {
"ef4d59a14946175997479dbc2d1a3cd8", "ef4d59a14946175997479dbc2d1a3cd8",
}; };
static bool test_precheck(const void *data)
{
return l_checksum_is_supported(L_CHECKSUM_SHA256, true);
}
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
func, test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_checksum_is_supported(L_CHECKSUM_SHA256, true)) { add_test("/hmac-sha256/Test case 1", hmac_test, &test_case_1);
printf("SHA256 support missing, skipping...\n"); add_test("/hmac-sha256/Test case 2", hmac_test, &test_case_2);
goto done;
}
l_test_add("/hmac-sha256/Test case 1", hmac_test, &test_case_1);
l_test_add("/hmac-sha256/Test case 2", hmac_test, &test_case_2);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -81,17 +81,19 @@ static const struct kdf_data test_case_1 = {
"84f7d2291143d4d4", "84f7d2291143d4d4",
}; };
static bool test_precheck(const void *data)
{
return l_checksum_is_supported(L_CHECKSUM_SHA256, true);
}
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
func, test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_checksum_is_supported(L_CHECKSUM_SHA256, true)) { add_test("/kdf-sha256/Test case 1", kdf_test, &test_case_1);
printf("SHA256 support missing, skipping...\n");
goto done;
}
l_test_add("/kdf-sha256/Test case 1", kdf_test, &test_case_1);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -341,7 +341,7 @@ static const struct p2p_probe_req_data p2p_probe_req_data_1 = {
.group_caps = 0, .group_caps = 0,
}, },
.listen_channel = { .listen_channel = {
.country = "XX\x04", .country = { 'X', 'X', '\x04' },
.oper_class = 81, .oper_class = 81,
.channel_num = 1, .channel_num = 1,
}, },

View File

@ -113,19 +113,21 @@ static const struct prf_data test_case_3 = {
"f7b4abd43d87f0a68f1cbd9e2b6f7607", "f7b4abd43d87f0a68f1cbd9e2b6f7607",
}; };
static bool test_precheck(const void *data)
{
return l_checksum_is_supported(L_CHECKSUM_SHA1, true);
}
#define add_test(name, func, data) l_test_add_data_func_precheck(name, data, \
func, test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_checksum_is_supported(L_CHECKSUM_SHA1, true)) { add_test("/prf-sha1/Test case 1", prf_test, &test_case_1);
printf("SHA1 support missing, skipping...\n"); add_test("/prf-sha1/Test case 2", prf_test, &test_case_2);
goto done; add_test("/prf-sha1/Test case 3", prf_test, &test_case_3);
}
l_test_add("/prf-sha1/Test case 1", prf_test, &test_case_1);
l_test_add("/prf-sha1/Test case 2", prf_test, &test_case_2);
l_test_add("/prf-sha1/Test case 3", prf_test, &test_case_3);
done:
return l_test_run(); return l_test_run();
} }

View File

@ -871,32 +871,29 @@ static void test_pt_pwe(const void *data)
l_ecc_point_free(pt); l_ecc_point_free(pt);
} }
static bool test_precheck(const void *data)
{
return (l_getrandom_is_supported() &&
l_checksum_is_supported(L_CHECKSUM_SHA256, true));
}
#define add_test(name, func) l_test_add_func_precheck(name, func, \
test_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
if (!l_getrandom_is_supported()) { add_test("SAE anti-clogging", test_clogging);
l_info("l_getrandom not supported, skipping..."); add_test("SAE early confirm", test_early_confirm);
goto done; add_test("SAE reflection", test_reflection);
} add_test("SAE malformed commit", test_malformed_commit);
add_test("SAE malformed confirm", test_malformed_confirm);
add_test("SAE bad group", test_bad_group);
add_test("SAE bad confirm", test_bad_confirm);
add_test("SAE confirm after accept", test_confirm_after_accept);
add_test("SAE end-to-end", test_end_to_end);
add_test("SAE pt-pwe", test_pt_pwe);
if (!l_checksum_is_supported(L_CHECKSUM_SHA256, true)) {
l_info("SHA256/HMAC_SHA256 not supported, skipping...");
goto done;
}
l_test_add("SAE anti-clogging", test_clogging, NULL);
l_test_add("SAE early confirm", test_early_confirm, NULL);
l_test_add("SAE reflection", test_reflection, NULL);
l_test_add("SAE malformed commit", test_malformed_commit, NULL);
l_test_add("SAE malformed confirm", test_malformed_confirm, NULL);
l_test_add("SAE bad group", test_bad_group, NULL);
l_test_add("SAE bad confirm", test_bad_confirm, NULL);
l_test_add("SAE confirm after accept", test_confirm_after_accept, NULL);
l_test_add("SAE end-to-end", test_end_to_end, NULL);
l_test_add("SAE pt-pwe", test_pt_pwe, NULL);
done:
return l_test_run(); return l_test_run();
} }

66
unit/test-storage.c Normal file
View File

@ -0,0 +1,66 @@
/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2025 Locus Robotics. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <ell/ell.h>
#include "src/storage.h"
static bool aes_ctr_supported(const void *data)
{
return l_cipher_is_supported(L_CIPHER_AES_CTR);
}
static void test_short_encrypted_bytes(const void *data)
{
struct l_settings *settings = l_settings_new();
bool changed;
int err;
storage_init((const uint8_t *)"abc123", 6);
l_settings_set_string(settings, "Security", "EncryptedSecurity", "012345");
l_settings_set_string(settings, "Security", "EncryptedSalt", "012345");
err = __storage_decrypt(settings, "mySSID", &changed);
assert(err == -EBADMSG);
l_settings_free(settings);
storage_exit();
}
int main(int argc, char *argv[])
{
l_test_init(&argc, &argv);
l_test_add_func_precheck("/storage/profile encryption",
test_short_encrypted_bytes,
aes_ctr_supported, 0);
return l_test_run();
}

View File

@ -2574,6 +2574,40 @@ struct wsc_credential wsc_r_test_open_cred = {
.encryption_type = WSC_ENCRYPTION_TYPE_NONE, .encryption_type = WSC_ENCRYPTION_TYPE_NONE,
}; };
static bool getrandom_precheck(const void *data)
{
return l_getrandom_is_supported();
}
static bool aes_cbc_precheck(const void *data)
{
return (l_key_is_supported(L_KEY_FEATURE_DH) &&
l_cipher_is_supported(L_CIPHER_AES_CBC));
}
static bool key_crypto_precheck(const void *data)
{
return (l_key_is_supported(L_KEY_FEATURE_CRYPTO) &&
l_checksum_is_supported(L_CHECKSUM_SHA256, true));
}
static bool key_dh_precheck(const void *data)
{
return (l_key_is_supported(L_KEY_FEATURE_DH) &&
l_key_is_supported(L_KEY_FEATURE_CRYPTO) &&
l_checksum_is_supported(L_CHECKSUM_SHA256, true));
}
#define add_aes_cbc_test(name, func, data) l_test_add_data_func_precheck(name, \
data, func, \
aes_cbc_precheck, 0)
#define add_crypto_test(name, func, data) l_test_add_data_func_precheck(name, \
data, func, \
key_crypto_precheck, 0)
#define add_dh_test(name, func, data) l_test_add_data_func_precheck(name, \
data, func, \
key_dh_precheck, 0)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
l_test_init(&argc, &argv); l_test_init(&argc, &argv);
@ -2595,8 +2629,8 @@ int main(int argc, char *argv[])
l_test_add("/wsc/pin/valid pin", wsc_test_pin_valid, NULL); l_test_add("/wsc/pin/valid pin", wsc_test_pin_valid, NULL);
l_test_add("/wsc/pin/valid checksum", wsc_test_pin_checksum, NULL); l_test_add("/wsc/pin/valid checksum", wsc_test_pin_checksum, NULL);
if (l_getrandom_is_supported()) l_test_add_func_precheck("/wsc/pin/generate", wsc_test_pin_generate,
l_test_add("/wsc/pin/generate", wsc_test_pin_generate, NULL); getrandom_precheck, 0);
l_test_add("/wsc/gen_uuid/1", wsc_test_uuid_from_addr, l_test_add("/wsc/gen_uuid/1", wsc_test_uuid_from_addr,
&uuid_from_addr_data_1); &uuid_from_addr_data_1);
@ -2607,96 +2641,77 @@ int main(int argc, char *argv[])
l_test_add("/wsc/build/m1 1", wsc_test_build_m1, &m1_data_1); l_test_add("/wsc/build/m1 1", wsc_test_build_m1, &m1_data_1);
l_test_add("/wsc/build/m1 2", wsc_test_build_m1, &m1_data_2); l_test_add("/wsc/build/m1 2", wsc_test_build_m1, &m1_data_2);
if (!l_checksum_is_supported(L_CHECKSUM_SHA256, true)) { add_dh_test("/wsc/parse/m2 1", wsc_test_parse_m2, &m2_data_1);
printf("SHA256 support missing, skipping other tests...\n"); add_dh_test("/wsc/parse/m2 2", wsc_test_parse_m2, &m2_data_2);
goto done;
}
if (!l_key_is_supported(L_KEY_FEATURE_CRYPTO)) { add_crypto_test("/wsc/build/m2 1", wsc_test_build_m2, &m2_data_1);
printf("Key crypto not supported, skipping other tests...\n");
goto done;
}
if (l_key_is_supported(L_KEY_FEATURE_DH)) { add_crypto_test("/wsc/parse/m3 1", wsc_test_parse_m3, &m3_data_1);
l_test_add("/wsc/parse/m2 1", wsc_test_parse_m2, &m2_data_1); add_crypto_test("/wsc/build/m3 1", wsc_test_build_m3, &m3_data_1);
l_test_add("/wsc/parse/m2 2", wsc_test_parse_m2, &m2_data_2);
}
l_test_add("/wsc/build/m2 1", wsc_test_build_m2, &m2_data_1); add_crypto_test("/wsc/parse/m4 1", wsc_test_parse_m4, &m4_data_1);
add_crypto_test("/wsc/build/m4 1", wsc_test_build_m4, &m4_data_1);
l_test_add("/wsc/parse/m3 1", wsc_test_parse_m3, &m3_data_1); add_crypto_test("/wsc/parse/m4 encrypted settings 1",
l_test_add("/wsc/build/m3 1", wsc_test_build_m3, &m3_data_1); wsc_test_parse_m4_encrypted_settings,
&m4_encrypted_settings_data_1);
add_crypto_test("/wsc/build/m4 encrypted settings 1",
wsc_test_build_m4_encrypted_settings,
&m4_encrypted_settings_data_1);
l_test_add("/wsc/parse/m4 1", wsc_test_parse_m4, &m4_data_1); add_crypto_test("/wsc/parse/m5 1", wsc_test_parse_m5, &m5_data_1);
l_test_add("/wsc/build/m4 1", wsc_test_build_m4, &m4_data_1); add_crypto_test("/wsc/build/m5 1", wsc_test_build_m5, &m5_data_1);
l_test_add("/wsc/parse/m4 encrypted settings 1", add_crypto_test("/wsc/parse/m6 1", wsc_test_parse_m6, &m6_data_1);
wsc_test_parse_m4_encrypted_settings, add_crypto_test("/wsc/build/m6 1", wsc_test_build_m6, &m6_data_1);
&m4_encrypted_settings_data_1);
l_test_add("/wsc/build/m4 encrypted settings 1",
wsc_test_build_m4_encrypted_settings,
&m4_encrypted_settings_data_1);
l_test_add("/wsc/parse/m5 1", wsc_test_parse_m5, &m5_data_1); add_crypto_test("/wsc/parse/m6 encrypted settings 1",
l_test_add("/wsc/build/m5 1", wsc_test_build_m5, &m5_data_1); wsc_test_parse_m6_encrypted_settings,
&m6_encrypted_settings_data_1);
add_crypto_test("/wsc/build/m6 encrypted settings 1",
wsc_test_build_m6_encrypted_settings,
&m6_encrypted_settings_data_1);
l_test_add("/wsc/parse/m6 1", wsc_test_parse_m6, &m6_data_1); add_crypto_test("/wsc/parse/m7 1", wsc_test_parse_m7, &m7_data_1);
l_test_add("/wsc/build/m6 1", wsc_test_build_m6, &m6_data_1); add_crypto_test("/wsc/build/m7 1", wsc_test_build_m7, &m7_data_1);
l_test_add("/wsc/parse/m6 encrypted settings 1", add_crypto_test("/wsc/parse/m8 1", wsc_test_parse_m8, &m8_data_1);
wsc_test_parse_m6_encrypted_settings, add_crypto_test("/wsc/build/m8 1", wsc_test_build_m8, &m8_data_1);
&m6_encrypted_settings_data_1);
l_test_add("/wsc/build/m6 encrypted settings 1",
wsc_test_build_m6_encrypted_settings,
&m6_encrypted_settings_data_1);
l_test_add("/wsc/parse/m7 1", wsc_test_parse_m7, &m7_data_1); add_crypto_test("/wsc/parse/m8 encrypted settings 1",
l_test_add("/wsc/build/m7 1", wsc_test_build_m7, &m7_data_1); wsc_test_parse_m8_encrypted_settings,
&m8_encrypted_settings_data_1);
add_crypto_test("/wsc/build/m8 encrypted settings 1",
wsc_test_build_m8_encrypted_settings,
&m8_encrypted_settings_data_1);
l_test_add("/wsc/parse/m8 1", wsc_test_parse_m8, &m8_data_1); add_crypto_test("/wsc/parse/wsc_done 1", wsc_test_parse_wsc_done,
l_test_add("/wsc/build/m8 1", wsc_test_build_m8, &m8_data_1);
l_test_add("/wsc/parse/m8 encrypted settings 1",
wsc_test_parse_m8_encrypted_settings,
&m8_encrypted_settings_data_1);
l_test_add("/wsc/build/m8 encrypted settings 1",
wsc_test_build_m8_encrypted_settings,
&m8_encrypted_settings_data_1);
l_test_add("/wsc/parse/wsc_done 1", wsc_test_parse_wsc_done,
&wsc_done_data_1); &wsc_done_data_1);
l_test_add("/wsc/build/wsc_done 1", wsc_test_build_wsc_done, add_crypto_test("/wsc/build/wsc_done 1", wsc_test_build_wsc_done,
&wsc_done_data_1); &wsc_done_data_1);
if (!l_key_is_supported(L_KEY_FEATURE_DH)) add_dh_test("/wsc/diffie-hellman/generate pubkey 1",
goto done;
l_test_add("/wsc/diffie-hellman/generate pubkey 1",
wsc_test_dh_generate_pubkey, wsc_test_dh_generate_pubkey,
&dh_generate_pubkey_test_data_1); &dh_generate_pubkey_test_data_1);
l_test_add("/wsc/diffie-hellman/generate pubkey 2", add_dh_test("/wsc/diffie-hellman/generate pubkey 2",
wsc_test_dh_generate_pubkey, wsc_test_dh_generate_pubkey,
&dh_generate_pubkey_test_data_2); &dh_generate_pubkey_test_data_2);
if (!l_cipher_is_supported(L_CIPHER_AES_CBC)) add_aes_cbc_test("/wsc/handshake/PBC Handshake Test",
goto done;
l_test_add("/wsc/handshake/PBC Handshake Test",
wsc_test_pbc_handshake, NULL); wsc_test_pbc_handshake, NULL);
l_test_add("/wsc/retransmission/no fragmentation", add_aes_cbc_test("/wsc/retransmission/no fragmentation",
wsc_test_retransmission_no_fragmentation, NULL); wsc_test_retransmission_no_fragmentation, NULL);
l_test_add("/wsc-r/handshake/PBC Handshake WPA2 passphrase test", add_aes_cbc_test("/wsc-r/handshake/PBC Handshake WPA2 passphrase test",
wsc_r_test_pbc_handshake, wsc_r_test_pbc_handshake,
&wsc_r_test_wpa2_cred_passphrase); &wsc_r_test_wpa2_cred_passphrase);
l_test_add("/wsc-r/handshake/PBC Handshake WPA2 PSK test", add_aes_cbc_test("/wsc-r/handshake/PBC Handshake WPA2 PSK test",
wsc_r_test_pbc_handshake, wsc_r_test_pbc_handshake,
&wsc_r_test_wpa2_cred_psk); &wsc_r_test_wpa2_cred_psk);
l_test_add("/wsc-r/handshake/PBC Handshake Open test", add_aes_cbc_test("/wsc-r/handshake/PBC Handshake Open test",
wsc_r_test_pbc_handshake, wsc_r_test_pbc_handshake,
&wsc_r_test_open_cred); &wsc_r_test_open_cred);
done:
return l_test_run(); return l_test_run();
} }