3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2026-01-09 22:47:56 +01:00

Compare commits

...

295 Commits
2.17 ... master

Author SHA1 Message Date
Marcel Holtmann
3e7a8feee0 Release 3.10 2025-09-25 15:36:37 +02:00
James Prestwood
3760a49650 station: print vendor quirks (if any) when connecting/roaming
This makes it clear the BSS being selected for a connection/roam has
any quirks associated with its OUI(s) and that IWD may behave
differently based on these.
2025-08-27 12:40:50 -05:00
James Prestwood
c4d114d804 auto-t: add AP roam test for bad neighbor reports/candidate lists 2025-08-27 12:40:44 -05:00
James Prestwood
ffe79bfada station: check vendor quirk for BSS TM request candidate list
If the AP vendor has known issues with the preferred candidate list
ignore it and jump directly to requesting a neighbor report.
2025-08-27 12:40:28 -05:00
James Prestwood
c0efaf21ad station: get neighbor report on BSS TM request
If a BSS is requesting IWD roam elsewhere but does not include a
preferred candidate list try getting a neighbor report before doing
a full scan.

If the limited scan based on the candidate list comes up empty this
would previously result in IWD giving up on the AP roam entirely.
This patch also improves that behavior slightly by doing a full
scan afterwards as a last ditch effort. If no BSS's are found after
that, IWD will give up on the AP roam.
2025-08-27 12:38:34 -05:00
James Prestwood
cee079da5b handshake: use vendor quirk to disable check of replay counters
This has been a long standing issue on Aruba APs where the scan
IEs differ from the IEs received during FT. For compatibility we
have been carrying a patch to disable the replay counter check but
this isn't something that was ever acceptable for upstream. Now
with the addition of vendor quirks this check can be disabled only
for the OUI of Aruba APs.

Reported-by: Michael Johnson <mjohnson459@gmail.com>
Co-authored-by: Michael Johnson <<mjohnson459@gmail.com>
2025-08-27 12:37:54 -05:00
James Prestwood
df30309aac station: set vendor quirks into handshake object 2025-08-27 12:37:49 -05:00
James Prestwood
2fe8c13016 scan: store vendor quirks in scan_bss
As each vendor IE is parsed lookup if there are any quirks associated
with it, and store these in a bit mask.
2025-08-27 12:37:21 -05:00
James Prestwood
84666b9703 handshake: add vendor quirks into handshake object 2025-08-27 12:37:00 -05:00
James Prestwood
54c0dbb3c8 handshake: pass object to handshake_util_ap_ie_matches
This is to prepare for supporting a vendor quirk, where we'll need
the handshake to lookup if the quirk to disable a specific check.
2025-08-27 12:36:13 -05:00
James Prestwood
6e9e0928b0 vendor_quirks: implement two vendor quirks
ignore_bss_tm_candidates:
  When a BSS requests a station roam it can optionally include a
  list of BSS's that can be roamed to. IWD uses this list and only
  scans on those frequencies. In some cases though the AP's list
  contains very poor options and it would be better for IWD to
  request a full neighbor report.

replay_counter_mismatch:
  On some Aruba APs there is a mismatch in the replay counters
  between what is seen in scans versus authentications/associations.
  This difference is not allowed in the spec, therefore IWD will
  not connect. This quirk is intended to relax that check.
2025-08-27 12:31:57 -05:00
James Prestwood
a1247fe46e vendor_quirks: initial skeleton
This module will provide a database for known issues or quirks with
wireless vendors.

The vendor_quirks_append_for_oui() API is intended to be called from
scan.c when parsing vendor attributes. This will lookup any quirks
associated with the OUI provided and combine them into an existing
vendor_quirk structure. This can be repeated against all the vendor
OUI's seen in the scan then referenced later to alter IWD behavior.

In the future more critera could be added such as MAC address prefix
or more generalized IE matches e.g.

vendor_quirks_append_for_mac()
vendor_quirks_append_for_ie()
etc.
2025-08-27 12:26:03 -05:00
James Prestwood
088bb2e308 station: clear roam_freqs on delayed roam
If there were no BSS candidates found after trying to roam make
sure the old roam_freqs list gets cleared so IWD doesn't end up
scanning potentially old frequencies on the next retry.
2025-08-26 10:00:32 -05:00
James Prestwood
57dc5d843c monitor: add Cisco Meraki as a printable vendor 2025-08-26 09:38:01 -05:00
James Prestwood
8cb134f935 scan: check support before using colocated flag 2025-08-26 09:37:17 -05:00
James Prestwood
46037c428c wiphy: add comments around the driver quirks 2025-08-26 09:36:44 -05:00
James Prestwood
161de4a3ad wiphy: add driver quirk for the colocated scan flag
Some drivers do not handle the colocated scan flag very well and this
results in BSS's not being seen in scans. This of course results in
very poor behavior.

This has been seen on ath11k specifically but after some
conversations [1] on the linux-wireless mailing list others have
reported issues with iwlwifi acting similarly. Since there are many
hardware variants that use both ath11k and iwlwifi this new quirk
isn't being forced to those drivers, but let users configure IWD to
disable the flag if needed.

[1] https://lore.kernel.org/linux-wireless/d1e75a08-047d-7947-d51a-2e486efead77@candelatech.com/
2025-08-26 09:35:35 -05:00
James Prestwood
77ee863f04 auto-t: add test for channel switch during roam
In kernel 6.8 a new CMD_ASSOCIATE failure path was added which checks
if the AP has a channel switch in progress. Eariler patches update
IWD into handling this case better, and this new test exercises that.
2025-08-20 11:18:51 -05:00
James Prestwood
405d1ab77c auto-t: make waiting for channel switch configurable 2025-08-20 11:18:45 -05:00
James Prestwood
dc1589f3fe netdev: disconnect rather than deauth in FT association failure
After CSA IE parsing was added to the kernel this opened up the
possibility that associations could be rejected locally based on
the contents of this CSA IE in the AP's beacons. Overall, it was
always possible for a local rejection but this case was never
considered by IWD. The CSA-based rejection is something that can
and does happen out in the wild.

When this association rejection happens it desync's IWD and the
kernel's state:

1. IWD begins an FT roam. Authenticates successfully, then proceeds
   to calling netdev_ft_reassociate().
2. Immediately IWD transitions to a ft-roaming state and waits for
   an association response.
3. CMD_ASSOCIATE is rejected by the kernel in the ACK which IWD
   handles by sending a deauthenticate command to the kernel (since
   we have a valid authentication to the new BSS).
4. Due to a bug IWD uses the target BSSID to deauthenticate which
   the kernel rejects since it has no knowledge of this auth. This
   error is not handled or logged.
5. IWD proceeds, assuming its deauthenticated, and transitions to a
   disconnected state. The kernel remains "connected" which of course
   prevents any future connections.

A simple fix for this is to address the bug (4) in IWD that deauths
using the current BSS roam target. This is actually legacy behavior
from back when IWD used CMD_AUTHENTICATE. Today the kernel is unaware
that IWD authenticated so a deauth is not going to be effective.
Instead we can issue a CMD_DISCONNECT. This is somewhat of a large
hammer, but since the handshake and internal state has already been
modified to use the new target BSS we cannot go back and maintain the
existing connect (though it is _possible_, see the TODO in the
patch).
2025-08-20 11:18:01 -05:00
James Prestwood
755280a4cc netdev: check connected in channel switch event
In an ideal world userspace should never be getting a channel switch
event unless connected to an AP, but alas this has been seen at least
with ath10k hardware. This causes IWD to crash since the logic
assumes netdev->handshake is set.
2025-08-20 11:17:54 -05:00
Gokul Sivakumar
df2c5cf7fa doc: add STA inactive and connected time duration info to diagnostics 2025-08-07 17:59:15 -05:00
Gokul Sivakumar
fee0e5de33 netdev: parse INACTIVE_TIME and CONNECTED_TIME in netdev_get_station
These two newly parsed station info params "inactive time" and the
"connected time" would be helpful to track the duration (in ms) for
which the station was last inactive and the total duration (in s) for
which the station is currently connected to the AP.

When the wlan device is in STA mode, these fields represent the info
of this station device. And when wlan device is in AP mode, then these
fields repesents the stations that are connected to this AP device.
2025-08-06 09:33:42 -05:00
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
Marcel Holtmann
bff5006b38 Release 3.5 2025-03-26 10:47:40 +01:00
James Prestwood
ea571861d6 auto-t: update scapy imports for newer version
Something changed between scapy versions and now the modules
being imported don't exist.
2025-03-07 10:29:19 -06:00
James Prestwood
f3e4263f51 doc: document how to disable blacklisting 2025-03-04 08:43:54 -06:00
James Prestwood
77639d2d45 blacklist: allow configuration to disable the blacklist
Certain use cases may not need or want this feature so allowing it to
be disabled is a much cleaner way than doing something like setting
the timeouts very low.

Now [Blacklist].InitialTimeout can be set to zero which will prevent
any blacklisting.

In addition some other small changes were added:
 - Warn if the multiplier is 0, and set to 1 if so.
 - Warn if the initial timeout exceeds the maximum timeout.
 - Log if the blacklist is disabled
 - Use L_USEC_PER_SEC instead of magic numbers.
2025-03-04 08:40:53 -06:00
James Prestwood
1662707f22 doc: document [DriverQuirks].SaeDisable 2025-02-13 09:27:20 -06:00
James Prestwood
5f4bf2a5e5 wiphy: add driver quirk to disable SAE
SAE/WPA3 is completely broken on brcmfmac, at least without a custom
kernel patch which isn't included in many OS distributions. In order
to help with this add a driver quirk so devices with brcmfmac can
utilize WPA2 instead of WPA3 and at least connect to networks at
this capacity until the fix is more widely distributed.
2025-02-13 09:26:17 -06:00
Marcel Holtmann
40af18f96a Release 3.4 2025-02-11 00:09:12 +01:00
Marcel Holtmann
ab4fa30c7e build: Require at least version 0.72 when building with external ELL 2025-02-10 23:57:46 +01:00
Marcel Holtmann
43f73823ec build: Include extra files for ELL test extensions 2025-02-10 23:02:10 +01:00
Marcel Holtmann
f4439fd2b6 build: Enable support for TAP, the Test Anything Protocol 2025-02-07 23:01:37 +01:00
James Prestwood
bf82aff039 handshake: add more debugging around PMKSA caching
Instead of just printing the PMKSA pointer separate this into two
separate debug messages, one for if the PMKSA exists and the other
if it does not. In addition print out the MAC of the AP so we have
a reference of which PMKSA this is.
2025-01-06 09:37:14 -06:00
Marcel Holtmann
4b535cee1f Release 3.3 2024-12-20 08:51:07 +01:00
James Prestwood
7144741537 netdev: destroy auth-proto after external auth
With external auth there is no associate event meaning the auth proto
never gets freed, which prevents eapol from starting inside the
OCI callback. Check for this specific case and free the auth proto
after signaling that external auth has completed.
2024-12-19 23:57:21 -06:00
James Prestwood
64b872f363 monitor: add --pcap-size,--pcap-count
The user can now limit the size and count of PCAP files iwmon will
create. This allows iwmon to run for long periods of time without
filling up disk space.
2024-12-17 11:26:38 -06:00
James Prestwood
83a2457550 monitor: add support for limiting PCAP size/count
This implements support for "rolling captures" by allowing iwmon to
limit the PCAP file size and number of PCAP's that are created.
This is a useful feature when long term monitoring is needed. If
there is some rare behavior requiring iwmon to run for days, months,
or longer the resulting PCAP file would become quite large and fill
up disk space.

When enabled (command line arguments in subsequent patch) the PCAP
file size is checked on each write. If it exceeds the limit a new
PCAP file will be created. Once the number of old PCAP files reaches
the set limit the oldest PCAP will be removed from disk.
2024-12-17 11:24:12 -06:00
James Prestwood
43f895142c monitor: track current PCAP size
This will come into play when support for rolling captures is
added to iwmon.
2024-12-17 11:23:56 -06:00
James Prestwood
c352b35bf1 monitor: add --time-format,-t option
For syncing iwmon captures with other logging its useful to
timestamp in some absolute format like UTC. This adds an
option which allows the user to specify what time format to
show. For now support:

delta - (default) The time delta between the first packet
        and the current packet.
utc   - The packet time in UTC
2024-12-17 11:22:03 -06:00
James Prestwood
d4bba5c838 wiphy: add info print for MulticastRxDisabled quirk
This was forgotten in the prior patch set. It does not change any
behavior but just adds this to the driver flags debug print when
IWD starts up.
2024-12-17 11:21:28 -06:00
Marcel Holtmann
1dd9f94713 Release 3.2 2024-11-25 19:04:47 +01:00
James Prestwood
c458e6612d doc: document [DriverQuirks].MulticastRxDisable 2024-11-25 11:47:14 -06:00
James Prestwood
45db339dcd dpp: use wiphy_supports_multicast_rx
The ath10k driver has shown some performance issues, specifically
packet loss, when frame watches are registered with the multicast
RX flag set. This is relevant for DPP which registers for these
when DPP starts (if the driver supports it). This has only been
observed when there are large groups of clients all using the same
wifi channel so its unlikely to be much of an issue for those using
IWD/ath10k and DPP unless you run large deployments of clients.

But for large deployments with IWD/ath10k we need a way to disable
the multicast RX registrations. Now, with the addition of
wiphy_supports_multicast_rx we can both check that the driver
supports this as well as if its been disabled by the driver quirk.
2024-11-25 11:47:07 -06:00
James Prestwood
887d8c8fe8 wiphy: add driver quirk for disabling multicast rx (and helper)
This driver quirk and associated helper API lets other modules both
check if multicast RX is supported, and if its been disabled via
the driver quirk setting.
2024-11-25 11:46:53 -06:00
James Prestwood
c6932efa30 wiphy: make "wiphy" const in wiphy_has_ext_feature 2024-11-25 11:46:49 -06:00
James Prestwood
f3ba82b0e1 doc: document DisablePMKSA option 2024-11-25 08:55:16 -06:00
James Prestwood
a26fcd8f2d auto-t: add PMKSA tests
Adds a test for just PMKSA and testing expiration as well as includes
some PMKSA tests in the SAE roaming test to ensure FT/reassociation
works.
2024-11-25 08:54:57 -06:00
James Prestwood
ab49b404fd station: support PMKSA connections
The actual connection piece of this is very minimal, and only
requires station to check if there is a PMKSA cached, and if so
include the PMKID in the RSNE. Netdev then takes care of the rest.

The remainder of this patch is the error handling if a PMKSA
connection fails with INVALID_PMKID. In this case IWD should retry
the same BSS without PMKSA.

An option was also added to disable PMKSA if a user wants to do
that. In theory PMKSA is actually less secure compared to SAE so
it could be something a user wants to disable. Going forward though
it will be enabled by default as its a requirement from the WiFi
alliance for WPA3 certification.
2024-11-25 08:53:01 -06:00
James Prestwood
9bc71b2853 station: hold reference to handshake object
To prepare for PMKSA support station needs access to the handshake
object. This is because if PMKSA fails due to an expired/missing
PMKSA on the AP station should retry using the standard association.
This poses a problem currently because netdev frees the handshake
prior to calling the connect callback.
2024-11-25 08:52:21 -06:00
James Prestwood
5b104967ce netdev: add support to use PMKSA over SAE if available
This was quite simple and only requiring caching the PMKSA after a
successful handshake, and using the correct authentication type
for connections if we have a prior PMKSA cached.

This is only being added for initial SAE associations for now since
this is where we gain the biggest improvement, in addition to the
requirement by the WiFi alliance to label products as "WPA3 capable"
2024-11-25 08:51:28 -06:00
James Prestwood
4680c0c13b handshake: add handshake_state_remove_pmksa
This is needed in order to clear the PMKSA from the handshake state
without actually putting it back into the cache. This is something
that will be needed in case the AP rejects the association due to
an expired (or forgotten) PMKSA.
2024-11-25 08:50:59 -06:00
Denis Kenzior
c36358cc7c handshake: Add pmksa setter & stealer
The majority of this patch was authored by Denis Kenzior, but
I have appended setting the PMK inside handshake_state_set_pmksa
as well as checking if the pmkid exists in
handshake_state_steal_pmkid.

Authored-by: Denis Kenzior <denkenz@gmail.com>
Authored-by: James Prestwood <prestwoj@gmail.com>
2024-11-25 08:46:51 -06:00
Denis Kenzior
235f6e5f14 pmksa: Add debugging 2024-11-25 08:41:31 -06:00
Denis Kenzior
980e132f48 unit: Add basic pmksa test 2024-11-25 08:37:28 -06:00
Denis Kenzior
900aa5810e pmksa: Add skeleton 2024-11-25 08:34:29 -06:00
James Prestwood
8cf83d6620 auto-t: update testSAE to disable PMKSA
There are quite a few tests here for various scenarios and PMKSA
throws a wrench into that. Rather than potentially breaking the
tests in attempt to get them working with PMKSA, just disable PMKSA.
2024-11-25 08:34:07 -06:00
James Prestwood
ee52bc60ff auto-t: add pmksa_flush() to hostapd module 2024-11-25 08:33:57 -06:00
James Prestwood
3f4a29651e auto-t: always initialize StationDebug in Device class
Since IWD doesn't utilize DBus signals in "normal" operations its
fine to lazy initialize any of the DBus interfaces since properties
can be obtained as needed with Get/GetAll.

For test-runner though StationDebug uses signals for debug events
and until the StationDebug class is initialized (via a method call
or property access) all signals will be lost. Fix this by always
initializing the StationDebug interface when a Device class is
initialized.
2024-11-25 08:33:44 -06:00
James Prestwood
f58cad8cd9 unit: update use of handshake_state with ref/unref 2024-11-25 08:33:30 -06:00
James Prestwood
b9c3feb198 handshake: add ref counting to handshake_state
This adds a ref count to the handshake state object (as well as
ref/unref APIs). Currently IWD is careful to ensure that netdev
holds the root reference to the handshake state. Other modules do
track it themselves, but ensure that it doesn't get referenced
after netdev frees it.

Future work related to PMKSA will require that station holds a
references to the handshake state, specifically for retry logic,
after netdev is done with it so we need a way to delay the free
until station is also done.
2024-11-25 08:32:03 -06:00
James Prestwood
94ebc9d90b station: print client count in scan results 2024-11-20 11:51:32 -06:00
James Prestwood
b0759ebbb2 doc: document [Rank].HighUtilization/StationCount thresholds 2024-11-20 11:51:23 -06:00
James Prestwood
f2ac45eb52 scan: add ranking modifiers for utilization/station count
The utilization rank factor already existed but was very rigid
and only checked a few values. This adds the (optional) ability
to start applying an exponentially decaying factor to both
utilization and station count after some threshold is reached.

This area needs to be re-worked in order to support very highly
loaded networks. If a network either doesn't support client
balancing or does it poorly its left up to the clients to choose
the best BSS possible given all the information available. In
these cases connecting to a highly loaded BSS may fail, or result
in a disconnect soon after connecting. In these cases its likely
better for IWD to choose a slightly lower RSSI/datarate BSS over
the conventionally 'best' BSS in order to aid in distributing
the network load.

The thresholds are currently optional and not enabled by default
but if set they behave as follows:

If the value is above the threshold it is mapped to an integer
between 0 and 30. (using a starting range of <value> - 255).
This integer is then used to index in the exponential decay table
to get a factor between 1 and 0. This factor is then applied to
the rank.

Note that as the value increases above the threshold the rank
will be increasingly effected, as is expected for an exponential
function. These option should be used with care as it may have
unintended consequences, especially with very high load networks.
i.e. you may see IWD roaming to BSS's with much lower signal if
there are high load BSS's nearby.

To maintain the existing behavior if there is no utilization
factor set in main.conf the legacy thresholds/factors will be
used.
2024-11-20 11:47:25 -06:00
James Prestwood
7c5b40ff6b scan: parse station count from BSS load IE
This will be used in BSS ranking
2024-11-20 11:47:15 -06:00
James Prestwood
7465abe5f8 network: use util_exponential_decay 2024-11-20 11:46:46 -06:00
James Prestwood
a910a21beb util: add util_exponential_decay
This is copied from network.c that uses a static table to lookup
exponential decay values by index (generated from 1/pow(n, 0.3)).
network.c uses this for network ranking but it can be useful for
BSS ranking as well if you need to apply some exponential backoff
to a value.
2024-11-20 11:46:43 -06:00
James Prestwood
c40e665094 unit: add linear mapping test 2024-11-20 11:39:12 -06:00
James Prestwood
bb57d61add util: add util_linear_map
This has been needed elsewhere but generally shortcuts could be
taken mapping with ranges starting/ending with zero. This is a
more general linear mapping utility to map values between any
two ranges.
2024-11-20 11:37:14 -06:00
Rudi Heitbaum
fc2965649c anqputil: fix -std=c23 build failure
gcc-15 switched to -std=c23 by default:

    https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=55e3bd376b2214e200fa76d12b67ff259b06c212

As a result `iwd` fails the build as:

    ../src/anqputil.c:134:24: error: incompatible types when returning type '_Bool' but 'char **' was expected
      134 |                 return false;
          |                        ^~~~~

Signed-off-by: Rudi Heitbaum <rudi@heitbaum.com>
2024-11-20 11:36:28 -06:00
Rudi Heitbaum
fa25de4ad1 crypto: fix -std=c23 build failure
gcc-15 switched to -std=c23 by default:

    https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=55e3bd376b2214e200fa76d12b67ff259b06c212

As a result `iwd` fails the build as:

    ../src/crypto.c:1215:24: error: incompatible types when returning type '_Bool' but 'struct l_ecc_point *' was expected
     1215 |                 return false;
          |                        ^~~~~

Signed-off-by: Rudi Heitbaum <rudi@heitbaum.com>
2024-11-20 11:36:20 -06:00
Sergei Trofimovich
901305dcdd wired: fix -std=c23 build failure
gcc-15 switched to -std=c23 by default:

    https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=55e3bd376b2214e200fa76d12b67ff259b06c212

As a result `iwd` fails the build as:

    wired/ethdev.c: In function 'pae_open':
    wired/ethdev.c:340:55:
      error: passing argument 4 of 'l_io_set_read_handler'
        from incompatible pointer type [-Wincompatible-pointer-types]
      340 |         l_io_set_read_handler(pae_io, pae_read, NULL, pae_destroy);
          |                                                       ^~~~~~~~~~~
          |                                                       |
          |                                                       void (*)(void)
    In file included from ...-ell-0.70-dev/include/ell/ell.h:19,
                     from wired/ethdev.c:38:
    ...-ell-0.70-dev/include/ell/io.h:33:68:
      note: expected 'l_io_destroy_cb_t' {aka 'void (*)(void *)'}
        but argument is of type 'void (*)(void)'
       33 |                                 void *user_data, l_io_destroy_cb_t destroy);
          |                                                  ~~~~~~~~~~~~~~~~~~^~~~~~~

C23 changed the meaning of `void (*)()` from partially defined prototype
to `void (*)(void)`.
2024-11-20 11:34:03 -06:00
James Prestwood
b4a4495537 monitor: add better info about the country code IE
The 3rd byte of the country code was being printed as ASCII but this
byte isn't always a printable character. Instead we can check what
the value is and describe what it means from the spec.
2024-11-13 11:40:13 -06:00
Marcel Holtmann
ccd91fe556 Release 3.1 2024-11-07 19:36:18 +01:00
James Prestwood
e89e4d692c general: add .codespellrc file
This is what was used to get clean output from codespell. Common
words/abbreviations and exclusion were added that come up in the
code base.
2024-11-07 19:11:59 +01:00
James Prestwood
0868418ad1 treewide: fix spelling mistakes 2024-11-07 19:11:59 +01:00
James Prestwood
d81de65533 unit: add test with list of known unsupported frequencies
These frequencies were seen being advertised by a driver and IWD has
no operating class/channel mapping for them. Specifically 5960 was
causing issues due to a few bugs and mapping to channel 2 of the 6ghz
band. Those bugs have now been resolved.

If these frequencies can be supported in a clean manor we can remove
this test, but until then ensure IWD does not parse them.
2024-10-24 16:00:36 -05:00
James Prestwood
65073ffcfa util: warn on invalid channels when iterating a frequency set
This should not happen but if it does we should alert the user.
2024-10-24 12:11:39 -05:00
James Prestwood
d0b9fc84b5 band: check the operating class band before checking e4
After the band is established we check the e4 table for the channel
that matches. The problem here is we will end up checking all the
operating classes, even those that are not within the band that was
determined. This could result in false positives and return a
channel that doesn't make sense.
2024-10-24 12:11:31 -05:00
James Prestwood
e0727bfeb6 nl80211util: check band when parsing supported frequencies
When the frequencies/channels were parsed there was no check that the
resulting band matched what was expected. Now, pass the band object
itself in which has the band set to what is expected.
2024-10-24 12:10:27 -05:00
James Prestwood
8e10e00904 band: correct oper class 136 starting frequency
This should be 5925, not 5950
2024-10-24 12:10:23 -05:00
James Prestwood
a2b2f66c4c station: check support for all sysfs settings
If IPv6 is disabled or not supported at the kernel level writing the
sysfs settings will fail. A few of them had a support check but this
patch adds a supported bool to the remainder so we done get errors
like:

Unable to write drop_unsolicited_na to /proc/sys/net/ipv6/conf/wlan0/drop_unsolicited_na
2024-10-24 09:12:12 -05:00
James Prestwood
ca9b7ccaf6 dpp: tie frame registration to DPP state
Similar to several other modules DPP registers for its frame
watches on init then ignores anything is receives unless DPP
is actually running.

Due to some recent issues surrounding ath10k and multicast frames
it was discovered that simply registering for multicast RX frames
causes a significant performance impact depending on the current
channel load.

Regardless of the impact to a single driver, it is actually more
efficient to only register for the DPP frames when DPP starts
rather than when IWD initializes. This prevents any of the frames
from hitting userspace which would otherwise be ignored.

Using the frame-xchg group ID's we can only register for DPP
frames when needed, then close that group and the associated
frame watches.
2024-10-24 09:09:42 -05:00
James Prestwood
354bce64dd frame-xchg: add DPP frame group 2024-10-24 09:09:38 -05:00
James Prestwood
ff4edacb42 frame-xchg: add multicast RX flag argument
DPP optionally uses the multicast RX flag for frame registrations but
since frame-xchg did not support that, it used its own registration
internally. To avoid code duplication within DPP add a flag to
frame_watch_add in order to allow DPP to utilize frame-xchg.
2024-10-24 09:09:25 -05:00
James Prestwood
a6edf6f31e network: fix OWE transition BSS selection
The selection loop was choosing an initial candidate purely for
use of the "fallback_to_blacklist" flag. But we have a similar
case with OWE transitional networks where we avoid the legacy
open network in preference for OWE:

/* Don't want to connect to the Open BSS if possible */
if (!bss->rsne)
	continue;

If no OWE network gets selected we may iterate all BSS's and end
the loop, which then returns NULL.

To fix this move the blacklist check earlier and still ignore any
BSS's in the blacklist. Also add a new flag in the selection loop
indicating an open network was skipped. If we then exhaust all
other BSS's we can return this candidate.
2024-10-23 17:13:36 -05:00
James Prestwood
31787e3788 network: don't allow connection to OWE AKM if disabled 2024-10-23 17:12:57 -05:00
James Prestwood
e98a76aefb wiphy: add OweDisable driver quirk
Some drivers like brcmfmac don't support OWE but from userspace its
not possible to query this information. Rather than completely
blacklist brcmfmac we can allow the user to configure this and
disable OWE in IWD.
2024-10-23 17:07:27 -05:00
Denis Kenzior
4a04d41409 treewide: Comply with doc/coding-style.txt M13 2024-10-23 16:57:28 -05:00
Marcel Holtmann
8bb22a722b Release 3.0 2024-10-20 15:00:20 +02:00
James Prestwood
c459dc75c0 band: add "GB" as a valid alpha2 code
The "UK" alpha2 code is not the official code for the United Kingdom
but is a "reserved" code for compatibility. The official alpha2 is
"GB" which is being added to the EU list. This fixes issues parsing
neighbor reports, for example:

src/station.c:parse_neighbor_report() Neighbor report received for xx:xx:xx:xx:xx:xx: ch 136 (oper class 3), MD not set
Failed to find band with country string 'GB 32' and oper class 3, trying fallback
src/station.c:station_add_neighbor_report_freqs() Ignored: unsupported oper class
2024-10-08 11:14:08 -05:00
Vivek Das Mohapatra
1a554a300d mpdu: tolerate technically illegal but harmless cloned IEs 2024-10-07 13:26:07 -05:00
Vivek Das Mohapatra
6cc6a8a2cb unit: add a test for harmless IE clones
Test handling of technically illegal but harmless cloned IEs.
Based on real traffic captured from retail APs.

As cloned IEs are now allowed the

  "/IE order/Bad (Duplicate + Out of Order IE) 1"

test payload has been altered to be more-wrong so it still fails
verification as expected.
2024-10-07 13:19:38 -05:00
James Prestwood
b0a011d8f4 netdev: fix crash in the RSSI polling fallback workaround
Prior to adding the polling fallback this code path was only used for
signal level list notifications and netdev_rssi_polling_update() was
structured as such, where if the RSSI list feature existed there was
nothing to be done as the kernel handled the notifications.

For certain mediatek cards this is broken, hence why the fallback was
added. But netdev_rssi_polling_update() was never changed to take
this into account which bypassed the timer cleanup on disconnections
resulting in a crash when the timer fired after IWD was disconnected:

iwd: ++++++++ backtrace ++++++++
iwd: #0  0x7b5459642520 in /lib/x86_64-linux-gnu/libc.so.6
iwd: #1  0x7b54597aedf4 in /lib/x86_64-linux-gnu/libc.so.6
iwd: #2  0x49f82d in l_netlink_message_append() at ome/jprestwood/iwd/ell/netlink.c:825
iwd: #3  0x4a0c12 in l_genl_msg_append_attr() at ome/jprestwood/iwd/ell/genl.c:1522
iwd: #4  0x405c61 in netdev_rssi_poll() at ome/jprestwood/iwd/src/netdev.c:764
iwd: #5  0x49cce4 in timeout_callback() at ome/jprestwood/iwd/ell/timeout.c:70
iwd: #6  0x49c2ed in l_main_iterate() at ome/jprestwood/iwd/ell/main.c:455 (discriminator 2)
iwd: #7  0x49c3bc in l_main_run() at ome/jprestwood/iwd/ell/main.c:504
iwd: #8  0x49c5f0 in l_main_run_with_signal() at ome/jprestwood/iwd/ell/main.c:632
iwd: #9  0x4049ed in main() at ome/jprestwood/iwd/src/main.c:614
iwd: #10 0x7b5459629d90 in /lib/x86_64-linux-gnu/libc.so.6
iwd: #11 0x7b5459629e40 in /lib/x86_64-linux-gnu/libc.so.6
iwd: +++++++++++++++++++++++++++

To fix this we need to add checks for the cqm_poll_fallback flag in
netdev_rssi_polling_update().
2024-10-03 21:32:23 -05:00
James Prestwood
a27b7823df manager: fix UseDefaultInterface warning
This logic was incorrect here, we only want to print if the option
is used, not if its unset.
2024-09-25 09:52:38 -05:00
Denis Kenzior
14b9291490 sae: Allow ability to force Group 19 / Hunt and Peck 2024-09-22 16:00:04 -05:00
Denis Kenzior
354200f9da netdev: external auth support
Certain FullMAC drivers do not expose CMD_ASSOCIATE/CMD_AUTHENTICATE,
but lack the ability to fully offload SAE connections to the firmware.
Such connections can still be supported on such firmware by using
CMD_EXTERNAL_AUTH & CMD_FRAME.  The firmware sets the
NL80211_FEATURE_SAE bit (which implies support for CMD_AUTHENTICATE, but
oh well), and no other offload extended features.

When CMD_CONNECT is issued, the firmware sends CMD_EXTERNAL_AUTH via
unicast to the owner of the connection.  The connection owner is then
expected to send SAE frames with the firmware using CMD_FRAME and
receive authenticate frames using unicast CMD_FRAME notifications as
well.  Once SAE authentication completes, userspace is expected to
send a final CMD_EXTERNAL_AUTH back to the kernel with the corresponding
status code.  On failure, a non-0 status code should be used.

Note that for historical reasons, SAE AKM sent in CMD_EXTERNAL_AUTH is
given in big endian order, not CPU order as is expected!
2024-09-22 15:59:20 -05:00
James Prestwood
acc5daf0e2 netdev: allow empty TX/RX bitrate attributes
The TX or RX bitrate attributes can contain zero nested attributes.
This causes netdev_parse_bitrate() to fail, but this shouldn't then
cause the overall parsing to fail (we just don't have those values).

Fix this by continuing to parse attributes if either the TX/RX
bitrates fail to parse.
2024-09-20 10:26:53 -05:00
Marcel Holtmann
5c22ab6621 Release 2.22 2024-09-11 17:49:36 +02:00
James Prestwood
af99fbb6c0 station: fix printing uint64_t by using PRIx64
This fixed non 64-bit builds. In addition the formatting for the
seconds integer was changed to %d, since its an int.
2024-09-11 09:44:46 -05:00
James Prestwood
4b2c6de45c station: fix crash if affinities watch gets removed
If the affinity watch is removed by setting an empty list the
disconnect callback won't be called which was the only place
the watch ID was cleared. This resulted in the next SetProperty call
to think a watch existed, and attempt to compare the sender address
which would be NULL.

The watch ID should be cleared inside the destroy callback, not
the disconnect callback.
2024-09-10 19:42:03 -05:00
James Prestwood
30cc3ecf7b station: emit property changed for connected AP on roaming
This was only done for connecting states, but needs to also be done
for roaming.
2024-09-10 19:42:03 -05:00
Marcel Holtmann
87a8884f25 Release 2.21 2024-09-09 09:09:29 +02:00
Marcel Holtmann
4f2bf0b0a6 build: Require at least version 0.69 when building with external ELL 2024-09-09 08:54:10 +02:00
James Prestwood
184c3efcb3 dpp: set cap on the PKEX timeout, and reduce once PKEX finishes
If we scan a huge number of frequencies the PKEX timeout can get
rather large. This was overlooked in a prior patch who's intent
was to reduce the PKEX time, but in these cases it increased it.
Now the timeout will be capped at 2 minutes, but will still be
as low as 10 seconds for a single frequency.

In addition there was no timer reset once PKEX was completed.
This could cause excessive waits if, for example, the peer left
the channel mid-authentication. IWD would just wait until the
long PKEX timeout to eventually reset DPP. Once PKEX completes
we can assume that this peer will complete authentication quickly
and if not, we can fail.
2024-09-08 17:26:54 -05:00
James Prestwood
3f06d0128a scan: check pending requests after regdom update
While there is proper handling for a regdom update during a
TRIGGER_SCAN scan, prior to NEW_SCAN_RESULTS there is no such
handling if the regdom update comes in during a GET_SCAN or
GET_SURVEY.

In both the 6ghz and non-6ghz code paths we have some issues:
  - For non-6ghz devices, or regdom updates that did not enable
    6ghz the wiphy state watch callback will automatically issues
    another GET_SURVEY/GET_SCAN without checking if there was
    already one pending. It does this using the current scan request
    which gets freed by the prior GET_SCAN/GET_SURVEY calls when
    they complete, causing invalid reads when the subsequent calls
    finish.
 - If 6ghz was enabled by the update we actually append another
   trigger command to the list and potentially run it if its the
   current request. This also will end up in the same situation as
   the request is freed by the pending GET_SURVEY/GET_SCAN calls.

For the non-6ghz case there is little to no harm in ignoring the
regdom update because its very unlikely it changed the allowed
frequencies.

For the 6ghz case we could potentially handle the new trigger scan
within get_scan_done, but thats beyond the scope of this change
and is likely quite intrusive.
2024-09-06 14:00:30 -05:00
James Prestwood
3bc8b90c0e scan: don't survey on external scans
Since surveys end up making driver calls in the kernel its not
entirely known how they are implemented or how long they will
take. For this reason the survey will be skipped if getting the
results from an external scan.

Doing this also fixes a crash caused by external scans where the
scan request pointer is not checked and dereferenced:

0x00005ffa6a0376de in get_survey_done (user_data=0x5ffa783a3f90) at src/scan.c:2059
0x0000749646a29bbd in ?? () from /usr/lib/libell.so.0
0x0000749646a243cb in ?? () from /usr/lib/libell.so.0
0x0000749646a24655 in l_main_iterate () from /usr/lib/libell.so.0
0x0000749646a24ace in l_main_run () from /usr/lib/libell.so.0
0x0000749646a263a4 in l_main_run_with_signal () from /usr/lib/libell.so.0
0x00005ffa6a00d642 in main (argc=<optimized out>, argv=<optimized out>) at src/main.c:614

Reported-by: Daniel Bond <danielbondno@gmail.com>
2024-09-06 14:00:07 -05:00
James Prestwood
f6cfcb8ca2 dpp: use peer_addr for pkex exchange request
This was hard coded to broadcast and missed in the initial changes
to support starting PKEX to a specific peer.
2024-09-06 13:59:29 -05:00
James Prestwood
163c2ebd37 netdev: fix potential command ID overwrite setting CQM threshold
With the introduction of affinities the CQM threshold can be toggled
by a DBus call. There was no check if there was already a pending
call which would cause the command ID to be overwritten and lose any
potential to cancel it, e.g. if netdev went down.
2024-09-04 22:24:42 -05:00
James Prestwood
154a29be05 netdev: fall back to RSSI polling if SET_CQM fails
Some drivers fail to set a CQM threshold and report not supported.
Its unclear exactly why but if this happens roaming is effectively
broken.

To work around this enable RSSI polling if -ENOTSUP is returned.
The polling callback has been changed to emit the HIGH/LOW signal
threshold events instead of just the RSSI level index, just as if
a CQM event came from the kernel.
2024-09-04 22:24:18 -05:00
James Prestwood
23cf6107c6 monitor: fix build with INGRESS/EGRESS definitions
These new values are undefined on older kernels (e.g. 5.15)
2024-09-04 22:07:33 -05:00
James Prestwood
c66438e34f auto-t: add tests for Affinities behavior 2024-09-03 10:24:33 -05:00
James Prestwood
c778ddf0c2 auto-t: add affinities property for station, and extended_service_set 2024-09-03 10:24:28 -05:00
James Prestwood
2ad9561069 station: Use Affinities property to change roaming threshold
When the affinity is set to the current BSS lower the roaming
threshold to loosly lock IWD to the current BSS. The lower
threshold is automatically removed upon roaming/disconnection
since the affinity array is also cleared out.
2024-09-03 10:24:15 -05:00
James Prestwood
f4ec1ee509 station: add Affinities DBus property
This property will hold an array of object paths for
BasicServiceSet (BSS) objects. For the purpose of this patch
only the setter/getter and client watch is implemented. The
purpose of this array is to guide or loosely lock IWD to certain
BSS's provided that some external client has more information
about the environment than what IWD takes into account for its
roaming decisions.

For the time being, the array is limited to only the connected
BSS path, and any roams or disconnects will clear the array.

The intended use case for this is if the device is stationary
an external client could reduce the likelihood of roaming by
setting the affinity to the current BSS.
2024-09-03 10:19:02 -05:00
James Prestwood
b98bc30c23 dbus: add PermissionDenied DBus error 2024-09-03 10:18:56 -05:00
James Prestwood
4c3cbdc8d3 doc: Document station Affinities property
This documents new DBus property that expose a bit more control to
how IWD roams.

Setting the affinity on the connected BSS effectively "locks" IWD to
that BSS (except at critical RSSI levels, explained below). This can
be useful for clients that have access to more information about the
environment than IWD. For example, if a client is stationary there
is likely no point in trying to roam until it has moved elsewhere.

A new main.conf option would also be added:

[General].CriticalRoamThreshold

This would be the new roam threshold set if the currently connected
BSS is in the Affinities list. If the RSSI continues to drop below
this level IWD will still attempt to roam.
2024-09-03 10:18:50 -05:00
James Prestwood
61cba6bd28 station: check for roam timeout before rearming
A user reported a crash which was due to the roam trigger timeout
being overwritten, followed by a disconnect. Post-disconnect the
timer would fire and result in a crash. Its not clear exactly where
the overwrite was happening but upon code inspection it could
happen in the following scenario:

1. Beacon loss event, start roam timeout
2. Signal low event, no check if timeout is running and the timeout
   gets overwritten.

The reported crash actually didn't appear to be from the above
scenario but something else, so this logic is being hardened and
improved

Now if a roam timeout already exists and trying to be rearmed IWD
will check the time remaining on the current timer and either keep
the active timer or reschedule it to the lesser of the two values
(current or new rearm time). This will avoid cases such as a long
roam timer being active (e.g. 60 seconds) followed by a beacon or
packet loss event which should trigger a more agressive roam
schedule.
2024-09-03 10:16:25 -05:00
James Prestwood
574b0d80dc station: don't allow FT-over-Air without offchannel support
If CMD_REMAIN_ON_CHANNEL isn't supported, don't allow FT-over-Air
2024-09-03 10:08:05 -05:00
James Prestwood
0c228f4465 wiphy: add flag for supporting remain on channel 2024-09-03 10:07:55 -05:00
Marcel Holtmann
1ac3915641 Release 2.20 2024-08-29 10:41:03 +02:00
James Prestwood
e9ac7ab378 netdev: add critical signal threshold level
This adds a secondary set of signal thresholds. The purpose of these
are to provide more flexibility in how IWD roams. The critical
threshold is intended to be temporary and is automatically reset
upon any connection changes: disconnects, roams, or new connections.
2024-08-27 21:41:17 -05:00
James Prestwood
7ba5b0f924 netdev: store signal threshold in netdev object, not globally
This prepares for the ability to toggle between two signal
thresholds in netdev. Since each netdev may not need/want the
same threshold store it in the netdev object rather than globally.
2024-08-27 21:40:29 -05:00
James Prestwood
54b6330845 netdev: define netdev settings in netdev.h
Following knownnetworks, this moves the settings into a header file
which is easier to maintain/read.
2024-08-27 21:40:05 -05:00
James Prestwood
b5aff74e3b dpp: scale PKEX timeout by the number of frequencies used
If the number of frequencies used is very small reduce the timeout
to avoid waiting for extended periods of time.
2024-08-27 21:25:21 -05:00
James Prestwood
294426b450 dpp: allow PKEX configurators to run without multicast RX support
Since IWD enrollees can send unicast frames, a PKEX configurator could
still run without multicast support. Using this combination basically
allows any driver to utilize DPP/PKEX assuming the MAC address can
be communicated using some out of band mechanism.
2024-08-27 21:25:15 -05:00
James Prestwood
4482b8dc24 dpp: add Address/Frequency as parameters to PKEX enrollees
The DPP spec allows for obtaining frequency and MAC addresses up
to the implementation. IWD already takes advantage of this by
first scanning for nearby APs and using only those frequencies.
For further optimization an enrollee may be able to determine the
configurators frequency and MAC ahead of time which would make
finding the configurator much faster.
2024-08-27 21:24:48 -05:00
James Prestwood
bf2441e311 dpp: factor out key derivation and starting PKEX into functions
This will make things a bit easier in future patches, and reduces
some of the length/complexity of these functions.
2024-08-27 21:24:33 -05:00
James Prestwood
95a9e052de scan: remove legacy "Ghz" band modifier settings 2024-08-27 21:23:46 -05:00
James Prestwood
5c7777ff0f manager: deprecate UseDefaultInterface 2024-08-27 21:22:54 -05:00
James Prestwood
d223f49fbc doc: deprecate UseDefaultInterface in docs 2024-08-27 21:22:49 -05:00
Denis Kenzior
e5c0e18751 monitor: Print rmnet flags 2024-08-23 12:30:57 -05:00
Denis Kenzior
5f74ed75e7 nl80211util: Add builder for CMD_EXTERNAL_AUTH
This is for sending status from the STA to the driver
2024-08-23 11:18:15 -05:00
Denis Kenzior
17fbab110c nl80211util: support attributes in CMD_EXTERNAL_AUTH 2024-08-23 11:18:10 -05:00
Denis Kenzior
02ec70e290 monitor: Don't skip genl control Done,Error messages 2024-08-23 11:17:30 -05:00
Denis Kenzior
5118f08d79 monitor: Don't dump survey results with noscan
Survey is used after each scan, creating quite a bit of spam.  Silence
survey results if noscan is set.
2024-08-23 11:17:26 -05:00
Denis Kenzior
e565b75032 defs: Add defs.h to hold certain global definitions
This will help to get rid of magic number use throughout the project.
The definitions should be limited to global magic numbers that are used
throughout the project, for example SSID length, MAC address length,
etc.
2024-08-23 11:17:20 -05:00
James Prestwood
db9c0480ef station: emit property changed for ConnectedAccessPoint
This was missed in a prior patch set. When station is connecting
or disconnecting ConnectedAccessPoint property change should be
emitted.
2024-08-23 11:09:59 -05:00
Marcel Holtmann
10f5bc9be7 build: Require at least version 0.68 when building with external ELL 2024-08-22 17:13:48 +02:00
James Prestwood
548ef00291 auto-t: Add test for BasicServiceSets 2024-08-19 11:43:30 -05:00
James Prestwood
93806cd522 auto-t: Add ExtendedServiceSet property 2024-08-19 11:43:27 -05:00
James Prestwood
1a3a035404 network: add back network_bss_list_clear
Rename network_bss_update_start back to network_bss_list_clear, since
this is what its now doing again.
2024-08-19 11:43:24 -05:00
James Prestwood
6d94599977 network: remove BasicServiceSet DBus registration code
This was moved into station.
2024-08-19 11:43:24 -05:00
James Prestwood
c639bf0b19 station: move BasicServiceSet DBus management into station
Due to an unnoticed bug after adding the BasicServiceSet object into
network, it became clear that since station already owns the scan_bss
objects it makes sense for it to manage the associated DBus objects
as well. This way network doesn't have to jump through hoops to
determine if the scan_bss object was remove, added, or updated. It
can just manage its list as it did prior.

From the station side this makes things very easy. When scan results
come in we either update or add a new DBus object. And any time a
scan_bss is freed we remove the DBus object.
2024-08-19 11:43:24 -05:00
James Prestwood
514e483bc3 network: add __network_path_append_bss
To reduce code duplication and prepare for moving the BSS interface
to station, add a new API so station can create a BSS path without
a network object directly.
2024-08-19 11:43:24 -05:00
Denis Kenzior
7604762013 eapol: Fix bogus warning
src/eapol.c:1041:9: error: ‘buf’ may be used uninitialized [-Werror=maybe-uninitialized]
 1041 |         l_put_be16(0, &frame->header.packet_len);
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This warning is bogus since the buffer is initialized through use of
eapol_frame members.  EAPoL-Start is a very simple frame.
2024-08-19 11:43:24 -05:00
James Prestwood
f81855349a auto-t: add SAE-H2E test for setting the default ECC group 2024-08-12 16:19:16 -05:00
James Prestwood
1455988efb sae: support default group for H2E
This was seemingly trivial at face value but doing so ended up
pointing out a bug with how group_retry is set when forcing
the default group. Since group_retry is initialized to -1 the
increment in the force_default_group block results in it being
set to zero, which is actually group 20, not 19. This did not
matter for hunt and peck, but H2E actually uses the retry value
to index its pre-generated points which then breaks SAE if
forcing the default group with H2E.

To handle H2E and force_default_group, the group selection
logic will always begin iterating the group array regardless of
SAE type.
2024-08-12 13:13:00 -05:00
James Prestwood
d2f14b2951 client: add station command "get-bsses"
This command will get the BasicServiceSet list for a given network.
If no network is supplied, its assumed to be the current network.
2024-08-12 12:10:58 -05:00
James Prestwood
73c79dbd41 client: refactor cmd_connect() and add find_network()
This will do all the lookup/searching needed to find a network
proxy object.
2024-08-12 12:10:58 -05:00
James Prestwood
0d7ff8ebd9 client: add BasicServiceSet interface 2024-08-12 12:10:58 -05:00
James Prestwood
05166d0999 client: Add BasicServiceSets property to network
The property itself is an array of paths, but this is difficult
to fit nicely within a terminal. Instead just display the count
of BSS's. Displaying a detailed list of BSS's will be done via
a separate command.
2024-08-12 12:10:58 -05:00
James Prestwood
0e3322b5da client: add net.connman.iwd.BasicServiceSet definition 2024-08-12 12:10:55 -05:00
James Prestwood
a7d8b9a068 client: separate property header and values into two functions
There are certain cases where we may not want to display the entire
header for a given set of properties. For example displaying a list
of proxy interfaces. Add finer control by separating out the header
and the prop/value display into two functions.
2024-08-12 12:10:50 -05:00
James Prestwood
b31e298df4 doc: document BasicServiceSet API 2024-08-12 12:10:47 -05:00
James Prestwood
a73b877c5b station: add ConnectedAccessPoint property
This property is a path that corresponds to a BasicServiceSet
object, the one the station is currently connected to.
2024-08-12 12:10:44 -05:00
James Prestwood
68e1d055dd network: remove network_bss_list_clear 2024-08-12 12:10:40 -05:00
James Prestwood
aca92df19b network: add ExtendedServiceSet DBus property
This contains a list of all BasicServiceSet objects (paths) that
exist for a network.
2024-08-12 12:10:28 -05:00
James Prestwood
560ad581ad station: use network_bss_{start,stop}_update
This will tell network the BSS list is being updated and it can
act accordingly as far as the BSS DBus registrations/unregistration.

In addition any scan_bss object needing to be freed has to wait
until after network_bss_stop_update() because network has to be able
to iterate its old list and unregister any BSS's that were not seen
in the scan results. This is done by pushing each BSS needing to be
freed into a queue, then destroying them after the BSS's are all
added.
2024-08-12 12:10:10 -05:00
James Prestwood
fe2a91ae11 network: Add BasicServiceSet object
This adds a new DBus object/interface for tracking BSS's for
a given network. Since scanning replaces scan_bss objects some
new APIs were added to avoid tearing down the associated DBus
object for each BSS.

network_bss_start_update() should be called before any new BSS's
are added to the network object. This will keep track of the old
list and create a new network->bss_list where more entries can
be added. This is effectively replacing network_bss_list_clear,
except it keeps the old list around until...

network_bss_stop_update() is called when all BSS's have been
added to the network object. This will then iterate the old list
and lookup if any BSS DBus objects need to be destroyed. Once
completed the old list is destroyed.
2024-08-12 12:01:57 -05:00
James Prestwood
cd1507620f dbus: Add net.connman.iwd.BasicServiceSet interface 2024-08-12 12:01:34 -05:00
Denis Kenzior
7ee7848a96 netdev: Simplify FILS handling in netdev_connect_common 2024-08-05 09:07:02 -05:00
Denis Kenzior
be7b19d587 fils: Ensure capability checks are consistent
iwd supports FILS only on softmac drivers.  Ensure the capability check
is consistent between wiphy and netdev, both the softmac and the
relevant EXT_FEATURE bit must be checked.

CMD_EXTERNAL_AUTH could potentially be used for FILS for FullMAC cards,
but no hardware supporting this has been identified yet.
2024-08-05 09:06:54 -05:00
Denis Kenzior
16f5bbc20a netdev: Create owe_sm for fullmac connections
Somehow this ability was lost in the refactoring.  OWE was intended to
be used on fullmac cards, but the state machine is only actually created
if the connection type ends up being softmac.

Fixes: 8b6ad5d3b9ec ("owe: netdev: refactor to remove OWE as an auth-proto")
2024-08-05 09:06:43 -05:00
Denis Kenzior
8a27cff8c0 ie: Add IE_AKM_IS_OWE
Similarly to IE_AKM_IS_SAE, IE_AKM_IS_FILS, etc
2024-08-05 09:06:39 -05:00
Denis Kenzior
d43f05224d wiphy: Fix use of wiphy_has_feature
Features with the _EXT_ in the name must be queried using the
wiphy_has_ext_feature method.

Fixes: bc7b12d1a4a7 ("wiphy: handle FILS AKMs")
2024-08-05 09:06:31 -05:00
Denis Kenzior
96492483ee monitor: Mask flags from attribute identifier
Certain flags (for example, NLA_F_NESTED) are ORed with the netlink
attribute type identifier prior to being sent on the wire.  Such flags
need to be masked off and not taken into consideration when attribute
type is being compared against known values.
2024-08-05 09:06:25 -05:00
Denis Kenzior
194d8a3052 monitor: Decode RMNet Mux Identifier 2024-08-05 09:06:20 -05:00
James Prestwood
d500a44ff3 station: improve zero oper class neighbor report workaround
The workaround for Cisco APs reporting an operating class of zero
is still a bug that remains in Cisco equipment. This is made even
worse with the introduction of 6GHz where the channel numbers
overlap with both 2.4 and 5GHz bands. This makes it impossible to
definitively choose a frequency given only a channel number.

To improve this workaround and cover the 6GHz band we can calculate
a frequency for each band and see what is successful. Then append
each frequency we get to the list. This will result in more
frequencies scanned, but this tradeoff is better than potentially
avoiding a roam to 6GHz or high order 5ghz channel numbers.
2024-08-05 09:05:49 -05:00
Denis Kenzior
faa6a392e4 monitor: Update to the new ell l_netlink_send API 2024-07-26 17:37:29 -05:00
Denis Kenzior
e33fa63243 netdev: Update to the new l_netlink_send API 2024-07-26 17:37:29 -05:00
Denis Kenzior
d7f669dd7c wired: Update to the new l_netlink_send API 2024-07-26 17:37:29 -05:00
Denis Kenzior
993e48cab2 qemu: Remove unsupported command line arguments
[denkenz@archdev ~]$ qemu-system-x86_64 --version
QEMU emulator version 9.0.1
Copyright (c) 2003-2024 Fabrice Bellard and the QEMU Project developers

QEMU now seems to complain that 'no-hpet' and 'no-acpi' command line
arguments are unrecognized.
2024-07-26 10:06:13 -05:00
Denis Kenzior
9762c61b4d monitor: Use genl APIs instead of open coding
l_genl class has nice ways of discovering and requesting families.  The
genl functionality has been added after the iwmon skeleton was created,
but it is now time to migrate to using these APIs.
2024-07-26 10:04:05 -05:00
James Prestwood
d8803b309d monitor: fix the CQM RSSI threshold attribute
This attribute is actually an array of signed 32 bit integers and it
was being treated as a single integer. This would work until more
than one threshold was set, then it would fail to parse it.
2024-07-26 09:52:25 -05:00
James Prestwood
f9a55e3728 auto-t: fix several DPP tests after station state changes
After the station state changes in DPP setting autoconnect=True was
causing DPP to stop prior to being able to scan for the network.
Instead we can start autoconnect earlier so we aren't toggling the
property while DPP is running.
2024-07-24 15:26:57 -05:00
James Prestwood
a63fd6abb9 auto-t: add DPP tests for state change checks 2024-07-24 15:26:55 -05:00
James Prestwood
eff2a2afcf dpp: explicitly disconnect station if enrollee is started
Prior to now the DPP state was required to be disconnected before
DPP would start. This is inconvenient for the user since it requires
extra state checking and/or DBus method calls. Instead model this
case like WSC and issue a disconnect to station if DPP is requested
to start.

The other conditions on stopping DPP are also preserved and no
changes to the configurator role have been made, i.e. being
disconnected while configuring still stops DPP. Similarly any
connection made during enrolling will stop DPP.

It should also be noted that station's autoconfigure setting is also
preserved and set back to its original value upon DPP completing.
2024-07-24 15:25:31 -05:00
James Prestwood
5cdad6ab86 station: add station_get_autoconnect
Gets the current autoconenct setting. This is not the current
autoconnect state. Will be used in DPP to reset station's autoconnect
setting back to what it was prior to DPP, in case of failure.
2024-07-24 15:25:28 -05:00
James Prestwood
30ca00d2c4 dpp: factor out PKEX/DPP start prep into function
In order to slightly rework the DPP state machine to handle
automatically disconnecting (for enrollees) functions need to be
created that isolate everything needed to start DPP/PKEX in case
a disconnect needs to be done first.
2024-07-24 15:25:25 -05:00
James Prestwood
fbf8ed4140 auto-t: don't print valgrind log if there was no error
If valgrind didn't report any issues, don't dump the logs. This
makes the test run a lot easier to look at without having to scroll
through pages of valgrind logs that provide no value.
2024-07-24 09:14:09 -05:00
James Prestwood
64d68b4f08 scan: fix invalid read when canceling an ongoing scan
When the survey code was added it neglected to add the same
cancelation logic that existed for the GET_SCAN call, i.e. if
a scan was canceled and there was a pending GET_SURVEY to the
kernel that needs to be canceled, and the request cleaned up.

Fixes: 35808debae ("scan: use GET_SURVEY for SNR calculation in ranking")
2024-07-24 09:12:42 -05:00
Yuki Sireneva
91f7253ae1 client: use display_table_row for IPv6 in station
IPv6 address entry was not updated to use display_table_row which led to
a shifted line in table, as shown below:

    $ iwctl station wlan0 show | head | sed 's| |.|g'
    .................................Station:.wlan0................................
    --------------------------------------------------------------------------------
    ..Settable..Property..............Value..........................................
    --------------------------------------------------------------------------------
    ............Scanning..............no...............................................
    ............State.................connected........................................
    ............Connected.network.....Clannad.Legacy...................................
    ............IPv4.address..........192.168.1.12.....................................
    ............IPv6.address........fdc3:541d:864f:0:96db:c9ff:fe36:b15............
    ............ConnectedBss..........cc:d8:43:77:91:0e................................

This patch aligns IPv6 address line with other lines in the table.

Fixes: 35dd2c08219a (client: update station to use display_table_row, 2022-07-07)
2024-07-23 18:52:51 -05:00
James Prestwood
06c8080664 auto-t: a few random autotest fixes
testEncryptedProfiles:
 - This would occationally fail because the test is expecting
   to explicitly connect but after the first failed connection
   autoconnect takes over and its a race to connect.
testPSK-roam:
 - Several rules were not being cleaned up which could cause
   tests afterwards to fail
 - The AP roam test started failing randomly because of the SNR
   ranking changes. It appears that with hwsim _sometimes_ the
   SNR is able to be determined which can effect the ranking. This
   test assumed the two BSS's would be the same ranking but the
   SNR sometimes causes this to not be true.
2024-07-18 16:08:46 -05:00
James Prestwood
b7da34a5d2 auto-t: Add deauth during the 4-way handshake test
This test will fail with current upstream as IWD hangs when the
deauthenticate event arrives. Once this is fixed the test should
pass.
2024-07-18 16:08:43 -05:00
James Prestwood
a972b77dc2 auto-t: add reason/test arguments to hostapd deauthenticate
The reason code is obvious, but the test argument is actually a
toggle to send the frame encrypted or unencrypted.
2024-07-18 16:08:40 -05:00
James Prestwood
d27de1b111 auto-t: add clear_events() to IWD class
The wait_for_event() function allows past events to cause this
function to return immediately. This behavior is known, and
relied on for some tests. But in some cases you want to only
handle _new_ events, so we need a way to clear out prior events.
2024-07-18 16:08:36 -05:00
James Prestwood
d2cda84383 station: add handshake-started debug event 2024-07-18 16:08:32 -05:00
James Prestwood
af1b017003 eapol: move HANDSHAKE_STARTED_EVENT to eapol_start()
This event is not used anywhere and can be leveraged in autotesting.
Move the event to eapol_start() so it gets called unconditionally
when the 4-way handshake is started.
2024-07-18 16:08:17 -05:00
James Prestwood
556f90ec28 netdev: handle disconnect event during a connection
If a disconnect arrives at any point during the 4-way handshake or
key setting this would result in netdev sending a disconnect event
to station. If this is a reassociation this case is unhandled in
station and causes a hang as it expects any connection failure to
be handled via the reassociation callback, not a random disconnect
event.

To handle this case we can utilize netdev_disconnected() along with
the new NETDEV_RESULT_DISCONNECTED result to ensure the connect
callback gets called if it exists (indicating a pending connection)

Below are logs showing the "Unexpected disconnect event" which
prevents IWD from cleaning up its state and ultimately results in a
hang:

Jul 16 18:16:13: src/station.c:station_transition_reassociate()
Jul 16 18:16:13: event: state, old: connected, new: roaming
Jul 16 18:16:13: src/wiphy.c:wiphy_radio_work_done() Work item 65 done
Jul 16 18:16:13: src/wiphy.c:wiphy_radio_work_next() Starting work item 66
Jul 16 18:16:13: src/netdev.c:netdev_mlme_notify() MLME notification Del Station(20)
Jul 16 18:16:13: src/netdev.c:netdev_link_notify() event 16 on ifindex 6
Jul 16 18:16:13: src/netdev.c:netdev_mlme_notify() MLME notification Deauthenticate(39)
Jul 16 18:16:13: src/netdev.c:netdev_deauthenticate_event()
Jul 16 18:16:13: src/netdev.c:netdev_mlme_notify() MLME notification New Station(19)
Jul 16 18:16:13: src/station.c:station_netdev_event() Associating
Jul 16 18:16:13: src/netdev.c:netdev_mlme_notify() MLME notification Authenticate(37)
Jul 16 18:16:13: src/netdev.c:netdev_authenticate_event()
Jul 16 18:16:13: src/netdev.c:netdev_mlme_notify() MLME notification Associate(38)
Jul 16 18:16:13: src/netdev.c:netdev_associate_event()
Jul 16 18:16:13: src/netdev.c:netdev_link_notify() event 16 on ifindex 6
Jul 16 18:16:13: src/netdev.c:netdev_mlme_notify() MLME notification Connect(46)
Jul 16 18:16:13: src/netdev.c:netdev_connect_event()
Jul 16 18:16:13: src/netdev.c:netdev_connect_event() aborting and ignore_connect_event not set, proceed
Jul 16 18:16:13: src/netdev.c:netdev_connect_event() expect_connect_failure not set, proceed
Jul 16 18:16:13: src/netdev.c:parse_request_ies()
Jul 16 18:16:13: src/netdev.c:netdev_connect_event() Request / Response IEs parsed
Jul 16 18:16:13: src/netdev.c:netdev_get_oci()
Jul 16 18:16:13: src/netdev.c:netdev_link_notify() event 16 on ifindex 6
Jul 16 18:16:13: src/netdev.c:netdev_link_notify() event 16 on ifindex 6
Jul 16 18:16:13: src/netdev.c:netdev_link_notify() event 16 on ifindex 6
Jul 16 18:16:13: src/netdev.c:netdev_get_oci_cb() Obtained OCI: freq: 5220, width: 3, center1: 5210, center2: 0
Jul 16 18:16:13: src/eapol.c:eapol_start()
Jul 16 18:16:13: src/netdev.c:netdev_unicast_notify() Unicast notification Control Port Frame(129)
Jul 16 18:16:13: src/netdev.c:netdev_control_port_frame_event()
Jul 16 18:16:13: src/eapol.c:eapol_handle_ptk_1_of_4() ifindex=6
Jul 16 18:16:13: src/netdev.c:netdev_mlme_notify() MLME notification Control Port TX Status(139)
Jul 16 18:16:14: src/netdev.c:netdev_mlme_notify() MLME notification Notify CQM(64)
Jul 16 18:16:14: src/netdev.c:netdev_cqm_event() Signal change event (above=1 signal=-60)
Jul 16 18:16:17: src/netdev.c:netdev_link_notify() event 16 on ifindex 6
Jul 16 18:16:17: src/netdev.c:netdev_mlme_notify() MLME notification Del Station(20)
Jul 16 18:16:17: src/netdev.c:netdev_mlme_notify() MLME notification Deauthenticate(39)
Jul 16 18:16:17: src/netdev.c:netdev_deauthenticate_event()
Jul 16 18:16:17: src/netdev.c:netdev_mlme_notify() MLME notification Disconnect(48)
Jul 16 18:16:17: src/netdev.c:netdev_disconnect_event()
Jul 16 18:16:17: Received Deauthentication event, reason: 15, from_ap: true
Jul 16 18:16:17: src/wiphy.c:wiphy_radio_work_done() Work item 66 done
Jul 16 18:16:17: src/station.c:station_disconnect_event() 6
Jul 16 18:16:17: Unexpected disconnect event
Jul 16 18:16:17: src/netdev.c:netdev_link_notify() event 16 on ifindex 6
Jul 16 18:16:17: src/wiphy.c:wiphy_reg_notify() Notification of command Reg Change(36)
Jul 16 18:16:17: src/wiphy.c:wiphy_update_reg_domain() New reg domain country code for (global) is XX
2024-07-18 16:07:41 -05:00
James Prestwood
22f238706c station: update logic for handshake failure
After adding the NETDEV_RESULT_DISCONNECTED enum, handshake failures
initiated by the AP come in via this result so the existing logic
to call network_connect_failed() was broken. We could still get a
handshake failure generated internally, so that has been preserved
(via NETDEV_RESULT_HANDSHAKE_FAILED) but a check for a 4-way
handshake timeout reason code was also added.
2024-07-18 16:06:41 -05:00
James Prestwood
906afefbf3 station: handle NETDEV_RESULT_DISCONNECTED
This new event is sent during a connection if netdev recieves a
disconnect event. This patch cleans up station to handle this
case and leave the existing NETDEV_EVENT_DISCONNECTED_BY_{AP,SME}
handling only for CONNECTED, NETCONFIG, and FW_ROAMING states.
2024-07-18 16:05:37 -05:00
James Prestwood
38c36ff145 netdev: add NETDEV_RESULT_DISCONNECTED
This new result is meant to handle cases where a disconnect
event (deauth/disassoc) was received during an ongoing connection.
Whether that's during authentication, association, the 4-way
handshake, or key setting.
2024-07-18 16:05:14 -05:00
James Prestwood
9ea0117dc4 station: print unknown channel number in neighbor report
If the channel number resulted in a failure to parse the neighbor
report entry, print it for debugging.
2024-07-18 16:05:01 -05:00
Kasper Kantz
4f81953338 p2putil: remove static from const strlen
src/p2putil.c: In function 'p2p_get_random_string':
 src/p2putil.c:2641:37: error: initializer element is not constant     2641 |
        static const int set_size = strlen(CHARSET);         |
                     ^~~~~~
2024-07-12 16:24:00 -05:00
Steve Schrock
ffc6d6325d main: update usage with logger option 2024-07-12 11:58:51 -05:00
Steve Schrock
6d46698494 main: set the identity in the logger
This causes "iwd" to be output in each message sent to syslog to help
quickly identify iwd messages.
2024-07-11 17:13:43 -05:00
Marcel Holtmann
b54dcafa80 Release 2.19 2024-07-08 22:12:42 +02:00
Marcel Holtmann
ac0f13798b build: Add test-nl80211util binary to ignore list 2024-07-08 22:12:20 +02:00
Marcel Holtmann
51771694cd build: Require at least version 0.67 when building with external ELL 2024-07-08 22:11:24 +02:00
James Prestwood
9232991e25 auto-t: add test for a deauth coming in during an FT-roam 2024-06-26 09:38:06 -05:00
James Prestwood
71dc347582 station: add debug event prior to sending an FT-auth frame 2024-06-26 09:38:06 -05:00
James Prestwood
31b18a8c8b auto-t: add test for deauth after authentication
This code path is not exercised in the autotest but commonly does
happen in the real world. There is no associated bug with this, but
its helpful to have this event triggered in case something got
introduced in the future.
2024-06-26 09:38:06 -05:00
James Prestwood
d92e4c5663 station: add auth/assoc debug events
These will be useful to trigger behavior around authentication and
association.
2024-06-26 09:38:06 -05:00
James Prestwood
300f0ddac5 netdev: reuse NETDEV_EVENT_{AUTHENTICATING,ASSOCIATING}
The authenticating event was not used anymore and the associating
event use was questionable (after the CMD_CONNECT callback).

No other modules actually utilize these events but they are useful
for autotests. Move these events around to map 1:1 when the kernel
sends the auth/assoc events.
2024-06-26 09:38:06 -05:00
James Prestwood
77cf621f15 nl80211util: use nl80211_parse_attrs/nested for get_key_seq
This really reduces the number of checks needed and overall function
length by using the helper APIs.
2024-06-26 09:38:06 -05:00
James Prestwood
5c824cc24e netdev: use nl80211_parse_attrs for deauth event
Especially for parsing a single attribute, nl80211_parse_attrs
makes the most sense to use here.
2024-06-26 09:38:06 -05:00
James Prestwood
3fd5250c0d station: refactor the printing of scan results, print SNR/load
There are a few values which are nice to see in debug logs. Namely
the BSS load and SNR. Both of these values may not be available
either due to the AP or local hardware limiations. Rather than print
dummy values for these refactor the print so append the values only
if they are set in the scan result.
2024-06-20 10:51:00 -05:00
James Prestwood
8de70b1952 scan: add flag if BSS load was advertised
For ranking purposes the utilization was defaulted to a valid (127)
which would not change the rank if that IE was not found in the
scan results. Historically this was printed (debug) as part of the
scan results but that was removed as it was somewhat confusing. i.e.
did the AP _really_ have a utilization of 127? or was the IE not
found?

Since it is useful to see the BSS load if that is advertised add a
flag to the scan_bss struct to indicate if the IE was present which
can be checked.
2024-06-20 10:51:00 -05:00
James Prestwood
35808debae scan: use GET_SURVEY for SNR calculation in ranking
This issues a GET_SURVEY dump after scan results are available and
populates the survey information within the scan results. Currently
the only value obtained is the noise for a given frequency but the
survey results structure was created if in the future more values
need to be added.

From the noise, the SNR can be calculated. This is then used in the
ranking calculation to help lower BSS ranks that are on high noise
channels.
2024-06-20 10:51:00 -05:00
James Prestwood
685d105739 unit: add simple test for nl80211util 2024-06-20 10:51:00 -05:00
James Prestwood
43d5e89fac scan: fixed flush flag parsing
Parsing the flush flag for external scans was not done correctly
as it was not parsing the ATTR_SCAN_FLAGS but instead the flag
bitmap. Fix this by parsing the flags attribute, then checking if
the bit is set.
2024-06-20 10:51:00 -05:00
James Prestwood
88a71eca72 nl80211util: add ATTR_SCAN_FLAGS 2024-06-20 10:51:00 -05:00
James Prestwood
b4983fdcae nl80211util: add nl80211_parse_nested
Add a nested attribute parser. For the first supported attribute
add NL80211_ATTR_SURVEY_INFO.

This allows parsing of nested attributes in the same convenient
way as nl80211_parse_attrs but allows for support of any level of
nested attributes provided that a handler is added for each.
2024-06-20 10:51:00 -05:00
James Prestwood
16a05316b2 nl80211util: refactor nl80211_parse_attrs for nested variant
To prep for adding a _nested() variant of this function refactor
this to act on an l_genl_attr object rather than the message itself.
In addition a handler specific to the attribute being parsed is
now passed in, with the current "handler_for_type" being renamed to
"handler_for_nl80211" that corresponds to root level attributes.
2024-06-20 10:51:00 -05:00
James Prestwood
1dda441b85 nl80211util: add ATTR_SURVEY_INFO to parse_attrs 2024-06-20 10:51:00 -05:00
James Prestwood
5cf826c73a netdev: downgrade L_WARN_ON for ensure_eapol_registered
This warning is guaranteed to happen for SAE networks where there are
multiple netdev_authenticate_events. This should just be a check so
we don't register eapol twice, not a warning.
2024-06-20 10:51:00 -05:00
Marcel Holtmann
9a5c64481e Release 2.18 2024-06-04 15:36:50 +02:00
Marcel Holtmann
6de09a5eba build: Require at least version 0.66 when building with external ELL 2024-06-03 20:33:31 +02:00
Laura Peeters
03645acf4f eap-tls: Allow tls_msg_len to be zero
EAP-TTLS Start packets are empty by default, but can still be sent with
the L flag set. When attempting to reassemble a message we should not
fail if the length of the message is 0, and just treat it as any other
unfragmented message with the L flag set.
2024-05-31 16:23:15 -05:00
Denis Kenzior
c1e2a6c44c build: Add notifylist.[ch] from ell 2024-05-31 15:45:11 -05:00
Denis Kenzior
b3e7b7539e watchlist: Remove unused APIs
watchlist_new and watchlist_free were never used.  Get rid of them.
2024-05-29 17:52:23 -05:00
James Prestwood
fdbbd8b383 unit: add test for band fallback
This test uses the same country/country3 values seen by an AP vendor
which causes issues with IWD. The alpha2 is ES (Spain) and the 3rd
byte is 4, indicating to use the E-4. The issue then comes when the
neighbor report claims the BSS is under operating class 3 which is
not part of E-4.

With the fallback implemented, this test will pass since it will
try and lookup only on ES (the EU table) which operating class 3 is
part of.
2024-05-14 10:30:48 -05:00
James Prestwood
70d1d71612 band: support band lookup fallback for buggy APs
Its been seen that some vendors incorrectly set the 3rd byte of the
country code which causes the band lookup to fail with the provided
operating class. This isn't compliant with the spec, but its been
seen out in the wild and it causes IWD to behave poorly, specifically
with roaming since it cannot parse neighbor reports. This then
requires IWD to do a full scan on each roam.

Instead of a hard rejection, IWD can instead attempt to determine
the band by ignoring that 3rd byte and only use the alpha2 string.
This makes IWD slightly less strict but at the advantage of not being
crippled when exposed to poor AP configurations.
2024-05-14 10:27:36 -05:00
James Prestwood
4bfc794a37 auto-t: remove default group SAE test for specific OUI
There are already tests for UseDefaultEccGroup and this test is
not longer valid as the static OUI list has been removed.
2024-05-14 10:26:17 -05:00
James Prestwood
8a1f8d1a9a ie: remove is_ie_default_sae_group_oui
This is no longer used and instead a generic option was added
rather than referencing a static OUI list.
2024-05-14 10:26:11 -05:00
James Prestwood
b05c1cafcd scan: remove force_default_sae_group from scan_bss
This was added to support a single buggy AP model that failed to
negotiate the SAE group correctly. This may still be a problem but
since then the [Network].UseDefaultEccGroup option has been added
which accomplishes the same thing.

Remove the special handling for this specific OUI and rely on the
user setting the new option if they have problems.
2024-05-14 10:25:46 -05:00
James Prestwood
3be01a83ab build: Create ell directory for ell/ell.h target
Both ell/shared and ell/internal targets first create the ell/
directory within IWD. This apparently was just luck that one of
these always finished first in parallel builds. On my system at
least when building using dpkg-buildpackage IWD fails to build
due to the ell/ directory missing. From the logs it appears that
both the shared/internal targets were started but didn't complete
(or at least create the directory) before the ell/ell.h target:

make[1]: Entering directory '/home/jprestwood/tmp/iwd'
/usr/bin/mkdir -p ell
/usr/bin/mkdir -p ell
echo -n > ell/ell.h
/usr/bin/mkdir -p src
/bin/bash: line 1: ell/ell.h: No such file or directory
make[1]: *** [Makefile:4028: ell/ell.h] Error 1

Creating the ell/ directory within the ell/ell.h target solve
the issue. For reference this is the configure command dpkg
is using:

./configure --build=x86_64-linux-gnu \
	--prefix=/usr \
	--includedir=/usr/include \
	--mandir=/usr/share/man \
	--infodir=/usr/share/info \
	--sysconfdir=/etc \
	--localstatedir=/var \
	--disable-option-checking \
	--disable-silent-rules \
	--libdir=/usr/lib/x86_64-linux-gnu \
	--runstatedir=/run \
	--disable-maintainer-mode \
	--disable-dependency-tracking \
	--enable-tools \
	--enable-dbus-policy
2024-05-14 10:24:09 -05:00
James Prestwood
26efca80d7 nlmon: parse/print neighbor reports
Adds an IE parser for neighbor report elements
2024-05-09 10:23:45 -05:00
John Brandt
5fb3ac5937 eapol: include IGTK in 4-way handshake as AP
When SAE with MFP is being used, include the IGTK in message 3 of the
4-way handshake.
2024-05-07 11:19:51 -05:00
John Brandt
9274f70fec handshake: add functions to save and set IGTK
To add MFP support in the AP mode, add utility functions to save the
IGTK and to add the IGTK to handshake messages.
2024-05-07 11:15:25 -05:00
John Brandt
78bdb26296 eapol: encrypt key data for AKM-defined ciphers
Support encrypting key data when the cipher is AKM-defined. This is
needed to support SAE in AP mode.
2024-05-07 11:03:45 -05:00
John Brandt
3132e9f595 eapol: support PTK derivation with SHA256
Support PTK derivation in case the negotiated AKM requires SHA256. This
is needed to support SAE in AP mode.
2024-05-07 10:52:20 -05:00
John Brandt
b9e4dfbd40 sae: support reception of Confirm frame by AP
Experimental AP-mode support for receiving a Confirm frame when in the
COMMITTED state. The AP will reply with a Confirm frame.

Note that when acting as an AP, on reception of a Commit frame, the AP
only replies with a Commit frame. The protocols allows to also already
send the Confirm frame, but older clients may not support simultaneously
receiving a Commit and Confirm frame.
2024-05-07 10:50:39 -05:00
John Brandt
2e80a09184 sae: refactor and add function sae_calculate_keys
Refactor code by moving code to the new function sae_calculate_keys.
This will make it easier in the next commits to add SAE support for AP
mode.
2024-05-07 10:50:39 -05:00
John Brandt
49cddea10b unit: fix SAE unit tests
Don't mark either client as being the authenticator. In the current unit
tests, both instances act as clients to test functionality. This ensures
the unit does not show an error during the following commits where SAE
for AP mode is added.
2024-05-07 10:50:39 -05:00
Marcel Holtmann
be3f6a2ca0 udev: Add module for checking interface renaming actions 2024-04-16 14:57:57 +02:00
James Prestwood
88d3261bc2 unit: fix test-band with new rate estimation returns
-ENETUNREACH is the correct return to check as this indicates either
the RSSI was too low or the local capabilities were not compatible.
2024-04-15 16:51:54 -05:00
James Prestwood
e5816b024f band: return -ENETUNREACH for HE rate estimation
This was overlooked in a prior patch and causes warnings to be
printed when the RSSI is too low to estimate an HE data rate or
due to incompatible local capabilities (e.g. MCS support).

Similar to the other estimations, return -ENETUNREACH if the IE
was valid but incompatible.
2024-04-15 16:51:54 -05:00
James Prestwood
9cdc726dc1 wiphy: handle -ENETUNREACH for rate estimation
If the RSSI is too low or the local capabilities were not
compatible to estimate the rate don't warn but instead treat
this the same as -ENOTSUP and drop down to the next capability
set.
2024-04-15 16:51:54 -05:00
James Prestwood
e196cb1178 wiphy: include MAC of BSS with invalid HE capabilities
The prior print was not very descriptive, and now will log the
MAC of the offending BSS.
2024-04-15 16:51:54 -05:00
187 changed files with 7648 additions and 1716 deletions

3
.codespellrc Normal file
View File

@ -0,0 +1,3 @@
[codespell]
ignore-words-list = fils, FILS, SME, assertIn, OCE, clen, aci, ELL, sav, ths
skip = build-aux, linux, autom4te.cache, aclocal.m4, AUTHORS, libtool, configure*, *.5, ell

3
.gitignore vendored
View File

@ -66,6 +66,9 @@ unit/test-p2p
unit/test-band
unit/test-dpp
unit/test-json
unit/test-nl80211util
unit/test-pmksa
unit/test-storage
unit/cert-*.pem
unit/cert-*.csr
unit/cert-*.srl

View File

@ -1,3 +1,82 @@
ver 3.10:
Fix issue with handling neighbor report on BSS TM request.
Fix issue with handling deauth and FT association failure.
Fix issue with handling roaming and old frequencies.
ver 3.9:
Fix issue with Access Point mode and frequency unlocking.
Fix issue with network configuration and BSS retry logic.
Fix issue with handling busy notification from Access Point.
Fix issue with handling P-192, P-224 and P-521 for SAE.
ver 3.8:
Fix issue with handling unit tests and missing kernel features.
ver 3.7:
Fix issue with handling length of EncryptedSecurity.
Fix issue with handling empty affinities lists.
Fix issue with handling survey scanning results.
Fix issue with handling duplicate values in DPP URI.
ver 3.6:
Fix issue with handling blacklisting and roaming requests.
Fix issue with handling CQM thresholds for FullMAC devices.
Add support for PMKSA when using FullMAC devices.
ver 3.5:
Add support for option to disable blacklist handling.
Add support for option to disable SAE for broken drivers.
ver 3.4:
Add support for the Test Anything Protocol.
ver 3.3:
Fix issue with handling External Authentication.
ver 3.2:
Fix issue with GCC 15 and -std=c23 build errors.
Add support for using PMKSA over SAE if available.
Add support for HighUtilization/StationCount thresholds.
Add support for disabling Multicast RX option.
ver 3.1:
Fix issue with handling OWE transition BSS selection.
Fix issue with handling oper class 136 starting frequency.
ver 3.0:
Fix issue with handling alpha2 code for United Kingdom.
Fix issue with handling empty TX/RX bitrate attributes.
Fix issue with handling RSSI polling fallback workaround.
Fix issue with handling harmless cloned information elements.
Add experimental support for External Authentication feature.
ver 2.22:
Fix issue with handling the Affinities property.
Fix issue with handling ConnectedAccessPoint signal when roaming.
ver 2.21:
Fix issue with pending scan requests after regdom update.
Fix issue with handling the rearming of the roaming timeout.
Fix issue with survey request and externally triggered scans.
Fix issue with RSSI fallback when setting CQM threshold fails.
Fix issue with FT-over-Air without offchannel support.
Add support for per station Affinities property.
ver 2.20:
Fix issue with PKEX timeout and number of frequencies used.
Fix issue with handling logic for handshake failures.
Fix issue with handling ConnectedAccessPoint signal.
ver 2.19:
Fix issue with handling flush flag for external scans.
Fix issue with handling SNR calculation in ranking.
ver 2.18:
Fix issue with handling BSS with invalid HE capabilities.
Fix issue with neighbor reports with invalid country codes.
Fix issue with EAP-TTLS Start packets with L flag set.
Add support for enabling SAE for AP mode operation.
ver 2.17:
Fix issue with handling deauthenticate on disconnect.
Fix issue with handling of rate estimation errors.

View File

@ -64,12 +64,16 @@ ell_headers = ell/util.h \
ell/acd.h \
ell/cleanup.h \
ell/netconfig.h \
ell/sysctl.h
ell/sysctl.h \
ell/notifylist.h \
ell/minheap.h
ell_sources = ell/private.h \
ell/missing.h \
ell/util.c \
ell/test-private.h \
ell/test.c \
ell/test-dbus.c \
ell/strv.c \
ell/utf8.c \
ell/queue.c \
@ -145,7 +149,9 @@ ell_sources = ell/private.h \
ell/dhcp6-transport.c \
ell/acd.c \
ell/netconfig.c \
ell/sysctl.c
ell/sysctl.c \
ell/notifylist.c \
ell/minheap.c
ell_shared = ell/useful.h ell/asn1-private.h
@ -212,7 +218,8 @@ eap_sources = src/eap.c src/eap.h src/eap-private.h \
if DAEMON
libexec_PROGRAMS += src/iwd
src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \
src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h \
src/missing.h src/defs.h \
src/netdev.h src/netdev.c \
src/wiphy.h src/wiphy.c \
src/device.c \
@ -265,6 +272,10 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \
src/dpp-util.h src/dpp-util.c \
src/json.h src/json.c \
src/dpp.c \
src/udev.c \
src/pmksa.h src/pmksa.c \
src/vendor_quirks.h \
src/vendor_quirks.c \
$(eap_sources) \
$(builtin_sources)
@ -316,6 +327,7 @@ client_iwctl_SOURCES = client/main.c \
client/daemon.c client/daemon.h \
client/dpp.c client/dpp-pkex.c \
client/station-debug.c \
client/bss.c \
src/util.c src/util.h \
src/band.c src/band.h
@ -430,7 +442,8 @@ unit_tests += unit/test-cmac-aes \
unit/test-ie unit/test-util unit/test-ssid-security \
unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \
unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \
unit/test-dpp unit/test-json
unit/test-dpp unit/test-json unit/test-nl80211util \
unit/test-pmksa unit/test-storage
endif
if CLIENT
@ -449,6 +462,7 @@ unit_test_eap_sim_SOURCES = unit/test-eap-sim.c \
src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \
src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \
src/util.h src/util.c \
src/simauth.h src/simauth.c \
@ -508,6 +522,7 @@ unit_test_eapol_SOURCES = unit/test-eapol.c \
src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \
src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \
src/eap-tls.c src/eap-ttls.c \
src/eap-md5.c src/util.c \
@ -538,6 +553,7 @@ unit_test_wsc_SOURCES = unit/test-wsc.c src/wscutil.h src/wscutil.c \
src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \
src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \
src/util.h src/util.c \
src/erp.h src/erp.c \
@ -556,6 +572,7 @@ unit_test_sae_SOURCES = unit/test-sae.c \
src/crypto.h src/crypto.c \
src/ie.h src/ie.c \
src/handshake.h src/handshake.c \
src/pmksa.h src/pmksa.c \
src/erp.h src/erp.c \
src/band.h src/band.c \
src/util.h src/util.c \
@ -579,6 +596,22 @@ unit_test_dpp_LDADD = $(ell_ldadd)
unit_test_json_SOURCES = unit/test-json.c src/json.h src/json.c shared/jsmn.h
unit_test_json_LDADD = $(ell_ldadd)
unit_test_nl80211util_SOURCES = unit/test-nl80211util.c \
src/nl80211util.h src/nl80211util.c \
src/band.h src/band.c \
src/ie.h src/ie.c \
src/util.h src/util.c
unit_test_nl80211util_LDADD = $(ell_ldadd)
unit_test_pmksa_SOURCES = unit/test-pmksa.c src/pmksa.c src/pmksa.h \
src/module.h src/util.h
unit_test_pmksa_LDADD = $(ell_ldadd)
unit_test_storage_SOURCES = unit/test-storage.c src/storage.c src/storage.h \
src/crypto.c src/crypto.h \
src/common.c src/common.h
unit_test_storage_LDADD = $(ell_ldadd)
endif
if CLIENT
@ -594,6 +627,9 @@ unit_test_client_SOURCES = unit/test-client.c \
unit_test_client_LDADD = $(ell_ldadd) $(client_ldadd)
endif
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \
$(top_srcdir)/build-aux/tap-driver.sh
TESTS = $(unit_tests)
EXTRA_DIST = src/genbuiltin src/iwd.service.in src/net.connman.iwd.service \
@ -710,6 +746,7 @@ ell/internal: Makefile
done > $@
ell/ell.h: Makefile
$(AM_V_at)$(MKDIR_P) ell
$(AM_V_at)echo -n > $@
$(AM_V_GEN)for f in $(ell_headers) ; do \
echo "#include <$$f>" >> $@ ; \

4
TODO
View File

@ -110,7 +110,7 @@ Wireless monitor
- Subscribe to all nl80211 multicast groups at startup
It seems the nlmon packets are limited to actual subscribed mutlicast
It seems the nlmon packets are limited to actual subscribed multicast
groups. To get a complete picture of all the nl80211 commands and
events, it is required that iwmon adds membership to all multicast
groups that the nl80211 lists.
@ -234,7 +234,7 @@ Wireless daemon
- Implement Enrollee Session Overlap Detection after WSC Protocol Run
WSC Best Practices v2.0.1, Section 3.15 describes an enhacement to detect
WSC Best Practices v2.0.1, Section 3.15 describes an enhancement to detect
PBC session overlaps. The Enrollee is asked to perform an extra scan without
the PBC request in the ProbeRequest frames after EAP-WSC completes
successfully. If another AP in PBC mode is found, then a SessionOverlap

View File

@ -0,0 +1,156 @@
#!/usr/bin/python3
import unittest
import sys
sys.path.append('../util')
import iwd
from iwd import IWD
from iwd import NetworkType
from hostapd import HostapdCLI
class Test(unittest.TestCase):
def initial_connection(self):
ordered_network = self.device.get_ordered_network('TestAPRoam')
self.assertEqual(ordered_network.type, NetworkType.psk)
condition = 'not obj.connected'
self.wd.wait_for_object_condition(ordered_network.network_object, condition)
self.device.connect_bssid(self.bss_hostapd[0].bssid)
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device, condition)
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED')
self.assertFalse(self.bss_hostapd[1].list_sta())
def test_full_scan(self):
"""
Tests that IWD first tries a limited scan, then a full scan after
an AP directed roam. After the full scan yields no results IWD
should stop trying to roam.
"""
self.initial_connection()
# Disable other APs, so the scans come up empty
self.bss_hostapd[1].disable()
self.bss_hostapd[2].disable()
# Send a bad candidate list with the BSS TM request which contains a
# channel with no AP operating on it.
self.bss_hostapd[0].send_bss_transition(
self.device.address,
[(self.bss_hostapd[1].bssid, "8f0000005105060603000000")]
)
self.device.wait_for_event("roam-scan-triggered")
self.device.wait_for_event("no-roam-candidates")
# IWD should then trigger a full scan
self.device.wait_for_event("full-roam-scan")
self.device.wait_for_event("no-roam-candidates", timeout=30)
# IWD should not trigger a roam again after the above 2 failures.
with self.assertRaises(TimeoutError):
self.device.wait_for_event("roam-scan-triggered", timeout=60)
def test_bad_candidate_list(self):
"""
Tests behavior when the AP sends a candidate list but the scan
finds no BSS's. IWD should fall back to a full scan after.
"""
self.initial_connection()
# Send a bad candidate list with the BSS TM request which contains a
# channel with no AP operating on it.
self.bss_hostapd[0].send_bss_transition(
self.device.address,
[(self.bss_hostapd[1].bssid, "8f0000005105060603000000")]
)
self.device.wait_for_event("roam-scan-triggered")
self.device.wait_for_event("no-roam-candidates")
# IWD should then trigger a full scan
self.device.wait_for_event("full-roam-scan")
self.device.wait_for_event("roaming", timeout=30)
self.device.wait_for_event("connected")
def test_bad_neighbor_report(self):
"""
Tests behavior when the AP sends no candidate list. IWD should
request a neighbor report. If the limited scan yields no BSS's IWD
should fall back to a full scan.
"""
# Set a bad neighbor (channel that no AP is on) to force the limited
# roam scan to fail
self.bss_hostapd[0].set_neighbor(
self.bss_hostapd[1].bssid,
"TestAPRoam",
'%s8f000000%s%s060603000000' % (self.bss_hostapd[1].bssid.replace(':', ''), "51", "0b")
)
self.initial_connection()
self.bss_hostapd[0].send_bss_transition(self.device.address, [])
self.device.wait_for_event("roam-scan-triggered")
# The AP will have sent a neighbor report with a single BSS but on
# channel 11 which no AP is on. This should result in a limited scan
# picking up no candidates.
self.device.wait_for_event("no-roam-candidates", timeout=30)
# IWD should then trigger a full scan
self.device.wait_for_event("full-roam-scan")
self.device.wait_for_event("roaming", timeout=30)
self.device.wait_for_event("connected")
def test_ignore_candidate_list_quirk(self):
"""
Tests that IWD ignores the candidate list sent by the AP since its
OUI indicates it should be ignored.
"""
# Set the OUI so the candidate list should be ignored
for hapd in self.bss_hostapd:
hapd.set_value('vendor_elements', 'dd0400180a01')
self.initial_connection()
# Send with a candidate list (should be ignored)
self.bss_hostapd[0].send_bss_transition(
self.device.address,
[(self.bss_hostapd[1].bssid, "8f0000005105060603000000")]
)
# IWD should ignore the list and trigger a full scan since we have not
# set any neighbors
self.device.wait_for_event("full-roam-scan")
self.device.wait_for_event("roaming", timeout=30)
self.device.wait_for_event("connected")
def setUp(self):
self.wd = IWD(True)
devices = self.wd.list_devices(1)
self.device = devices[0]
def tearDown(self):
self.wd = None
self.device = None
for hapd in self.bss_hostapd:
hapd.reload()
@classmethod
def setUpClass(cls):
IWD.copy_to_storage('TestAPRoam.psk')
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
HostapdCLI(config='ssid2.conf'),
HostapdCLI(config='ssid3.conf') ]
@classmethod
def tearDownClass(cls):
IWD.clear_storage()
if __name__ == '__main__':
unittest.main(exit=True)

View File

@ -11,52 +11,58 @@ from iwd import NetworkType
from hostapd import HostapdCLI
class Test(unittest.TestCase):
def validate(self, expect_roam=True):
wd = IWD()
devices = wd.list_devices(1)
device = devices[0]
ordered_network = device.get_ordered_network('TestAPRoam')
def initial_connection(self):
ordered_network = self.device.get_ordered_network('TestAPRoam')
self.assertEqual(ordered_network.type, NetworkType.psk)
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'
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.assertFalse(self.bss_hostapd[1].list_sta())
self.bss_hostapd[0].send_bss_transition(device.address,
[(self.bss_hostapd[1].bssid, '8f0000005102060603000000')],
def validate_roam(self, from_bss, to_bss, expect_roam=True):
from_bss.send_bss_transition(self.device.address,
self.neighbor_list,
disassoc_imminent=expect_roam)
if expect_roam:
from_condition = 'obj.state == DeviceState.roaming'
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:
device.wait_for_event("no-roam-candidates")
device.disconnect()
condition = 'not obj.connected'
wd.wait_for_object_condition(ordered_network.network_object, condition)
self.device.wait_for_event("no-roam-candidates")
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):
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
def setUpClass(cls):
@ -65,6 +71,10 @@ class Test(unittest.TestCase):
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
HostapdCLI(config='ssid2.conf'),
HostapdCLI(config='ssid3.conf') ]
cls.neighbor_list = [
(cls.bss_hostapd[0].bssid, "8f0000005101060603000000"),
(cls.bss_hostapd[1].bssid, "8f0000005102060603000000"),
]
@classmethod
def tearDownClass(cls):

View File

@ -1,5 +1,7 @@
[SETUP]
num_radios=4
hwsim_medium=true
start_iwd=false
[HOSTAPD]
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=EasilyGuessedPassword

View File

@ -0,0 +1,41 @@
hw_mode=g
channel=1
ssid=TestFT
utf8_ssid=1
ctrl_interface=/var/run/hostapd
r1_key_holder=120000000001
nas_identifier=dummy1
wpa=2
# Can support WPA-PSK and FT-PSK (space separated list) and/or EAP at the same
# time but we want to force FT
wpa_key_mgmt=FT-PSK
wpa_pairwise=CCMP
wpa_passphrase=EasilyGuessedPassword
ieee80211w=0
rsn_preauth=1
rsn_preauth_interfaces=lo
disable_pmksa_caching=0
# Allow PMK cache to be shared opportunistically among configured interfaces
# and BSSes (i.e., all configurations within a single hostapd process).
okc=1
mobility_domain=1234
reassociation_deadline=60000
r0kh=12:00:00:00:00:01 dummy1 000102030405060708090a0b0c0d0e0f
r0kh=12:00:00:00:00:02 dummy2 000102030405060708090a0b0c0d0e0f
r1kh=12:00:00:00:00:01 00:00:00:00:00:01 000102030405060708090a0b0c0d0e0f
r1kh=12:00:00:00:00:02 00:00:00:00:00:02 000102030405060708090a0b0c0d0e0f
# Push mode only needed for 8021x, not PSK mode since msk already known
pmk_r1_push=0
# Allow locally generated FT response so we don't have to configure push/pull
# between BSSes running as separate hostapd processes as in the test-runner
# case. Only works with FT-PSK, otherwise brctl needs to be installed and
# CONFIG_BRIDGE enabled in the kernel.
ft_psk_generate_local=1
rkh_pull_timeout=50
ft_over_ds=0
ap_table_expiration_time=36000
ap_table_max_size=10
rrm_neighbor_report=1
ocv=1

View File

@ -0,0 +1,41 @@
hw_mode=g
channel=2
ssid=TestFT
utf8_ssid=1
ctrl_interface=/var/run/hostapd
r1_key_holder=120000000002
nas_identifier=dummy2
wpa=2
# Can support WPA-PSK and FT-PSK (space separated list) and/or EAP at the same
# time but we want to force FT
wpa_key_mgmt=FT-PSK
wpa_pairwise=CCMP
wpa_passphrase=EasilyGuessedPassword
ieee80211w=0
rsn_preauth=1
rsn_preauth_interfaces=lo
disable_pmksa_caching=0
# Allow PMK cache to be shared opportunistically among configured interfaces
# and BSSes (i.e., all configurations within a single hostapd process).
okc=1
mobility_domain=1234
reassociation_deadline=60000
r0kh=12:00:00:00:00:01 dummy1 000102030405060708090a0b0c0d0e0f
r0kh=12:00:00:00:00:02 dummy2 000102030405060708090a0b0c0d0e0f
r1kh=12:00:00:00:00:01 00:00:00:00:00:01 000102030405060708090a0b0c0d0e0f
r1kh=12:00:00:00:00:02 00:00:00:00:00:02 000102030405060708090a0b0c0d0e0f
# Push mode only needed for 8021x, not PSK mode since msk already known
pmk_r1_push=0
# Allow locally generated FT response so we don't have to configure push/pull
# between BSSes running as separate hostapd processes as in the test-runner
# case. Only works with FT-PSK, otherwise brctl needs to be installed and
# CONFIG_BRIDGE enabled in the kernel.
ft_psk_generate_local=1
rkh_pull_timeout=50
ft_over_ds=0
ap_table_expiration_time=36000
ap_table_max_size=10
rrm_neighbor_report=1
ocv=1

View File

@ -0,0 +1,8 @@
[SETUP]
num_radios=3
start_iwd=0
hwsim_medium=yes
[HOSTAPD]
rad0=ft-psk-ccmp-1.conf
rad1=ft-psk-ccmp-2.conf

View File

@ -0,0 +1,5 @@
[Scan]
DisableMacAddressRandomization=true
[General]
RoamRetryInterval=1

View File

@ -0,0 +1,216 @@
#! /usr/bin/python3
import unittest
import sys, os
import dbus
sys.path.append('../util')
from config import ctx
import iwd
from iwd import IWD, IWDDBusAbstract
from iwd import NetworkType
from hwsim import Hwsim
from hostapd import HostapdCLI
#
# Separate client used to test DBus disconnects so we don't bring down the
# entire IWD python library
#
class AffinityClient(IWDDBusAbstract):
def __init__(self, device_path):
self._bus = dbus.bus.BusConnection(address_or_type=ctx.dbus_address)
self._station_prop_if = dbus.Interface(
self._bus.get_object(iwd.IWD_SERVICE, device_path),
iwd.DBUS_PROPERTIES)
def set(self, values):
self._station_prop_if.Set(iwd.IWD_STATION_INTERFACE, 'Affinities', dbus.Array([dbus.ObjectPath(v) for v in values], signature="o"))
def close(self):
self._bus.close()
class Test(unittest.TestCase):
def connect(self, device, hapd):
ordered_network = device.get_ordered_network('TestFT', full_scan=True)
self.assertEqual(ordered_network.type, NetworkType.psk)
condition = 'not obj.connected'
self.wd.wait_for_object_condition(ordered_network.network_object, condition)
device.connect_bssid(hapd.bssid)
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(device, condition)
def test_set_affinity(self):
device = self.wd.list_devices(1)[0]
self.connect(device, self.bss_hostapd[0])
print(device.connected_bss)
device.affinities = [device.connected_bss]
# IWD should not attempt to roam
with self.assertRaises(TimeoutError):
device.wait_for_event("roam-scan-triggered")
device.affinities = []
device.wait_for_event("roam-scan-triggered")
def test_roam_below_critical(self):
device = self.wd.list_devices(1)[0]
self.connect(device, self.bss_hostapd[0])
device.affinities = [device.connected_bss]
# IWD should not attempt to roam
with self.assertRaises(TimeoutError):
device.wait_for_event("roam-scan-triggered")
# Lower signal past critical level
self.bss0_rule.signal = -9000
device.wait_for_event("roam-scan-triggered")
def test_error_conditions(self):
device = self.wd.list_devices(1)[0]
# Calling set while disconnected should fail
with self.assertRaises(iwd.NotConnectedEx):
device.affinities = ["/some/path"]
self.connect(device, self.bss_hostapd[0])
device.affinities = [device.connected_bss]
# An invalid path should fail
with self.assertRaises(iwd.InvalidArgumentsEx):
device.affinities = [device.connected_bss, "/an/invalid/path"]
def test_affinity_client_disconnect(self):
device = self.wd.list_devices(1)[0]
client = AffinityClient(device.device_path)
self.connect(device, self.bss_hostapd[0])
client.set([device.connected_bss])
with self.assertRaises(TimeoutError):
device.wait_for_event("roam-scan-triggered")
client._bus.close()
device.wait_for_event("roam-scan-triggered")
def test_affinity_client_reconnect_during_roam(self):
device = self.wd.list_devices(1)[0]
client = AffinityClient(device.device_path)
self.connect(device, self.bss_hostapd[0])
client.set([device.connected_bss])
# Lower signal past critical level
self.bss0_rule.signal = -9000
device.wait_for_event("roam-scan-triggered")
client.close()
del client
client = AffinityClient(device.device_path)
# setting here should get cleared after connecting
client.set([device.connected_bss])
device.wait_for_event("ft-authenticating")
device.wait_for_event("associating")
device.wait_for_event("connected")
# Affinity should be reset, and IWD should be trying to roam
device.wait_for_event("roam-scan-triggered")
def test_cleanup_with_connected_client(self):
device = self.wd.list_devices(1)[0]
client = AffinityClient(device.device_path)
self.connect(device, self.bss_hostapd[0])
client.set([device.connected_bss])
self.wd.stop()
def test_affinity_removed_after_roam(self):
device = self.wd.list_devices(1)[0]
self.connect(device, self.bss_hostapd[0])
device.affinities = [device.connected_bss]
# Lower signal past critical level
self.bss0_rule.signal = -9000
device.wait_for_event("roam-scan-triggered")
device.wait_for_event("ft-authenticating")
device.wait_for_event("associating")
device.wait_for_event("connected")
self.assertEqual(device.affinities, [])
def tearDown(self):
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" down')
os.system('ip link set "' + self.bss_hostapd[1].ifname + '" down')
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" up')
os.system('ip link set "' + self.bss_hostapd[1].ifname + '" up')
self.wd.stop()
self.wd = None
def setUp(self):
self.bss0_rule.signal = -8000
self.bss1_rule.signal = -8000
self.wd = IWD(True)
@classmethod
def setUpClass(cls):
hwsim = Hwsim()
IWD.copy_to_storage('TestFT.psk')
cls.bss_hostapd = [ HostapdCLI(config='ft-psk-ccmp-1.conf'),
HostapdCLI(config='ft-psk-ccmp-2.conf') ]
rad0 = hwsim.get_radio('rad0')
rad1 = hwsim.get_radio('rad1')
cls.bss0_rule = hwsim.rules.create()
cls.bss0_rule.source = rad0.addresses[0]
cls.bss0_rule.bidirectional = True
cls.bss0_rule.signal = -8000
cls.bss0_rule.enabled = True
cls.bss1_rule = hwsim.rules.create()
cls.bss1_rule.source = rad1.addresses[0]
cls.bss1_rule.bidirectional = True
cls.bss1_rule.signal = -8000
cls.bss1_rule.enabled = True
cls.bss_hostapd[0].set_address('12:00:00:00:00:01')
cls.bss_hostapd[1].set_address('12:00:00:00:00:02')
HostapdCLI.group_neighbors(*cls.bss_hostapd)
@classmethod
def tearDownClass(cls):
IWD.clear_storage()
cls.bss_hostapd = None
cls.bss0_rule.remove()
cls.bss1_rule.remove()
if __name__ == '__main__':
unittest.main(exit=True)

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)
def test_blacklist_disabled(self):
wd = self.wd
bss_hostapd = self.bss_hostapd
rule0 = self.rule0
rule1 = self.rule1
rule2 = self.rule2
psk_agent = PSKAgent(["secret123", 'secret123'])
wd.register_psk_agent(psk_agent)
devices = wd.list_devices(1)
device = devices[0]
rule0.drop = True
rule0.enabled = True
device.autoconnect = True
condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_condition(device, condition)
ordered_network = device.get_ordered_network("TestBlacklist", full_scan=True)
self.assertEqual(ordered_network.type, NetworkType.psk)
# The first BSS should fail, and we should connect to the second. This
# should not result in a connection blacklist though since its disabled.
bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % device.address)
device.disconnect()
rule0.drop = False
device.autoconnect = True
# Verify the first BSS wasn't blacklisted.
bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % device.address)
def setUp(self):
_, _, name = self.id().split(".")
# TODO: If we have this pattern elsewhere it might be nice to turn this
# into a decorator e.g.
#
# @config("main.conf.disabled")
# @profile("TestBlacklist.psk")
# def test_blacklist_disabled(self)
# ...
#
if name == "test_blacklist_disabled":
IWD.copy_to_storage("main.conf.disabled", IWD_CONFIG_DIR, "main.conf")
IWD.copy_to_storage("TestBlacklist.psk")
else:
IWD.copy_to_storage("main.conf.default", IWD_CONFIG_DIR, "main.conf")
self.wd = IWD(True)
def tearDown(self):
IWD.clear_storage()
self.wd = None
self.rule0.drop = False
self.rule1.drop = False
self.rule2.drop = False
@classmethod
def setUpClass(cls):

View File

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

View File

@ -0,0 +1,98 @@
#! /usr/bin/python3
import unittest
import sys, os
sys.path.append('../util')
import iwd
from iwd import IWD
from iwd import PSKAgent
from iwd import NetworkType
from hwsim import Hwsim
from hostapd import HostapdCLI
import testutil
class Test(unittest.TestCase):
def test_bss_unregister(self):
device = self.wd.list_devices(1)[0]
ordered_network = device.get_ordered_network('ssidTKIP', full_scan=True)
network = ordered_network.network_object
self.assertEqual(len(network.extended_service_set), 2)
ends = [parts.split('/')[-1] for parts in network.extended_service_set]
self.assertIn(self.bss_hostapd[0].bssid.replace(':', ''), ends)
self.assertIn(self.bss_hostapd[1].bssid.replace(':', ''), ends)
self.rule_bss1.enabled = True
# Even with flushing, the kernel still seems to return the scan
# results
self.wd.wait(40)
ordered_network = device.get_ordered_network('ssidTKIP', full_scan=True)
network = ordered_network.network_object
ends = [parts.split('/')[-1] for parts in network.extended_service_set]
self.assertIn(self.bss_hostapd[0].bssid.replace(':', ''), ends)
self.assertNotIn(self.bss_hostapd[1].bssid.replace(':', ''), ends)
self.rule_bss0.enabled = True
self.wd.wait(40)
ordered_networks = device.get_ordered_networks('ssidTKIP', full_scan=True)
self.assertIsNone(ordered_networks)
self.rule_bss0.enabled = False
ordered_networks = device.get_ordered_networks('ssidTKIP', full_scan=True)
ends = [parts.split('/')[-1] for parts in network.extended_service_set]
self.assertIn(self.bss_hostapd[0].bssid.replace(':', ''), ends)
self.assertNotIn(self.bss_hostapd[1].bssid.replace(':', ''), ends)
def tearDown(self):
self.rule_bss0.enabled = False
self.rule_bss1.enabled = False
self.wd.stop()
self.wd.wait(10)
self.wd = None
def setUp(self):
self.wd = IWD(True)
@classmethod
def setUpClass(cls):
hwsim = Hwsim()
IWD.copy_to_storage('ssidTKIP.psk')
cls.bss_hostapd = [ HostapdCLI(config='ssidTKIP-1.conf'),
HostapdCLI(config='ssidTKIP-2.conf') ]
rad0 = hwsim.get_radio('rad0')
rad1 = hwsim.get_radio('rad1')
cls.rule_bss0 = hwsim.rules.create()
cls.rule_bss0.source = rad0.addresses[0]
cls.rule_bss0.bidirectional = True
cls.rule_bss0.drop = True
cls.rule_bss1 = hwsim.rules.create()
cls.rule_bss1.source = rad1.addresses[0]
cls.rule_bss1.bidirectional = True
cls.rule_bss1.drop = True
@classmethod
def tearDownClass(cls):
IWD.clear_storage()
cls.bss_hostapd = None
cls.rule_bss0.remove()
cls.rule_bss1.remove()
if __name__ == '__main__':
unittest.main(exit=True)

View File

@ -0,0 +1,8 @@
[SETUP]
num_radios=3
hwsim_medium=yes
start_iwd=no
[HOSTAPD]
rad0=ssidTKIP-1.conf
rad1=ssidTKIP-2.conf

View File

@ -0,0 +1,7 @@
hw_mode=g
channel=1
ssid=ssidTKIP
wpa=1
wpa_pairwise=TKIP
wpa_passphrase=secret123

View File

@ -0,0 +1,7 @@
hw_mode=g
channel=2
ssid=ssidTKIP
wpa=1
wpa_pairwise=TKIP
wpa_passphrase=secret123

View File

@ -0,0 +1,5 @@
[Security]
Passphrase=secret123
[Settings]
AutoConnect=False

View File

@ -11,7 +11,7 @@ from iwd import IWD
class Test8021xNetwork(unittest.TestCase):
'''
The bellow test cases excesise the following connection scenarios:
The below test cases excesise the following connection scenarios:
Network config is
present at start time: Connect: AutoConnect: Result:

View File

@ -11,7 +11,7 @@ from iwd import IWD
class TestOpenNetwork(unittest.TestCase):
'''
The bellow test cases excesise the following connection scenarios:
The below test cases excesise the following connection scenarios:
Network config is
present at start time: Connect: AutoConnect: Result:

View File

@ -11,7 +11,7 @@ from iwd import IWD
class TestWpaNetwork(unittest.TestCase):
'''
The bellow test cases exercise the following connection scenarios:
The below test cases exercise the following connection scenarios:
Network config is
present at start time: Connect: AutoConnect: Result:

View File

@ -38,20 +38,18 @@ class Test(unittest.TestCase):
def test_iwd_as_enrollee_scan_after(self):
self.wpas.disconnect()
self.device.autoconnect = True
uri = self.device.dpp_start_enrollee()
self.wpas.dpp_configurator_create(uri)
self.wpas.dpp_configurator_start('ssidCCMP', 'secret123')
self.hapd.reload()
with self.assertRaises(Exception):
self.device.get_ordered_network('ssidCCMP', scan_if_needed=False)
self.hapd.reload()
self.hapd.wait_for_event('AP-ENABLED')
self.device.autoconnect = True
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device, condition)

View File

@ -4,7 +4,7 @@ import unittest
import sys
sys.path.append('../util')
from iwd import IWD, SharedCodeAgent
from iwd import IWD, SharedCodeAgent, DeviceState
from iwd import DeviceProvisioning
from wpas import Wpas
from hostapd import HostapdCLI
@ -160,10 +160,8 @@ class Test(unittest.TestCase):
def test_pkex_iwd_to_iwd(self):
self.start_iwd_pkex_configurator(self.device[0])
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
self.device[1].autoconnect = True
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device[1], condition)
@ -176,10 +174,8 @@ class Test(unittest.TestCase):
def test_pkex_configurator_with_agent(self):
self.start_iwd_pkex_configurator(self.device[0], agent=True)
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
self.device[1].autoconnect = True
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device[1], condition)
@ -198,8 +194,8 @@ class Test(unittest.TestCase):
self.start_iwd_pkex_configurator(self.device[0])
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
self.device[1].autoconnect = False
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device[1], condition)
@ -210,6 +206,24 @@ class Test(unittest.TestCase):
self.assertIn("SendHostname=true", settings)
def test_existing_incorrect_profile(self):
self.hapd.reload()
self.hapd.wait_for_event('AP-ENABLED')
IWD.copy_to_storage("existingProfile.psk", "/tmp/ns0/", "ssidCCMP.psk")
# Start connecting
self.device[1].autoconnect = True
self.wd.wait_for_object_condition(self.device[1], 'obj.state == DeviceState.connecting')
# We should be able to start DPP despite the connecting state
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
self.start_iwd_pkex_configurator(self.device[0])
self.assertEqual(self.device[1].state, DeviceState.disconnected)
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device[1], condition)
def test_existing_hidden_network(self):
self.hapd_hidden.reload()
self.hapd_hidden.wait_for_event('AP-ENABLED')
@ -222,8 +236,9 @@ class Test(unittest.TestCase):
self.start_iwd_pkex_configurator(self.device[0], profile='ssidHidden.psk')
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
self.device[1].autoconnect = False
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device[1], condition)
@ -239,8 +254,8 @@ class Test(unittest.TestCase):
self.hapd_hidden.wait_for_event('AP-ENABLED')
self.start_iwd_pkex_configurator(self.device[0], profile='ssidHidden.psk')
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
self.device[1].autoconnect = False
self.device[1].dpp_pkex_enroll('secret123', identifier="test")
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device[1], condition)

View File

@ -0,0 +1,107 @@
#!/usr/bin/python3
import unittest
import sys
sys.path.append('../util')
from iwd import IWD, SharedCodeAgent, DeviceState
from iwd import DeviceProvisioning
from wpas import Wpas
from hostapd import HostapdCLI
from hwsim import Hwsim
from config import ctx
from time import time
import os
class Test(unittest.TestCase):
def auto_connect(self):
IWD.copy_to_storage('ssidCCMP.psk')
self.device.autoconnect = True
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device, condition)
def test_configurator_stops_on_disconnect(self):
self.auto_connect()
self.device.dpp_start_configurator()
self.device.disconnect()
condition = 'obj.state == DeviceState.disconnected'
self.wd.wait_for_object_condition(self.device, condition)
self.assertEqual(self.device._device_provisioning.started, False)
def test_enrollee_stops_on_connect(self):
# Scan to get a list of networks
self.device.scan()
self.wd.wait_for_object_condition(self.device, 'obj.scanning == True')
self.wd.wait_for_object_condition(self.device, 'obj.scanning == False')
self.device.dpp_start_enrollee()
network = self.device.get_ordered_network("ssidCCMP")
network.network_object.connect()
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device, condition)
self.assertEqual(self.device._device_provisioning.started, False)
def test_enrollee_disconnects_automatically(self):
self.auto_connect()
self.device.dpp_start_enrollee()
condition = 'obj.state == DeviceState.disconnected'
self.wd.wait_for_object_condition(self.device, condition)
def test_enrollee_autoconnect_stays_on(self):
# Put in an autoconnecting state, no saved profile though
self.device.autoconnect = True
self.device.dpp_start_enrollee()
# DPP should set autoconnect false, but then re-enable after it stops
self.wd.wait_for_object_condition(self.device, "obj.autoconnect == False")
self.wd.wait_for_object_condition(self.device._device_provisioning, "obj.started == True")
# Stop DPP
self.device.dpp_stop()
self.wd.wait_for_object_condition(self.device, "obj.autoconnect == True")
def test_enrollee_autoconnect_stays_off(self):
# Autoconnect should be off by default
self.device.dpp_start_enrollee()
# DPP should set autoconnect false, but stay off after it stops
self.wd.wait_for_object_condition(self.device, "obj.autoconnect == False")
self.wd.wait_for_object_condition(self.device._device_provisioning, "obj.started == True")
# Stop DPP
self.device.dpp_stop()
self.wd.wait_for_object_condition(self.device, "obj.autoconnect == False")
def setUp(self):
self.wd = IWD(True)
self.device = self.wd.list_devices(1)[0]
def tearDown(self):
self.wd.stop()
self.wd = None
@classmethod
def setUpClass(cls):
hapd = HostapdCLI(config="hostapd.conf")
hapd.reload()
hapd.wait_for_event("AP-ENABLED")
@classmethod
def tearDownClass(cls):
pass
if __name__ == '__main__':
unittest.main(exit=True)

View File

@ -104,7 +104,7 @@ class Test(unittest.TestCase):
self.assertTrue(self.profile_is_encrypted('ssidCCMP.psk'))
# Tests that a profile that doesn't decrypt wont become a known network
# Tests that a profile that doesn't decrypt won't become a known network
def test_decryption_failure(self):
bad_config = \
'''

View File

@ -1,2 +1,5 @@
[Security]
Passphrase=secret123
[General]
AutoConnect=false

View File

@ -3,7 +3,11 @@ import sys
import sys
import os
from scapy.layers.dot11 import *
from scapy.arch import str2mac, get_if_raw_hwaddr
from scapy.arch import str2mac
try:
from scapy.arch import get_if_raw_hwaddr
except:
from scapy.arch.unix import get_if_raw_hwaddr
from time import time, sleep
from threading import Thread

View File

@ -12,7 +12,7 @@ from hostapd import HostapdCLI
class TestWpaNetwork(unittest.TestCase):
'''
The bellow test cases excesise the following connection scenarios:
The below test cases excesise the following connection scenarios:
Network config is
present at start time: Connect: AutoConnect: Result:

View File

@ -12,7 +12,7 @@ from hostapd import HostapdCLI
class TestOpenNetwork(unittest.TestCase):
'''
The bellow test cases excesise the following connection scenarios:
The below test cases excesise the following connection scenarios:
Network config is
present at start time: Connect: AutoConnect: Result:

View File

@ -12,7 +12,7 @@ from hostapd import HostapdCLI
class TestWpaNetwork(unittest.TestCase):
'''
The bellow test cases excesise the following connection scenarios:
The below test cases excesise the following connection scenarios:
Network config is
present at start time: Connect: AutoConnect: Result:

View File

@ -11,7 +11,7 @@ from iwd import NetworkType
class TestMFP(unittest.TestCase):
'''
The bellow test cases excesise the following MFP option setting scenarios:
The below test cases excesise the following MFP option setting scenarios:
IWD_MFP: AP_MFP: Result:
0 0 No MFP, connection succeeds

View File

@ -137,7 +137,7 @@ class Test(unittest.TestCase):
# since (T2 - T1) / 2 is shorter than 60s. It is now about 10s since the last
# renewal or 5s before the next DHCPREQUEST frame that is going to be lost. We'll
# wait T1 seconds, so until about 10s after the failed attempt, we'll check that
# there was no renewal by that time, just in case, and we'll reenable frame delivery.
# there was no renewal by that time, just in case, and we'll re-enable frame delivery.
# We'll then wait another 60s and we should see the lease has been successfully
# renewed some 10 seconds earlier on the 1st DHCPREQUEST retransmission.
#

View File

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

View File

@ -8,6 +8,8 @@ from iwd import PSKAgent
from iwd import NetworkType
class Test(unittest.TestCase):
def connect_failure(self, ex):
self.failure_triggered = True
def test_netconfig_timeout(self):
IWD.copy_to_storage('autoconnect.psk', name='ap-ns1.psk')
@ -27,23 +29,34 @@ class Test(unittest.TestCase):
condition = 'not obj.connected'
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)
device.wait_for_event("connecting (netconfig)")
# Netconfig should fail, and IWD should disconnect
from_condition = 'obj.state == DeviceState.connecting'
to_condition = 'obj.state == DeviceState.disconnecting'
wd.wait_for_object_change(device, from_condition, to_condition, max_wait=60)
# Autoconnect should then try again
condition = 'obj.state == DeviceState.connecting'
wd.wait_for_object_condition(device, condition)
device.wait_for_event("connecting (netconfig)")
# IWD should attempt to connect, but it will of course fail again.
device.wait_for_event("netconfig-failed", timeout=1000)
device.disconnect()
condition = 'obj.state == DeviceState.disconnected'

View File

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

View File

@ -0,0 +1,114 @@
#!/usr/bin/python3
import unittest
import sys
sys.path.append('../util')
from iwd import IWD
from iwd import PSKAgent
from iwd import NetworkType
from hostapd import HostapdCLI
import testutil
class Test(unittest.TestCase):
def validate_connection(self, wd, ssid, hostapd, expected_group):
psk_agent = PSKAgent("secret123")
wd.register_psk_agent(psk_agent)
devices = wd.list_devices(1)
self.assertIsNotNone(devices)
device = devices[0]
device.disconnect()
network = device.get_ordered_network(ssid, full_scan=True)
self.assertEqual(network.type, NetworkType.psk)
network.network_object.connect()
condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_condition(device, condition)
wd.wait(2)
testutil.test_iface_operstate(intf=device.name)
testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
# Initial connection PMKSA should not be used. So we should see the
# SAE group set.
sta_status = hostapd.sta_status(device.address)
self.assertEqual(int(sta_status["sae_group"]), expected_group)
device.disconnect()
condition = 'not obj.connected'
wd.wait_for_object_condition(network.network_object, condition)
wd.unregister_psk_agent(psk_agent)
network.network_object.connect(wait=False)
condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_condition(device, condition)
wd.wait(2)
testutil.test_iface_operstate(intf=device.name)
testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
# Having connected once prior we should have a PMKSA and SAE should not
# have been used.
sta_status = hostapd.sta_status(device.address)
self.assertNotIn("sae_group", sta_status.keys())
device.disconnect()
condition = 'not obj.connected'
wd.wait_for_object_condition(network.network_object, condition)
hostapd.pmksa_flush()
wd.wait(5)
network.network_object.connect()
device.wait_for_event("pmksa-invalid-pmkid")
condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_condition(device, condition)
wd.wait(2)
testutil.test_iface_operstate(intf=device.name)
testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
# Manually flushing the PMKSA from the AP then reconnecting we should
# have failed (INVALID_PMKID) then retried the same BSS with SAE, not
# PMKSA.
sta_status = hostapd.sta_status(device.address)
self.assertEqual(int(sta_status["sae_group"]), expected_group)
def test_pmksa_sae(self):
self.hostapd.wait_for_event("AP-ENABLED")
self.validate_connection(self.wd, "ssidSAE", self.hostapd, 19)
def setUp(self):
self.hostapd.default()
self.wd = IWD(True)
def tearDown(self):
self.wd.clear_storage()
self.wd = None
@classmethod
def setUpClass(cls):
cls.hostapd = HostapdCLI(config='ssidSAE.conf')
@classmethod
def tearDownClass(cls):
pass
if __name__ == '__main__':
unittest.main(exit=True)

View File

@ -0,0 +1,7 @@
[SETUP]
num_radios=2
start_iwd=0
hwsim_medium=yes
[HOSTAPD]
rad0=ssidSAE.conf

View File

@ -1,6 +1,6 @@
hw_mode=g
channel=1
ssid=ssidSAE-default-group
ssid=ssidSAE
wpa=2
wpa_key_mgmt=SAE
@ -9,4 +9,4 @@ sae_password=secret123
sae_groups=19
ieee80211w=2
sae_pwe=0
vendor_elements=dd0cf4f5e8050500000000000000
rsn_preauth=1

View File

@ -0,0 +1,114 @@
#! /usr/bin/python3
import unittest
import sys, os
sys.path.append('../util')
from iwd import IWD
from iwd import NetworkType
from hostapd import HostapdCLI
from packaging import version
from subprocess import run
import re
import testutil
#
# The CSA handling was added in kernel 6.8, so for any earlier kernel this test
# won't pass.
#
def kernel_is_newer(min_version="6.8"):
proc = run(["uname", "-r"], capture_output=True)
version_str = proc.stdout.decode("utf-8")
match = re.match(r"(\d+\.\d+)", version_str)
if not match:
return False
return version.parse(match.group(1)) >= version.parse(min_version)
class Test(unittest.TestCase):
def test_channel_switch_during_roam(self):
wd = self.wd
device = wd.list_devices(1)[0]
ordered_network = device.get_ordered_network('TestFT', full_scan=True)
self.assertEqual(ordered_network.type, NetworkType.psk)
condition = 'not obj.connected'
wd.wait_for_object_condition(ordered_network.network_object, condition)
self.assertFalse(self.bss_hostapd[0].list_sta())
self.assertFalse(self.bss_hostapd[1].list_sta())
device.connect_bssid(self.bss_hostapd[0].bssid)
condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_condition(device, condition)
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % device.address)
testutil.test_iface_operstate(device.name)
testutil.test_ifaces_connected(self.bss_hostapd[0].ifname, device.name)
self.assertRaises(Exception, testutil.test_ifaces_connected,
(self.bss_hostapd[1].ifname, device.name, True, True))
# Start a channel switch and wait for it to begin
self.bss_hostapd[1].chan_switch(6, wait=False)
self.bss_hostapd[1].wait_for_event("CTRL-EVENT-STARTED-CHANNEL-SWITCH")
# Initiate a roam immediately which should get rejected by the kernel
device.roam(self.bss_hostapd[1].bssid)
# IWD should authenticate, then proceed to association
device.wait_for_event("ft-authenticating")
device.wait_for_event("ft-roaming")
# The kernel should reject the association, which should trigger a
# disconnect
condition = 'obj.state == DeviceState.disconnected'
wd.wait_for_object_condition(device, condition)
condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_condition(device, condition)
def tearDown(self):
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" down')
os.system('ip link set "' + self.bss_hostapd[1].ifname + '" down')
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" up')
os.system('ip link set "' + self.bss_hostapd[1].ifname + '" up')
for hapd in self.bss_hostapd:
hapd.default()
self.wd.stop()
self.wd = None
def setUp(self):
self.wd = IWD(True)
@classmethod
def setUpClass(cls):
if not kernel_is_newer():
raise unittest.SkipTest()
IWD.copy_to_storage('TestFT.psk')
cls.bss_hostapd = [ HostapdCLI(config='ft-psk-ccmp-1.conf'),
HostapdCLI(config='ft-psk-ccmp-2.conf'),
HostapdCLI(config='ft-psk-ccmp-3.conf') ]
unused = HostapdCLI(config='ft-psk-ccmp-3.conf')
unused.disable()
cls.bss_hostapd[0].set_address('12:00:00:00:00:01')
cls.bss_hostapd[1].set_address('12:00:00:00:00:02')
cls.bss_hostapd[2].set_address('12:00:00:00:00:03')
HostapdCLI.group_neighbors(*cls.bss_hostapd)
@classmethod
def tearDownClass(cls):
IWD.clear_storage()
cls.bss_hostapd = None

View File

@ -0,0 +1,112 @@
#! /usr/bin/python3
import unittest
import sys, os
sys.path.append('../util')
import iwd
from iwd import IWD
from iwd import PSKAgent
from iwd import NetworkType
from hwsim import Hwsim
from hostapd import HostapdCLI
import testutil
class Test(unittest.TestCase):
def validate_connection(self, wd):
device = wd.list_devices(1)[0]
ordered_network = device.get_ordered_network('TestFT', full_scan=True)
self.assertEqual(ordered_network.type, NetworkType.psk)
condition = 'not obj.connected'
wd.wait_for_object_condition(ordered_network.network_object, condition)
self.assertFalse(self.bss_hostapd[0].list_sta())
self.assertFalse(self.bss_hostapd[1].list_sta())
device.connect_bssid(self.bss_hostapd[0].bssid)
condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_condition(device, condition)
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % device.address)
testutil.test_iface_operstate(device.name)
testutil.test_ifaces_connected(self.bss_hostapd[0].ifname, device.name)
self.assertRaises(Exception, testutil.test_ifaces_connected,
(self.bss_hostapd[1].ifname, device.name, True, True))
self.rule0.enabled = True
device.roam(self.bss_hostapd[1].bssid)
device.clear_events()
device.wait_for_event("handshake-started")
self.bss_hostapd[1].deauthenticate(device.address, reason=15, test=1)
# Check that iwd is on BSS 1 once out of roaming state and doesn't
# go through 'disconnected', 'autoconnect', 'connecting' in between
from_condition = 'obj.state == DeviceState.roaming'
to_condition = 'obj.state == DeviceState.disconnected'
wd.wait_for_object_change(device, from_condition, to_condition)
def test_disconnect_during_handshake(self):
self.bss_hostapd[0].set_value('wpa_key_mgmt', 'WPA-PSK')
self.bss_hostapd[0].reload()
self.bss_hostapd[0].wait_for_event("AP-ENABLED")
self.bss_hostapd[1].set_value('wpa_key_mgmt', 'WPA-PSK')
self.bss_hostapd[1].reload()
self.bss_hostapd[1].wait_for_event("AP-ENABLED")
self.validate_connection(self.wd)
def tearDown(self):
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" down')
os.system('ip link set "' + self.bss_hostapd[1].ifname + '" down')
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" up')
os.system('ip link set "' + self.bss_hostapd[1].ifname + '" up')
for hapd in self.bss_hostapd:
hapd.default()
self.wd.stop()
self.wd = None
def setUp(self):
self.wd = IWD(True)
@classmethod
def setUpClass(cls):
hwsim = Hwsim()
IWD.copy_to_storage('TestFT.psk')
cls.bss_hostapd = [ HostapdCLI(config='ft-psk-ccmp-1.conf'),
HostapdCLI(config='ft-psk-ccmp-2.conf') ]
unused = HostapdCLI(config='ft-psk-ccmp-3.conf')
unused.disable()
cls.bss_hostapd[0].set_address('12:00:00:00:00:01')
cls.bss_hostapd[1].set_address('12:00:00:00:00:02')
rad1 = hwsim.get_radio('rad1')
cls.rule0 = hwsim.rules.create()
cls.rule0.destination = rad1.addresses[0]
cls.rule0.prefix = '08'
cls.rule0.drop = True
HostapdCLI.group_neighbors(*cls.bss_hostapd)
@classmethod
def tearDownClass(cls):
IWD.clear_storage()
cls.bss_hostapd = None
cls.rule0.remove()
if __name__ == '__main__':
unittest.main(exit=True)

View File

@ -149,6 +149,21 @@ class Test(unittest.TestCase):
condition = 'obj.state == DeviceState.disconnected'
self.wd.wait_for_object_condition(device, condition)
def test_ft_deauth_before_association(self):
self.rule2.enabled = True
self.rule3.enabled = True
device = self.wd.list_devices(1)[0]
self.connect(self.wd, device, self.bss_hostapd[0])
device.wait_for_event('ft-authenticating', timeout=60)
self.bss_hostapd[1].deauthenticate(device.address)
condition = 'obj.state == DeviceState.disconnected'
self.wd.wait_for_object_condition(device, condition)
def setUp(self):
self.wd = IWD(True)
@ -232,6 +247,9 @@ class Test(unittest.TestCase):
cls.rule2.remove()
cls.rule3.remove()
cls.assoc_rule.remove()
cls.rule_bss0.remove()
cls.rule_bss1.remove()
cls.rule_bss2.remove()
if __name__ == '__main__':
unittest.main(exit=True)

View File

@ -13,7 +13,7 @@ wpa=2
wpa_key_mgmt=FT-PSK
wpa_pairwise=CCMP
wpa_passphrase=EasilyGuessedPassword
ieee80211w=1
ieee80211w=0
rsn_preauth=1
rsn_preauth_interfaces=lo
disable_pmksa_caching=0

View File

@ -13,7 +13,7 @@ wpa=2
wpa_key_mgmt=FT-PSK
wpa_pairwise=CCMP
wpa_passphrase=EasilyGuessedPassword
ieee80211w=1
ieee80211w=0
rsn_preauth=1
rsn_preauth_interfaces=lo
disable_pmksa_caching=0

View File

@ -13,7 +13,7 @@ wpa=2
wpa_key_mgmt=FT-PSK
wpa_pairwise=CCMP
wpa_passphrase=EasilyGuessedPassword
ieee80211w=1
ieee80211w=0
rsn_preauth=1
rsn_preauth_interfaces=lo
disable_pmksa_caching=0

View File

@ -3,3 +3,6 @@ DisableMacAddressRandomization=true
[General]
RoamRetryInterval=1
# For disconnect_during_handshake_test
ManagementFrameProtection=0

View File

@ -81,12 +81,21 @@ class Test(unittest.TestCase):
cls.bss_hostapd[0].set_value('ocv', '0')
cls.bss_hostapd[0].set_value('ieee80211w', '0')
rad0 = hwsim.get_radio('rad0')
rad1 = hwsim.get_radio('rad1')
cls.rule0 = hwsim.rules.create()
cls.rule0.source = 'any'
cls.rule0.source = rad0.addresses[0]
cls.rule0.bidirectional = True
cls.rule0.signal = -8000
cls.rule0.enabled = True
cls.rule1 = hwsim.rules.create()
cls.rule1.source = rad1.addresses[0]
cls.rule1.bidirectional = True
cls.rule1.signal = -8500
cls.rule1.enabled = True
cls.bss_hostapd[0].set_address('12:00:00:00:00:01')
cls.bss_hostapd[1].set_address('12:00:00:00:00:02')
@ -95,6 +104,7 @@ class Test(unittest.TestCase):
IWD.clear_storage()
cls.bss_hostapd = None
cls.rule0.remove()
cls.rule1.remove()
if __name__ == '__main__':
unittest.main(exit=True)

View File

@ -17,7 +17,7 @@ from hwsim import Hwsim
class Test(unittest.TestCase):
# Normally the time between a failed roam attempt and the next roam attempt
# is 60 seconds (default RoamRetryInterval). Test that we retry roaming
# faster if the transision looks like this: LOW [roam] [same bss] HIGH LOW.
# faster if the transition looks like this: LOW [roam] [same bss] HIGH LOW.
def test_fast_retry(self):
hwsim = Hwsim()

View File

@ -15,7 +15,7 @@ from hostapd import HostapdCLI
from hwsim import Hwsim
class Test(unittest.TestCase):
# Test that we do not periodically retry roaming if the transision looks
# Test that we do not periodically retry roaming if the transition looks
# like this: LOW [roam] [new bss] HIGH.
def test_stop_retry(self):
hwsim = Hwsim()

View File

@ -13,7 +13,7 @@ import testutil
from config import ctx
class Test(unittest.TestCase):
def validate_connection(self, wd, ft=True):
def validate_connection(self, wd, ft=True, check_used_pmksa=False):
device = wd.list_devices(1)[0]
# This won't guarantee all BSS's are found, but at least ensures that
@ -37,6 +37,14 @@ class Test(unittest.TestCase):
self.assertRaises(Exception, testutil.test_ifaces_connected,
(self.bss_hostapd[1].ifname, device.name, True, True))
# If PMKSA was used, hostapd should not include the sae_group key in
# its status for the station.
sta_status = self.bss_hostapd[0].sta_status(device.address)
if check_used_pmksa:
self.assertNotIn("sae_group", sta_status.keys())
else:
self.assertIn("sae_group", sta_status.keys())
device.roam(self.bss_hostapd[1].bssid)
# Check that iwd is on BSS 1 once out of roaming state and doesn't
@ -88,6 +96,31 @@ class Test(unittest.TestCase):
self.validate_connection(wd, True)
def test_ft_roam_pmksa(self):
wd = IWD(True)
self.bss_hostapd[0].set_value('wpa_key_mgmt', 'FT-SAE SAE')
self.bss_hostapd[0].reload()
self.bss_hostapd[0].wait_for_event("AP-ENABLED")
self.bss_hostapd[1].set_value('wpa_key_mgmt', 'FT-SAE SAE')
self.bss_hostapd[1].reload()
self.bss_hostapd[1].wait_for_event("AP-ENABLED")
self.bss_hostapd[2].set_value('wpa_key_mgmt', 'FT-PSK')
self.bss_hostapd[2].reload()
self.bss_hostapd[2].wait_for_event("AP-ENABLED")
self.validate_connection(wd, True)
device = wd.list_devices(1)[0]
device.disconnect()
for hapd in self.bss_hostapd:
hapd.deauthenticate(device.address)
wd.wait(5)
self.validate_connection(wd, True, check_used_pmksa=True)
def test_reassociate_roam_success(self):
wd = IWD(True)
@ -103,6 +136,31 @@ class Test(unittest.TestCase):
self.validate_connection(wd, False)
def test_reassociate_roam_pmksa(self):
wd = IWD(True)
self.bss_hostapd[0].set_value('wpa_key_mgmt', 'SAE')
self.bss_hostapd[0].reload()
self.bss_hostapd[0].wait_for_event("AP-ENABLED")
self.bss_hostapd[1].set_value('wpa_key_mgmt', 'SAE')
self.bss_hostapd[1].reload()
self.bss_hostapd[1].wait_for_event("AP-ENABLED")
self.bss_hostapd[2].set_value('wpa_key_mgmt', 'WPA-PSK')
self.bss_hostapd[2].reload()
self.bss_hostapd[2].wait_for_event("AP-ENABLED")
self.validate_connection(wd, False)
device = wd.list_devices(1)[0]
device.disconnect()
for hapd in self.bss_hostapd:
hapd.deauthenticate(device.address)
wd.wait(5)
self.validate_connection(wd, False, check_used_pmksa=True)
def tearDown(self):
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" down')
os.system('ip link set "' + self.bss_hostapd[1].ifname + '" down')

View File

@ -52,12 +52,6 @@ class Test(unittest.TestCase):
self.hostapd.wait_for_event("AP-ENABLED")
self.validate_connection(self.wd, "ssidSAE", self.hostapd, 19)
def test_SAE_force_group_19(self):
# Vendor data from APs which require group 19 be used first
self.hostapd.reload()
self.hostapd.wait_for_event("AP-ENABLED")
self.validate_connection(self.wd, "ssidSAE-default-group", self.hostapd_defgroup, 19)
def test_SAE_Group20(self):
self.hostapd.set_value('sae_groups', '20')
self.hostapd.reload()
@ -88,7 +82,6 @@ class Test(unittest.TestCase):
def setUpClass(cls):
cls.hostapd = HostapdCLI(config='ssidSAE.conf')
cls.hostapd_h2e = HostapdCLI(config='ssidSAE-H2E.conf')
cls.hostapd_defgroup = HostapdCLI(config='ssidSAE-default-group.conf')
@classmethod
def tearDownClass(cls):

View File

@ -13,7 +13,7 @@ import testutil
class Test(unittest.TestCase):
def validate_connection(self, wd, rejected=False):
def validate_connection(self, wd, hostapd, rejected=False):
devices = wd.list_devices(1)
self.assertIsNotNone(devices)
device = devices[0]
@ -29,14 +29,14 @@ class Test(unittest.TestCase):
wd.wait(2)
testutil.test_iface_operstate(intf=device.name)
testutil.test_ifaces_connected(if0=device.name, if1=self.hostapd.ifname)
testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
if not rejected:
self.assertEqual(device.event_ocurred("ecc-group-rejected"), False)
print(self.hostapd._get_status())
print(hostapd._get_status())
sta_status = self.hostapd.sta_status(device.address)
sta_status = hostapd.sta_status(device.address)
print(sta_status)
@ -51,22 +51,27 @@ class Test(unittest.TestCase):
# - Connect, try only group 19
def test_auto_selection(self):
IWD.copy_to_storage("profiles/ssidSAE.psk.default", name="ssidSAE.psk")
self.validate_connection(self.wd, rejected=True)
self.validate_connection(self.wd, self.hostapd, rejected=True)
self.validate_connection(self.wd, rejected=False)
self.validate_connection(self.wd, self.hostapd, rejected=False)
# Try group 19 first
def test_default_group_enabled(self):
IWD.copy_to_storage("profiles/ssidSAE.psk.default_group", name="ssidSAE.psk")
self.validate_connection(self.wd)
self.validate_connection(self.wd, self.hostapd)
# Try group 19 first, with H2E
def test_default_group_enabled_h2e(self):
IWD.copy_to_storage("profiles/ssidSAE-H2E.psk.default_group", name="ssidSAE-H2E.psk")
self.validate_connection(self.wd, self.hostapd_h2e)
# Same as auto-selection but won't retain the default group setting
def test_default_group_disabled(self):
IWD.copy_to_storage("profiles/ssidSAE.psk.most_secure", name="ssidSAE.psk")
self.validate_connection(self.wd, rejected=True)
self.validate_connection(self.wd, self.hostapd, rejected=True)
# IWD should then retry but use only group 19
self.validate_connection(self.wd, rejected=True)
self.validate_connection(self.wd, self.hostapd, rejected=True)
def setUp(self):
self.hostapd.default()
@ -88,6 +93,9 @@ class Test(unittest.TestCase):
cls.hostapd = HostapdCLI(config='ssidSAE.conf')
cls.hostapd.default()
cls.hostapd_h2e = HostapdCLI(config='ssidSAE-H2E.conf')
cls.hostapd_h2e.default()
@classmethod
def tearDownClass(cls):
pass

View File

@ -1,9 +1,8 @@
[SETUP]
num_radios=4
num_radios=3
start_iwd=0
hwsim_medium=yes
[HOSTAPD]
rad0=ssidSAE.conf
rad1=ssidSAE-H2E.conf
rad2=ssidSAE-default-group.conf

View File

@ -4,3 +4,6 @@
# hardware, but fails when used in simulated environment with mac80211_hwsim.
# Disable MAC randomization for the tests with hidden networks.
DisableMacAddressRandomization=true
[General]
DisablePMKSA=true

View File

@ -0,0 +1,5 @@
[Security]
Passphrase=secret123
[Settings]
UseDefaultEccGroup=true

View File

@ -8,11 +8,12 @@ import iwd
from iwd import IWD
from iwd import PSKAgent
from iwd import NetworkType
from hostapd import HostapdCLI
import testutil
class Test(unittest.TestCase):
def test_connection_success(self):
def test_incorrect_password(self):
wd = IWD(True)
psk_agent = PSKAgent("InvalidPassword")
@ -34,6 +35,35 @@ class Test(unittest.TestCase):
wd.unregister_psk_agent(psk_agent)
def test_deauth_after_connection(self):
wd = IWD(True)
hostapd = HostapdCLI(config="ssidWPA2.conf")
psk_agent = PSKAgent("secret123")
wd.register_psk_agent(psk_agent)
devices = wd.list_devices(1)
self.assertIsNotNone(devices)
device = devices[0]
ordered_network = device.get_ordered_network('ssidWPA2')
self.assertEqual(ordered_network.type, NetworkType.psk)
condition = 'not obj.connected'
wd.wait_for_object_condition(ordered_network.network_object, condition)
ordered_network.network_object.connect(wait=False)
device.wait_for_event("authenticating")
# Trigger a deauth just after authenticating
hostapd.deauthenticate(device.address)
device.wait_for_event("disconnected")
wd.unregister_psk_agent(psk_agent)
@classmethod
def setUpClass(cls):
pass

View File

@ -184,8 +184,12 @@ class HostapdCLI(object):
cmd = self.cmdline + ['wps_pin', 'any', pin]
ctx.start_process(cmd).wait()
def deauthenticate(self, client_address):
def deauthenticate(self, client_address, reason=None, test=None):
cmd = self.cmdline + ['deauthenticate', client_address]
if reason:
cmd.append(f"reason={reason} test={test}")
ctx.start_process(cmd).wait()
def eapol_reauth(self, client_address):
@ -284,13 +288,15 @@ class HostapdCLI(object):
cmd = 'RESEND_M3 %s' % address
self.ctrl_sock.sendall(cmd.encode('utf-8'))
def chan_switch(self, channel):
def chan_switch(self, channel, wait=True):
if channel > len(chan_freq_map):
raise Exception("Only 2.4GHz channels supported for chan_switch")
cmd = self.cmdline + ['chan_switch', '50', str(chan_freq_map[channel])]
ctx.start_process(cmd).wait()
self.wait_for_event('AP-CSA-FINISHED')
if wait:
self.wait_for_event('AP-CSA-FINISHED')
def _get_status(self):
ret = {}
@ -364,3 +370,7 @@ class HostapdCLI(object):
others = [h for h in args if h != hapd]
hapd._add_neighbors(*others)
def pmksa_flush(self):
cmd = self.cmdline + ['pmksa_flush']
ctx.start_process(cmd).wait()

View File

@ -7,7 +7,10 @@ from weakref import WeakValueDictionary
from abc import ABCMeta, abstractmethod
from enum import Enum
from scapy.all import *
from scapy.contrib.wpa_eapol import WPA_key
try:
from scapy.contrib.wpa_eapol import WPA_key
except:
from scapy.layers.eap import EAPOL_KEY
import iwd
from config import ctx
@ -444,9 +447,15 @@ class Hwsim(iwd.AsyncOpAbstract):
# NOTE: Expected key_info is 0x008a, with the install flag
# this becomes 0x00ca.
eapol = WPA_key( descriptor_type = 2,
key_info = 0x00ca, # Includes an invalid install flag!
replay_counter = struct.pack(">Q", 100))
try:
eapol = WPA_key( descriptor_type = 2,
key_info = 0x00ca, # Includes an invalid install flag!
replay_counter = struct.pack(">Q", 100))
except:
eapol = EAPOL_KEY( key_descriptor_type = 2,
install = 1,
key_ack = 1,
key_replay_counter = 1)
frame /= LLC()/SNAP()/EAPOL(version="802.1X-2004", type="EAPOL-Key")
frame /= eapol

View File

@ -112,8 +112,8 @@ class AsyncOpAbstract(object):
self._is_completed = True
self._exception = _convert_dbus_ex(ex)
def _wait_for_async_op(self):
ctx.non_block_wait(lambda s: s._is_completed, 30, self, exception=None)
def _wait_for_async_op(self, timeout=50):
ctx.non_block_wait(lambda s: s._is_completed, timeout, self, exception=None)
self._is_completed = False
if self._exception is not None:
@ -280,8 +280,15 @@ class StationDebug(IWDDBusAbstract):
def autoconnect(self):
return self._properties['AutoConnect']
def connect_bssid(self, address):
self._iface.ConnectBssid(dbus.ByteArray.fromhex(address.replace(':', '')))
def connect_bssid(self, address, wait=True):
self._iface.ConnectBssid(
dbus.ByteArray.fromhex(address.replace(':', '')),
reply_handler=self._success,
error_handler=self._failure
)
if wait:
self._wait_for_async_op()
def roam(self, address):
self._iface.Roam(dbus.ByteArray.fromhex(address.replace(':', '')))
@ -299,6 +306,9 @@ class StationDebug(IWDDBusAbstract):
return False
def clear_events(self):
self._events = []
def wait_for_event(self, event, timeout=10):
return ctx.non_block_wait(self._poll_event, timeout, event,
exception=TimeoutError("waiting for event"))
@ -447,13 +457,15 @@ class Device(IWDDBusAbstract):
self._wps_manager_if = None
self._station_if = None
self._station_props = None
self._station_debug_obj = None
self._dpp_obj = None
self._sc_dpp_obj = None
self._ap_obj = None
IWDDBusAbstract.__init__(self, *args, **kwargs)
self._station_debug_obj = StationDebug(object_path=self._object_path,
namespace=self._namespace)
@property
def _wps_manager(self):
if self._wps_manager_if is None:
@ -593,6 +605,11 @@ class Device(IWDDBusAbstract):
props = self._station_properties()
return props.get('ConnectedNetwork')
@property
def connected_bss(self):
props = self._station_properties()
return props.get('ConnectedAccessPoint')
@property
def powered(self):
'''
@ -627,6 +644,19 @@ class Device(IWDDBusAbstract):
self._station_debug._prop_proxy.Set(IWD_STATION_DEBUG_INTERFACE,
'AutoConnect', value)
@property
def affinities(self):
return self._station_properties()['Affinities']
@affinities.setter
def affinities(self, values):
self._station_properties()
self._station_prop_if.Set(
IWD_STATION_INTERFACE, 'Affinities',
dbus.Array([dbus.ObjectPath(v) for v in values], signature="o"),
reply_handler=self._success, error_handler=self._failure)
self._wait_for_async_op()
def scan(self, wait=True):
'''Schedule a network scan.
@ -847,8 +877,8 @@ class Device(IWDDBusAbstract):
def stop_adhoc(self):
self._prop_proxy.Set(IWD_DEVICE_INTERFACE, 'Mode', 'station')
def connect_bssid(self, address):
self._station_debug.connect_bssid(address)
def connect_bssid(self, address, wait=True):
self._station_debug.connect_bssid(address, wait=wait)
def roam(self, address):
self._station_debug.roam(address)
@ -859,6 +889,9 @@ class Device(IWDDBusAbstract):
def wait_for_event(self, event, timeout=10):
self._station_debug.wait_for_event(event, timeout)
def clear_events(self):
self._station_debug.clear_events()
def event_ocurred(self, event):
return self._station_debug.event_ocurred(event)
@ -969,7 +1002,11 @@ class Network(IWDDBusAbstract):
'''
return bool(self._properties['Connected'])
def connect(self, wait=True):
@property
def extended_service_set(self):
return self._properties['ExtendedServiceSet']
def connect(self, wait=True, timeout=50, reply_handler=None, error_handler=None):
'''
Connect to the network. Request the device implied by the object
path to connect to specified network.
@ -984,12 +1021,19 @@ class Network(IWDDBusAbstract):
@rtype: void
'''
if not reply_handler:
reply_handler = self._success
if not error_handler:
error_handler = self._failure
self._iface.Connect(dbus_interface=self._iface_name,
reply_handler=self._success,
error_handler=self._failure)
reply_handler=reply_handler,
error_handler=error_handler,
timeout=timeout)
if wait:
self._wait_for_async_op()
self._wait_for_async_op(timeout=timeout)
def __str__(self, prefix = ''):
return prefix + 'Network:\n' \
@ -1453,10 +1497,10 @@ class IWD(AsyncOpAbstract):
@staticmethod
def create_in_storage(file_name, file_content, storage_dir=IWD_STORAGE_DIR):
fo = open(storage_dir + '/' + file_name, 'w')
f = open(storage_dir + '/' + file_name, 'w')
fo.write(file_content)
fo.close()
f.write(file_content)
f.close()
@staticmethod
def _ensure_storage_dir_exists(storage_dir):

View File

@ -237,7 +237,7 @@ class Wpas:
('' if go_intent is None else ' go_intent=' + str(go_intent)))
self.wait_for_event('OK')
# Pre-accept the next GO Negotiation Request from this peer to avoid the extra Respone + Request frames
# Pre-accept the next GO Negotiation Request from this peer to avoid the extra Response + Request frames
def p2p_authorize(self, peer, pin=None, go_intent=None):
self._rx_data = []
self._ctrl_request('P2P_CONNECT ' + peer['p2p_dev_addr'] + ' ' + ('pbc' if pin is None else pin) +

109
client/bss.c Normal file
View File

@ -0,0 +1,109 @@
/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2024, Locus Robotics
*
* 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 <ell/ell.h>
#include "ell/useful.h"
#include "client/dbus-proxy.h"
#include "client/display.h"
struct bss {
char *address;
};
static const char *get_address(const void *data)
{
const struct bss *bss = data;
return bss->address;
}
static void update_address(void *data, struct l_dbus_message_iter *variant)
{
struct bss *bss = data;
const char *value;
l_free(bss->address);
if (!l_dbus_message_iter_get_variant(variant, "s", &value)) {
bss->address = NULL;
return;
}
bss->address = l_strdup(value);
}
static const struct proxy_interface_property bss_properties[] = {
{ "Address", "s", update_address, get_address },
{ }
};
static void *bss_create(void)
{
return l_new(struct bss, 1);
}
static void bss_destroy(void *data)
{
struct bss *bss = data;
l_free(bss->address);
l_free(bss);
}
static void bss_display_inline(const char *margin, const void *data)
{
const struct bss *bss = data;
display("%s%s\n", margin, bss->address);
}
static const struct proxy_interface_type_ops ops = {
.create = bss_create,
.destroy = bss_destroy,
.display = bss_display_inline,
};
static struct proxy_interface_type bss_interface_type = {
.interface = IWD_BSS_INTERFACE,
.properties = bss_properties,
.ops = &ops,
};
static int bss_interface_init(void)
{
proxy_interface_type_register(&bss_interface_type);
return 0;
}
static void bss_interface_exit(void)
{
proxy_interface_type_unregister(&bss_interface_type);
}
INTERFACE_TYPE(bss_interface_type, bss_interface_init, bss_interface_exit)

View File

@ -47,10 +47,10 @@ static struct l_dbus *dbus;
static struct l_queue *proxy_interfaces;
static struct l_queue *proxy_interface_types;
void proxy_properties_display(const struct proxy_interface *proxy,
const char *caption, const char *margin,
unsigned int name_column_width,
unsigned int value_column_width)
void proxy_properties_display_inline(const struct proxy_interface *proxy,
const char *margin,
unsigned int name_column_width,
unsigned int value_column_width)
{
const void *data;
const struct proxy_interface_property *properties;
@ -59,11 +59,6 @@ void proxy_properties_display(const struct proxy_interface *proxy,
if (!proxy->type->properties)
return;
display_table_header(caption, "%s%-*s %-*s %-*s", margin,
8, "Settable",
name_column_width, "Property",
value_column_width, "Value");
data = proxy_interface_get_data(proxy);
properties = proxy->type->properties;
@ -82,6 +77,31 @@ void proxy_properties_display(const struct proxy_interface *proxy,
}
}
void proxy_properties_display_header(const char *caption, const char *margin,
unsigned int name_column_width,
unsigned int value_column_width)
{
display_table_header(caption, "%s%-*s %-*s %-*s", margin,
8, "Settable",
name_column_width, "Property",
value_column_width, "Value");
}
void proxy_properties_display(const struct proxy_interface *proxy,
const char *caption, const char *margin,
unsigned int name_column_width,
unsigned int value_column_width)
{
if (!proxy->type->properties)
return;
proxy_properties_display_header(caption, margin, name_column_width,
value_column_width);
proxy_properties_display_inline(proxy, margin, name_column_width,
value_column_width);
}
static const void *proxy_interface_property_tostr(
const struct proxy_interface *proxy,
const char *name)

View File

@ -41,6 +41,7 @@ struct proxy_interface;
#define IWD_DPP_INTERFACE "net.connman.iwd.DeviceProvisioning"
#define IWD_DPP_PKEX_INTERFACE \
"net.connman.iwd.SharedCodeDeviceProvisioning"
#define IWD_BSS_INTERFACE "net.connman.iwd.BasicServiceSet"
typedef bool (*proxy_property_match_func_t) (const void *a, const void *b);
@ -95,6 +96,13 @@ void proxy_properties_display(const struct proxy_interface *proxy,
const char *caption, const char *margin,
unsigned int name_column_width,
unsigned int value_column_width);
void proxy_properties_display_inline(const struct proxy_interface *proxy,
const char *margin,
unsigned int name_column_width,
unsigned int value_column_width);
void proxy_properties_display_header(const char *caption, const char *margin,
unsigned int name_column_width,
unsigned int value_column_width);
char *proxy_property_str_completion(const struct proxy_interface_type *type,
proxy_property_match_func_t function,

View File

@ -95,6 +95,8 @@ static const struct diagnostic_dict_mapping diagnostic_mapping[] = {
{ "Frequency", 'u' },
{ "Channel", 'q' },
{ "Security", 's' },
{ "InactiveTime", 'u', "ms" },
{ "ConnectedTime", 'u', "s" },
{ NULL }
};
@ -186,5 +188,5 @@ void diagnostic_display(struct l_dbus_message_iter *dict,
return;
parse_error:
display_error("Error parsing dignostics");
display_error("Error parsing diagnostics");
}

View File

@ -35,6 +35,7 @@ struct network {
char *identity;
char *name;
char *type;
struct l_queue *bss_list;
const struct proxy_interface *device;
};
@ -146,11 +147,58 @@ static void update_type(void *data, struct l_dbus_message_iter *variant)
network->type = l_strdup(value);
}
static bool match_path(const void *a, const void *user_data)
{
const char *path1 = a;
const char *path2 = user_data;
return !strcmp(path1, path2);
}
static void update_ess(void *data, struct l_dbus_message_iter *variant)
{
struct network *network = data;
struct l_dbus_message_iter array;
const char *path;
if (!network->bss_list)
network->bss_list = l_queue_new();
if (!l_dbus_message_iter_get_variant(variant, "ao", &array))
return;
while (l_dbus_message_iter_next_entry(&array, &path)) {
l_free(l_queue_remove_if(network->bss_list, match_path, path));
l_queue_push_head(network->bss_list, l_strdup(path));
}
}
static const char *get_ess(const void *data)
{
const struct network *network = data;
static char count[10];
snprintf(count, 10, "Count %u", l_queue_length(network->bss_list));
return count;
}
struct l_queue *network_get_bss_list(
const struct proxy_interface *network_proxy)
{
const struct network *network = proxy_interface_get_data(network_proxy);
if (!network)
return NULL;
return network->bss_list;
}
static const struct proxy_interface_property network_properties[] = {
{ "Name", "s", update_name, get_name },
{ "Connected", "b", update_connected},
{ "Device", "o", update_device},
{ "Type", "s", update_type},
{ "ExtendedServiceSet", "ao", update_ess, get_ess },
{ }
};
@ -171,7 +219,7 @@ static void network_display_inline(const char *margin, const void *data)
display("%s%s %s %s\n", margin, network->name ? network->name : "",
network->type ? network->type : "",
network->connected ? "connected" : "diconnected");
network->connected ? "connected" : "disconnected");
}
static void *network_create(void)
@ -186,6 +234,7 @@ static void network_destroy(void *data)
l_free(network->name);
l_free(network->type);
l_free(network->identity);
l_queue_destroy(network->bss_list, l_free);
network->device = NULL;

View File

@ -37,3 +37,5 @@ char *network_name_completion(const struct proxy_interface *device,
struct l_queue *network_match_by_device_and_args(
const struct proxy_interface *device,
const struct network_args *args);
struct l_queue *network_get_bss_list(
const struct proxy_interface *network_proxy);

View File

@ -175,8 +175,8 @@ static void display_addresses(const char *device_name)
continue;
have_address = true;
display("%s%*s %-*s%-*s\n", MARGIN, 8, "", 20,
"IPv6 address", 47, addrstr);
display_table_row(MARGIN, 3, 8, "", 20,
"IPv6 address", 47, addrstr);
} else if (cur->ifa_addr->sa_family == AF_INET) {
struct sockaddr_in *si =
(struct sockaddr_in *) cur->ifa_addr;
@ -283,28 +283,26 @@ static char *connect_cmd_arg_completion(const char *text, int state,
return network_name_completion(device, text, state);
}
static enum cmd_status cmd_connect(const char *device_name,
char **argv, int argc)
static const struct proxy_interface *find_network(const char *device_name,
const char *name,
const char *type)
{
struct network_args network_args;
struct l_queue *match;
const struct proxy_interface *network_proxy;
const struct proxy_interface *device_proxy;
if (argc < 1)
return CMD_STATUS_INVALID_ARGS;
device_proxy = device_proxy_find_by_name(device_name);
if (!device_proxy)
return CMD_STATUS_INVALID_VALUE;
return NULL;
network_args.name = argv[0];
network_args.type = argc >= 2 ? argv[1] : NULL;
network_args.name = name;
network_args.type = type;
match = network_match_by_device_and_args(device_proxy, &network_args);
if (!match) {
display("Invalid network name '%s'\n", network_args.name);
return CMD_STATUS_INVALID_VALUE;
return NULL;
}
if (l_queue_length(match) > 1) {
@ -315,11 +313,28 @@ static enum cmd_status cmd_connect(const char *device_name,
l_queue_destroy(match, NULL);
return CMD_STATUS_INVALID_VALUE;
return NULL;
}
network_proxy = l_queue_pop_head(match);
l_queue_destroy(match, NULL);
return network_proxy;
}
static enum cmd_status cmd_connect(const char *device_name,
char **argv, int argc)
{
const struct proxy_interface *network_proxy;
if (argc < 1)
return CMD_STATUS_INVALID_ARGS;
network_proxy = find_network(device_name, argv[0],
argc >= 2 ? argv[1] : NULL);
if (!network_proxy)
return CMD_STATUS_INVALID_VALUE;
network_connect(network_proxy);
return CMD_STATUS_TRIGGERED;
@ -708,6 +723,55 @@ static enum cmd_status cmd_show(const char *device_name,
return CMD_STATUS_TRIGGERED;
}
static enum cmd_status cmd_get_bsses(const char *device_name,
char **argv, int argc)
{
const struct proxy_interface *station_i =
device_proxy_find(device_name, IWD_STATION_INTERFACE);
const struct station *station = proxy_interface_get_data(station_i);
struct l_queue *bss_list;
const struct l_queue_entry *e;
const struct proxy_interface *network_proxy;
char header[256];
if (argc > 0)
network_proxy = find_network(device_name, argv[0],
argc >= 2 ? argv[1] : NULL);
else
network_proxy = station->connected_network;
if (!network_proxy) {
display_error("Can't find network");
return CMD_STATUS_INVALID_ARGS;
}
bss_list = network_get_bss_list(network_proxy);
if (!bss_list) {
display_error("No BSS list for network");
return CMD_STATUS_FAILED;
}
sprintf(header, "%s BasicServiceSets", network_get_name(network_proxy));
proxy_properties_display_header(header, MARGIN, 10, 18);
for (e = l_queue_get_entries(bss_list); e; e = e->next) {
const char *path = e->data;
const struct proxy_interface *bss_i = proxy_interface_find(
IWD_BSS_INTERFACE, path);
if (!bss_i)
continue;
display_table_row(MARGIN, 1, strlen(path), path);
proxy_properties_display_inline(bss_i, MARGIN, 10, 18);
display_table_row(MARGIN, 1, 1, "");
}
return CMD_STATUS_DONE;
}
static const struct command station_commands[] = {
{ NULL, "list", NULL, cmd_list, "List devices in Station mode", true },
{ "<wlan>", "connect",
@ -732,6 +796,8 @@ static const struct command station_commands[] = {
"Get hidden APs", true },
{ "<wlan>", "scan", NULL, cmd_scan, "Scan for networks" },
{ "<wlan>", "show", NULL, cmd_show, "Show station info", true },
{ "<wlan>", "get-bsses", "[network] [security]", cmd_get_bsses,
"Get BSS's for a network", true },
{ }
};

View File

@ -1,10 +1,12 @@
AC_PREREQ([2.69])
AC_INIT([iwd],[2.17])
AC_INIT([iwd],[3.10])
AC_CONFIG_HEADERS(config.h)
AC_CONFIG_AUX_DIR(build-aux)
AC_CONFIG_MACRO_DIR(build-aux)
AC_REQUIRE_AUX_FILE([tap-driver.sh])
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
@ -29,6 +31,7 @@ AC_PROG_CC_GCOV
AC_PROG_INSTALL
AC_PROG_MKDIR_P
AC_PROG_LN_S
AC_PROG_AWK
AC_SYS_LARGEFILE
@ -297,7 +300,7 @@ if (test "${enable_external_ell}" = "yes"); then
test "${enable_monitor}" != "no" ||
test "${enable_wired}" = "yes" ||
test "${enable_hwsim}" = "yes"); then
ell_min_version="0.64"
ell_min_version="0.77"
else
ell_min_version="0.5"
fi

View File

@ -31,6 +31,12 @@ Methods array{dict} GetDiagnostics()
TxMCS [optional] - Transmitting MCS index
InactiveTime [optional] - Time duration (in ms) for which the STA
connected to this BSS is currently inactive.
ConnectedTime [optional] - Time duration (in s) for which the STA
remains connected to this BSS.
Possible errors: net.connman.iwd.Failed
net.connman.iwd.NotConnected
net.connman.iwd.NotFound

10
doc/basic-service-set.txt Normal file
View File

@ -0,0 +1,10 @@
Basic service set hierarchy
=================
Service net.connman.iwd
Interface net.connman.iwd.BasicServiceSet
Object path /net/connman/iwd/{phy0,phy1,...}/{1,2,...}/Xxx
Properties string Address [readonly]
MAC address of BSS

View File

@ -322,10 +322,10 @@ M18: Use appropriate logging levels
An appropriate log level should be used depending on the type of message
being logged. Logging is done using the l_log APIs in ELL:
l_error An unexpected condition ocurred. These are generally fatal to the
l_error An unexpected condition occurred. These are generally fatal to the
current connection/protocol that is running but not generally to IWD's
overall operation.
l_warn An unexpected, but non-fatal condition ocurred
l_warn An unexpected, but non-fatal condition occurred
l_notice Should not be used directly. This log level is reserved for special
event type notifications which is handled by iwd_notice().
l_info Information that is expected during normal operation. l_info's use

View File

@ -135,7 +135,7 @@ Object path /net/connman/iwd/{phy0,phy1,...}/{1,2,...}
void StartConfigurator(object agent_path)
Start a shared code configurator using an agent
(distingushed by 'agent_path') to obtain the shared
(distinguished by 'agent_path') to obtain the shared
code. This method is meant for an automated use case
where a configurator is capable of configuring multiple
enrollees, and distinguishing between them by their
@ -196,7 +196,7 @@ Methods void Release() [noreply]
string RequestSharedCode(string identifier)
This method gets called when a shared code is requested
for a particular enrollee, distingushed by the
for a particular enrollee, distinguished by the
identifier. The shared code agent should lookup the
identifier and return the shared code, or return an
error if not found.

View File

@ -5,7 +5,7 @@ credentials for your e.g. cable/cellular provider, or via a dedicated account
like Boingo. Lots of these services also allow you to roam between networks.
The underlying authentication is standard WPA2-Enterprise but Hotspot 2.0 adds a
'discovery' stage to identifiying networks. This discovery is done using ANQP,
'discovery' stage to identifying networks. This discovery is done using ANQP,
which queries the network for additional information to determine if the client
has the credentials to connect.

View File

@ -11,6 +11,12 @@ Methods void Connect()
the object path to connect to specified network.
Connecting to WEP networks is not supported.
Note: When [General].EnableNetworkConfiguration is set
to true a call to Connect() has the potential to take
a significant amount of time. Specifically if DHCP is
either slow, or is unable to complete. The timeout for
DHCP is roughly 30 seconds per BSS.
Possible errors: net.connman.iwd.Aborted
net.connman.iwd.Busy
net.connman.iwd.Failed
@ -50,3 +56,8 @@ Properties string Name [readonly]
corresponding to this Network. If the network
is not provisioned or has not been connected to
before, the property is omitted.
array(object) ExtendedServiceSet [readonly]
Contains a list of paths of each individual
BasicServiceSet object.

View File

@ -56,7 +56,7 @@ Methods array(on) GetPeers()
between requested threshold values is a compromise
between resolution and the frequency of system
wakeups and context-switches that are going to be
occuring to update the client's signal meter. Only
occurring to update the client's signal meter. Only
one agent can be registered at any time.
Possible errors: [service].Error.InvalidArguments

View File

@ -164,6 +164,29 @@ Properties string State [readonly]
for networks. net.connman.iwd.Network objects are
updated when this property goes from true to false.
object ConnectedAccessPoint [readonly, optional]
net.connman.iwd.BasicServiceSet object representing the
BSS the device is currently connected to or to which
a connection is in progress.
ao Affinities [optional] [experimental]
Array of net.connman.iwd.BasicServiceSet object paths
that will be treated with higher affinity compared to
other BSS's. Currently the only allowed value to be
set in this array is the path to the currently connected
BasicServiceSet object, i.e.
Station.ConnectedAccessPoint.
Setting the affinity will lower the roaming threshold,
effectively locking IWD to the current BSS unless the
RSSI drops below the critical threshold set by
[General].CriticalRoamThreshold{5G} at which point
IWD will proceed with normal roaming behavior.
This property is cleared on roams/disconnections.
SignalLevelAgent hierarchy
==========================

View File

@ -53,6 +53,12 @@ Methods dict GetDiagnostics()
- GCMP-256
- CCMP-256
InactiveTime [optional] - Time duration (in ms) for which this STA
is currently inactive.
ConnectedTime [optional] - Time Duration (in s) for which this STA
remains connected to the BSS.
Possible errors: net.connman.iwd.Busy
net.connman.iwd.Failed
net.connman.iwd.NotConnected

View File

@ -218,7 +218,7 @@ supplicant running IWD:
#~~~~~~~~~~~~~~~~~~~~~~~~~ hw.conf ~~~~~~~~~~~~~~~~~~~~~~~~~
# Lines starting with # are ignored
# 'SETUP' is a manditory configuration group.
# 'SETUP' is a mandatory configuration group.
[SETUP]
#
# Total number of radios requested per network setup. This includes

View File

@ -224,129 +224,42 @@ struct iwmon_interface {
char *ifname;
bool exists;
struct l_netlink *rtnl;
struct l_netlink *genl;
struct l_genl *genl;
struct l_io *io;
};
static struct iwmon_interface monitor_interface = { };
static void genl_parse(uint16_t type, const void *data, uint32_t len,
const char *ifname)
static void nl80211_appeared(const struct l_genl_family_info *info,
void *user_data)
{
const struct genlmsghdr *genlmsg = data;
const struct nlattr *nla;
char name[GENL_NAMSIZ];
uint16_t id = 0;
if (nlmon)
return;
if (type != GENL_ID_CTRL)
return;
if (genlmsg->cmd != CTRL_CMD_NEWFAMILY)
return;
for (nla = data + GENL_HDRLEN; NLA_OK(nla, len);
nla = NLA_NEXT(nla, len)) {
switch (nla->nla_type & NLA_TYPE_MASK) {
case CTRL_ATTR_FAMILY_ID:
id = *((uint16_t *) NLA_DATA(nla));
break;
case CTRL_ATTR_FAMILY_NAME:
strncpy(name, NLA_DATA(nla), GENL_NAMSIZ - 1);
break;
}
}
if (id == 0)
return;
if (strcmp(name, NL80211_GENL_NAME))
return;
const char *ifname = user_data;
monitor_interface.io = open_packet(ifname);
if (!monitor_interface.io)
goto failed;
nlmon = nlmon_open(id, writer_path, &config);
nlmon = nlmon_open(l_genl_family_info_get_id(info),
writer_path, &config);
if (!nlmon)
goto failed;
l_io_set_read_handler(monitor_interface.io, nlmon_receive, nlmon, NULL);
return;
failed:
l_main_quit();
}
static void genl_notify(uint16_t type, const void *data,
uint32_t len, void *user_data)
static struct l_genl *genl_lookup(const char *ifname)
{
const char *ifname = user_data;
genl_parse(type, data, len, ifname);
}
static void genl_callback(int error, uint16_t type, const void *data,
uint32_t len, void *user_data)
{
const char *ifname = user_data;
if (error < 0) {
fprintf(stderr, "Failed to lookup nl80211 family\n");
l_main_quit();
return;
}
genl_parse(type, data, len, ifname);
}
static struct l_netlink *genl_lookup(const char *ifname)
{
struct l_netlink *genl;
char buf[GENL_HDRLEN + NLA_HDRLEN + GENL_NAMSIZ];
struct genlmsghdr *genlmsg;
struct nlattr *nla;
genl = l_netlink_new(NETLINK_GENERIC);
l_netlink_register(genl, GENL_ID_CTRL, genl_notify, NULL, NULL);
genlmsg = (struct genlmsghdr *) buf;
genlmsg->cmd = CTRL_CMD_GETFAMILY;
genlmsg->version = 0;
genlmsg->reserved = 0;
nla = (struct nlattr *) (buf + GENL_HDRLEN);
nla->nla_len = NLA_HDRLEN + GENL_NAMSIZ;
nla->nla_type = CTRL_ATTR_FAMILY_NAME;
strncpy(buf + GENL_HDRLEN + NLA_HDRLEN,
NL80211_GENL_NAME, GENL_NAMSIZ);
l_netlink_send(genl, GENL_ID_CTRL, 0, buf, sizeof(buf),
genl_callback, (char *) ifname, NULL);
struct l_genl *genl = l_genl_new();
l_genl_request_family(genl, NL80211_GENL_NAME, nl80211_appeared,
(char *) ifname, NULL);
return genl;
}
static size_t rta_add(void *rta_buf, unsigned short type, uint16_t len,
const void *data)
{
unsigned short rta_len = RTA_LENGTH(len);
struct rtattr *rta = rta_buf;
memset(RTA_DATA(rta), 0, RTA_SPACE(len));
rta->rta_len = rta_len;
rta->rta_type = type;
if (len)
memcpy(RTA_DATA(rta), data, len);
return RTA_SPACE(len);
}
static bool rta_linkinfo_kind(struct rtattr *rta, unsigned short len,
const char* kind)
{
@ -375,10 +288,9 @@ static struct l_netlink *rtm_interface_send_message(struct l_netlink *rtnl,
{
size_t nlmon_type_len = strlen(NLMON_TYPE);
unsigned short ifname_len = 0;
size_t bufsize;
struct ifinfomsg *rtmmsg;
void *rta_buf;
struct rtattr *linkinfo_rta;
struct l_netlink_message *nlm;
struct ifinfomsg ifi;
uint16_t flags = 0;
if (ifname) {
ifname_len = strlen(ifname) + 1;
@ -387,64 +299,41 @@ static struct l_netlink *rtm_interface_send_message(struct l_netlink *rtnl,
return NULL;
}
if (!L_IN_SET(rtm_msg_type, RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK))
return NULL;
if (!rtnl)
rtnl = l_netlink_new(NETLINK_ROUTE);
if (!rtnl)
return NULL;
bufsize = NLMSG_LENGTH(sizeof(struct ifinfomsg)) +
RTA_SPACE(ifname_len) + RTA_SPACE(0) +
RTA_SPACE(nlmon_type_len);
rtmmsg = l_malloc(bufsize);
memset(rtmmsg, 0, bufsize);
rtmmsg->ifi_family = AF_UNSPEC;
rtmmsg->ifi_change = ~0;
rta_buf = rtmmsg + 1;
if (ifname)
rta_buf += rta_add(rta_buf, IFLA_IFNAME, ifname_len, ifname);
linkinfo_rta = rta_buf;
rta_buf += rta_add(rta_buf, IFLA_LINKINFO, 0, NULL);
rta_buf += rta_add(rta_buf, IFLA_INFO_KIND, nlmon_type_len, NLMON_TYPE);
linkinfo_rta->rta_len = rta_buf - (void *) linkinfo_rta;
memset(&ifi, 0, sizeof(ifi));
ifi.ifi_family = AF_UNSPEC;
ifi.ifi_change = ~0;
switch (rtm_msg_type) {
case RTM_NEWLINK:
rtmmsg->ifi_flags = IFF_UP | IFF_ALLMULTI | IFF_NOARP;
l_netlink_send(rtnl, RTM_NEWLINK, NLM_F_CREATE|NLM_F_EXCL,
rtmmsg, rta_buf - (void *) rtmmsg, callback,
user_data, destroy);
ifi.ifi_flags = IFF_UP | IFF_ALLMULTI | IFF_NOARP;
flags = NLM_F_CREATE | NLM_F_EXCL;
break;
case RTM_DELLINK:
rta_buf += rta_add(rta_buf, IFLA_IFNAME, ifname_len, ifname);
l_netlink_send(rtnl, RTM_DELLINK, 0, rtmmsg,
rta_buf - (void *)rtmmsg, callback, user_data,
destroy);
break;
case RTM_GETLINK:
l_netlink_send(rtnl, RTM_GETLINK, NLM_F_DUMP, rtmmsg,
rta_buf - (void *)rtmmsg, callback, user_data,
destroy);
break;
default:
l_netlink_destroy(rtnl);
rtnl = NULL;
flags = NLM_F_DUMP;
break;
}
l_free(rtmmsg);
nlm = l_netlink_message_new(rtm_msg_type, flags);;
l_netlink_message_add_header(nlm, &ifi, sizeof(ifi));
if (ifname)
l_netlink_message_append(nlm, IFLA_IFNAME, ifname, ifname_len);
l_netlink_message_enter_nested(nlm, IFLA_LINKINFO);
l_netlink_message_append(nlm, IFLA_INFO_KIND,
NLMON_TYPE, nlmon_type_len);
l_netlink_message_leave_nested(nlm);
l_netlink_send(rtnl, nlm, callback, user_data, destroy);
return rtnl;
}
@ -689,7 +578,7 @@ static int analyze_pcap(const char *pathname)
printf("\n");
printf(" Number of packets: %lu\n", pkt_count);
printf(" Short packets: %lu\n", pkt_short);
printf(" Tuncated packets: %lu\n", pkt_trunc);
printf(" Truncated packets: %lu\n", pkt_trunc);
printf("\n");
printf(" Ethernet packets: %lu\n", pkt_ether);
printf(" PAE packets: %lu\n", pkt_pae);
@ -829,29 +718,36 @@ static void usage(void)
"Usage:\n");
printf("\tiwmon [options]\n");
printf("Options:\n"
"\t-r, --read <file> Read netlink PCAP trace file\n"
"\t-w, --write <file> Write netlink PCAP trace file\n"
"\t-a, --analyze <file> Analyze netlink PCAP trace file\n"
"\t-i, --interface <dev> Use specified netlink monitor\n"
"\t-n, --nortnl Don't show RTNL output\n"
"\t-y, --nowiphy Don't show 'New Wiphy' output\n"
"\t-s, --noscan Don't show scan result output\n"
"\t-e, --noies Don't show IEs except SSID\n"
"\t-h, --help Show help options\n");
"\t-r, --read <file> Read netlink PCAP trace file\n"
"\t-w, --write <file> Write netlink PCAP trace file\n"
"\t-a, --analyze <file> Analyze netlink PCAP trace file\n"
"\t-i, --interface <dev> Use specified netlink monitor\n"
"\t-n, --nortnl Don't show RTNL output\n"
"\t-y, --nowiphy Don't show 'New Wiphy' output\n"
"\t-s, --noscan Don't show scan result output\n"
"\t-e, --noies Don't show IEs except SSID\n"
"\t-t, --time-format <format> Time format to display. Either\n"
"\t\t\t\t 'delta' or 'utc'.\n"
"\t-W,--pcap-count Maximum number of PCAP files\n"
"\t-C,--pcap-size Maximum size (MB) of PCAP files\n"
"\t-h, --help Show help options\n");
}
static const struct option main_options[] = {
{ "read", required_argument, NULL, 'r' },
{ "write", required_argument, NULL, 'w' },
{ "analyze", required_argument, NULL, 'a' },
{ "nl80211", required_argument, NULL, 'F' },
{ "interface", required_argument, NULL, 'i' },
{ "nortnl", no_argument, NULL, 'n' },
{ "nowiphy", no_argument, NULL, 'y' },
{ "noscan", no_argument, NULL, 's' },
{ "noies", no_argument, NULL, 'e' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ "read", required_argument, NULL, 'r' },
{ "write", required_argument, NULL, 'w' },
{ "analyze", required_argument, NULL, 'a' },
{ "nl80211", required_argument, NULL, 'F' },
{ "interface", required_argument, NULL, 'i' },
{ "nortnl", no_argument, NULL, 'n' },
{ "nowiphy", no_argument, NULL, 'y' },
{ "noscan", no_argument, NULL, 's' },
{ "noies", no_argument, NULL, 'e' },
{ "time-format", required_argument, NULL, 't' },
{ "pcap-count", required_argument, NULL, 'W' },
{ "pcap-size", required_argument, NULL, 'C' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
};
@ -865,7 +761,7 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
opt = getopt_long(argc, argv, "r:w:a:i:nvhyse",
opt = getopt_long(argc, argv, "r:w:a:i:t:W:C:nvhyse",
main_options, NULL);
if (opt < 0)
break;
@ -895,6 +791,35 @@ int main(int argc, char *argv[])
break;
case 'e':
config.noies = true;
break;
case 't':
if (!strcmp(optarg, "delta"))
config.time_format = TIME_FORMAT_DELTA;
else if (!strcmp(optarg, "utc"))
config.time_format = TIME_FORMAT_UTC;
else {
printf("Invalid time format '%s'", optarg);
return EXIT_FAILURE;
}
break;
case 'W':
if (l_safe_atou32(optarg,
&config.pcap_file_count) < 0 ||
config.pcap_file_count == 0) {
printf("Invalid file count '%s'\n", optarg);
return EXIT_FAILURE;
}
break;
case 'C':
if (l_safe_atou32(optarg,
&config.pcap_file_size) < 0 ||
config.pcap_file_size == 0) {
printf("Invalid file size '%s'\n", optarg);
return EXIT_FAILURE;
}
break;
case 'v':
printf("%s\n", VERSION);
@ -957,7 +882,7 @@ int main(int argc, char *argv[])
l_io_destroy(monitor_interface.io);
l_netlink_destroy(monitor_interface.rtnl);
l_netlink_destroy(monitor_interface.genl);
l_genl_unref(monitor_interface.genl);
l_free(monitor_interface.ifname);
nlmon_close(nlmon);

View File

@ -29,6 +29,7 @@
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
@ -37,16 +38,26 @@
#include <linux/if.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/if_link.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
#include <linux/rtnetlink.h>
#include <linux/filter.h>
#include <linux/limits.h>
#include <ell/ell.h>
#ifndef ARPHRD_NETLINK
#define ARPHRD_NETLINK 824
#endif
#ifndef RMNET_FLAGS_INGRESS_MAP_CKSUMV5
#define RMNET_FLAGS_INGRESS_MAP_CKSUMV5 (1U << 4)
#endif
#ifndef RMNET_FLAGS_EGRESS_MAP_CKSUMV5
#define RMNET_FLAGS_EGRESS_MAP_CKSUMV5 (1U << 5)
#endif
#include "linux/nl80211.h"
#include "ell/useful.h"
@ -84,6 +95,8 @@
#define BSS_CAPABILITY_APSD (1<<11)
#define BSS_CAPABILITY_DSSS_OFDM (1<<13)
#define BYTES_PER_MB 1000000
struct nlmon *cur_nlmon;
enum msg_type {
@ -104,6 +117,12 @@ struct nlmon {
bool noscan;
bool noies;
bool read;
enum time_format time_format;
char *file_prefix;
unsigned int file_idx;
unsigned int max_files;
unsigned int max_size;
};
struct nlmon_req {
@ -176,11 +195,15 @@ static void nlmon_req_free(void *data)
}
static time_t time_offset = ((time_t) -1);
static enum time_format time_format;
static inline void update_time_offset(const struct timeval *tv)
static inline void update_time_offset(const struct timeval *tv,
enum time_format tf)
{
if (tv && time_offset == ((time_t) -1))
if (tv && time_offset == ((time_t) -1)) {
time_offset = tv->tv_sec;
time_format = tf;
}
}
#define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
@ -216,15 +239,38 @@ static void print_packet(const struct timeval *tv, char ident,
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
if (tv) {
struct tm *tm;
if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP);
if (n > 0)
ts_pos += n;
}
n = sprintf(ts_str + ts_pos, " %" PRId64 ".%06" PRId64,
switch (time_format) {
case TIME_FORMAT_DELTA:
n = sprintf(ts_str + ts_pos, " %" PRId64 ".%06" PRId64,
(int64_t)tv->tv_sec - time_offset,
(int64_t)tv->tv_usec);
break;
case TIME_FORMAT_UTC:
tm = gmtime(&tv->tv_sec);
if (!tm) {
n = sprintf(ts_str + ts_pos, "%s",
"Time error");
break;
}
n = strftime(ts_str + ts_pos, sizeof(ts_str) - ts_pos,
"%b %d %H:%M:%S", tm);
break;
default:
/* Should never happen */
printf("Unknown time format");
l_main_quit();
return;
}
if (n > 0) {
ts_pos += n;
ts_len += n;
@ -358,6 +404,7 @@ static const struct {
{ { 0x00, 0x50, 0xf2 }, "Microsoft" },
{ { 0x00, 0x90, 0x4c }, "Epigram" },
{ { 0x50, 0x6f, 0x9a }, "Wi-Fi Alliance" },
{ { 0x00, 0x18, 0x0a }, "Cisco Meraki" },
{ }
};
@ -482,7 +529,30 @@ static void print_ie_country(unsigned int level, const char *label,
return;
}
print_attr(level, "%s: %c%c%c", label, code[0], code[1], code[2]);
print_attr(level, "%s: %c%c", label, code[0], code[1]);
switch (code[2]) {
case ' ':
print_attr(level + 1,
"3rd octet: 0x%02x: All environments", code[2]);
break;
case 'O':
print_attr(level + 1,
"3rd octet: 0x%02x: Outdoor environments", code[2]);
break;
case 'I':
print_attr(level + 1,
"3rd octet: 0x%02x: Indoor environments", code[2]);
break;
case 'X':
print_attr(level + 1,
"3rd octet: 0x%02x: Non-country entity", code[2]);
break;
default:
print_attr(level + 1,
"3rd octet: 0x%02x: Annex E table", code[2]);
break;
}
while (i < size) {
if (code[i] > 200) {
@ -1649,7 +1719,7 @@ static void print_ie_vht_capabilities(unsigned int level,
[21] = "TXOP PS",
[22] = "+HTC-VHT Capable",
[23 ... 25] = "Maximum A-MPDU Length Exponent",
[26 ... 27] = "VHT Link Adapation Capable",
[26 ... 27] = "VHT Link Adaptation Capable",
[28] = "RX Antenna Pattern Consistency",
[29] = "TX Antenna Pattern Consistency",
[30 ... 31] = "Extended NSS BW Support",
@ -1846,7 +1916,7 @@ static void print_ie_interworking(unsigned int level,
size--;
ptr++;
if (!size)
if (size < 2)
return;
/*
@ -2452,6 +2522,86 @@ static void print_reduced_neighbor_report(unsigned int level, const char *label,
}
}
static void print_neighbor_report(unsigned int level, const char *label,
const void *data, uint16_t size)
{
struct ie_tlv_iter iter;
struct ie_neighbor_report_info info;
const char *phy_type_table[] = {
[0] = NULL,
[1] = NULL,
[2] = "DSSS",
[3] = NULL,
[4] = "OFDM",
[5] = "HDRSSS",
[6] = "ERP",
[7] = "HT",
[8] = "DMG",
[9] = "VHT",
[10] = "TVHT",
[11] = "S1G",
[12] = "CDMG",
[13] = "CMMG",
[14] = "HE"
};
ie_tlv_iter_init(&iter, data - 2, size + 2);
if (!ie_tlv_iter_next(&iter))
return;
if (ie_parse_neighbor_report(&iter, &info) < 0) {
print_attr(level, "Invalid Neighbor report");
return;
}
print_attr(level, "Neighbor Report for "MAC, MAC_STR(info.addr));
print_attr(level + 1, "Operating Class: %u", info.oper_class);
print_attr(level + 1, "Channel Number: %u", info.channel_num);
if (info.phy_type <= 14 && phy_type_table[info.phy_type])
print_attr(level + 1, "Phy Type: %s",
phy_type_table[info.phy_type]);
else
print_attr(level + 1, "Phy Type: Unknown (%u)", info.phy_type);
if (info.bss_transition_pref_present)
print_attr(level + 1, "BSS Transition Preference: %u",
info.bss_transition_pref);
switch (info.reachable) {
case 1:
print_attr(level + 1, "Reachability: Not Reachable");
break;
case 2:
print_attr(level + 1, "Reachability: Unknown");
break;
case 3:
print_attr(level + 1, "Reachability: Reachable");
break;
default:
break;
}
print_attr(level + 1, "BSSID Information");
if (info.security)
print_attr(level + 2, "Security bit set");
if (info.key_scope)
print_attr(level + 2, "Key scope bit set");
if (info.spectrum_mgmt)
print_attr(level + 2, "Spectrum Mgmt bit set");
if (info.qos)
print_attr(level + 2, "QoS bit set");
if (info.apsd)
print_attr(level + 2, "APSD bit set");
if (info.md)
print_attr(level + 2, "MD bit set");
if (info.ht)
print_attr(level + 2, "HT bit set");
}
static struct attr_entry ie_entry[] = {
{ IE_TYPE_SSID, "SSID",
ATTR_CUSTOM, { .function = print_ie_ssid } },
@ -2520,6 +2670,8 @@ static struct attr_entry ie_entry[] = {
ATTR_CUSTOM, { .function = print_reduced_neighbor_report } },
{ IE_TYPE_RSNX, "RSNX",
ATTR_CUSTOM, { .function = print_rsnx } },
{ IE_TYPE_NEIGHBOR_REPORT, "Neighbor Report",
ATTR_CUSTOM, { .function = print_neighbor_report } },
{ },
};
@ -4979,6 +5131,7 @@ static void print_rm_action_frame(unsigned int level, const uint8_t *body,
print_rm_request(level + 1, body + 1, body_len - 1);
break;
case 1:
case 5:
print_rm_report(level + 1, body + 1, body_len - 1);
break;
}
@ -5535,8 +5688,26 @@ static void print_cqm_event(unsigned int level, const char *label,
}
}
static void print_cqm_thresholds(unsigned int level, const char *label,
const void *data, uint16_t size)
{
const int32_t *thresholds = data;
unsigned int i;
if (size % 4) {
printf("malformed packet");
return;
}
print_attr(level, "%s:", label);
for (i = 0; i < size / 4; i++)
print_attr(level + 1, "Threshold: %d", thresholds[i]);
}
static const struct attr_entry cqm_table[] = {
{ NL80211_ATTR_CQM_RSSI_THOLD, "RSSI threshold", ATTR_U32 },
{ NL80211_ATTR_CQM_RSSI_THOLD, "RSSI thresholds", ATTR_CUSTOM,
{ .function = print_cqm_thresholds } },
{ NL80211_ATTR_CQM_RSSI_HYST, "RSSI hysteresis", ATTR_U32 },
{ NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
"RSSI threshold event", ATTR_CUSTOM,
@ -7153,8 +7324,10 @@ static void print_message(struct nlmon *nlmon, const struct timeval *tv,
if (nlmon->nowiphy && (cmd == NL80211_CMD_NEW_WIPHY))
return;
if (nlmon->noscan && ((cmd == NL80211_CMD_NEW_SCAN_RESULTS) ||
(cmd == NL80211_CMD_TRIGGER_SCAN)))
if (nlmon->noscan && L_IN_SET(cmd, NL80211_CMD_NEW_SCAN_RESULTS,
NL80211_CMD_NEW_SURVEY_RESULTS,
NL80211_CMD_TRIGGER_SCAN,
NL80211_CMD_GET_SURVEY))
return;
switch (type) {
@ -7229,6 +7402,64 @@ static bool nlmon_req_match(const void *a, const void *b)
return (req->seq == match->seq && req->pid == match->pid);
}
/*
* Ensures that PCAP names are zero padded when needed. This makes the files
* sort correctly.
*/
static void next_pcap_name(char *buf, size_t size, const char *prefix,
unsigned int idx, unsigned int max)
{
unsigned int ndigits = 1;
while (max > 9) {
max /= 10;
ndigits++;
}
snprintf(buf, size, "%s%.*u", prefix, ndigits, idx);
}
static bool check_pcap(struct nlmon *nlmon, size_t next_size)
{
char path[PATH_MAX];
if (!nlmon->pcap)
return false;
if (!nlmon->max_size)
return true;
if (pcap_get_size(nlmon->pcap) + next_size <= nlmon->max_size)
return true;
pcap_close(nlmon->pcap);
/* Exhausted the single PCAP file */
if (nlmon->max_files < 2) {
printf("Reached maximum size of PCAP, exiting\n");
nlmon->pcap = NULL;
l_main_quit();
return false;
}
next_pcap_name(path, sizeof(path), nlmon->file_prefix,
++nlmon->file_idx, nlmon->max_files);
nlmon->pcap = pcap_create(path);
if (nlmon->max_files > nlmon->file_idx)
return true;
/* Remove oldest PCAP file */
next_pcap_name(path, sizeof(path), nlmon->file_prefix,
nlmon->file_idx - nlmon->max_files, nlmon->max_files);
if (remove(path) < 0)
printf("Failed to remove old PCAP file %s\n", path);
return true;
}
static void store_packet(struct nlmon *nlmon, const struct timeval *tv,
uint16_t pkt_type,
uint16_t arphrd_type,
@ -7237,7 +7468,7 @@ static void store_packet(struct nlmon *nlmon, const struct timeval *tv,
{
uint8_t sll_hdr[16], *buf = sll_hdr;
if (!nlmon->pcap)
if (!check_pcap(nlmon, sizeof(sll_hdr) + size))
return;
memset(sll_hdr, 0, sizeof(sll_hdr));
@ -7362,6 +7593,10 @@ struct nlmon *nlmon_create(uint16_t id, const struct nlmon_config *config)
nlmon->noscan = config->noscan;
nlmon->noies = config->noies;
nlmon->read = config->read_only;
nlmon->time_format = config->time_format;
nlmon->max_files = config->pcap_file_count;
/* Command line expects MB, but use bytes internally */
nlmon->max_size = config->pcap_file_size * BYTES_PER_MB;
return nlmon;
}
@ -7496,8 +7731,51 @@ static void flags_str(const struct flag_names *table,
pos += sprintf(str + pos, "]");
}
static void print_rmnet_flags(unsigned int indent,
const char *label, uint32_t flags)
{
if (flags & RMNET_FLAGS_INGRESS_DEAGGREGATION)
print_attr(indent, "%s: %s", label, "deaggregation");
if (flags & RMNET_FLAGS_INGRESS_MAP_COMMANDS)
print_attr(indent, "%s: %s", label, "map commands");
if (flags & RMNET_FLAGS_INGRESS_MAP_CKSUMV4)
print_attr(indent, "%s: %s", label, "ingress_mapv4");
if (flags & RMNET_FLAGS_EGRESS_MAP_CKSUMV4)
print_attr(indent, "%s: %s", label, "egress_mapv4");
if (flags & RMNET_FLAGS_INGRESS_MAP_CKSUMV5)
print_attr(indent, "%s: %s", label, "ingress_mapv5");
if (flags & RMNET_FLAGS_EGRESS_MAP_CKSUMV5)
print_attr(indent, "%s: %s", label, "egress_mapv5");
}
static void print_ifla_rmnet_flags(unsigned int indent, const char *str,
const void *buf, uint16_t size)
{
struct ifla_rmnet_flags flags;
if (size != 8) {
printf("malformed packet\n");
return;
}
memcpy(&flags, buf, size);
print_attr(indent, "%s:", str);
print_rmnet_flags(indent + 1, "Flags", flags.flags);
print_rmnet_flags(indent + 1, "Mask", flags.mask);
}
static struct attr_entry link_info_data_entry[] = {
{ IFLA_RMNET_MUX_ID, "RMNet Mux Id", ATTR_U16 },
{ IFLA_RMNET_FLAGS, "RMNet Flags", ATTR_CUSTOM,
{ .function = print_ifla_rmnet_flags } },
{ },
};
static struct attr_entry link_info_entry[] = {
{ IFLA_INFO_KIND, "Kind", ATTR_STRING },
{ IFLA_INFO_DATA, "Info Data",
ATTR_NESTED, { link_info_data_entry } },
{ },
};
@ -7654,7 +7932,7 @@ static void print_rtnl_attributes(int indent, const struct attr_entry *table,
return;
for (attr = rt_attr; RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
uint16_t rta_type = attr->rta_type;
uint16_t rta_type = attr->rta_type & NLA_TYPE_MASK;
enum attr_type type = ATTR_UNSPEC;
attr_func_t function;
const struct attr_entry *nested;
@ -8155,7 +8433,10 @@ void nlmon_print_rtnl(struct nlmon *nlmon, const struct timeval *tv,
int64_t aligned_size = NLMSG_ALIGN(size);
const struct nlmsghdr *nlmsg;
update_time_offset(tv);
if (nlmon->nortnl)
return;
update_time_offset(tv, nlmon->time_format);
for (nlmsg = data; NLMSG_OK(nlmsg, aligned_size);
nlmsg = NLMSG_NEXT(nlmsg, aligned_size)) {
@ -8193,7 +8474,7 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
{
const struct nlmsghdr *nlmsg;
update_time_offset(tv);
update_time_offset(tv, nlmon->time_format);
for (nlmsg = data; NLMSG_OK(nlmsg, size);
nlmsg = NLMSG_NEXT(nlmsg, size)) {
@ -8202,7 +8483,8 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
continue;
}
if (!nlmon->read && nlmsg->nlmsg_type != nlmon->id)
if (nlmsg->nlmsg_type >= NLMSG_MIN_TYPE && !nlmon->read &&
nlmsg->nlmsg_type != nlmon->id)
continue;
nlmon_message(nlmon, tv, nlmsg);
@ -8215,7 +8497,7 @@ void nlmon_print_pae(struct nlmon *nlmon, const struct timeval *tv,
{
char extra_str[16];
update_time_offset(tv);
update_time_offset(tv, nlmon->time_format);
sprintf(extra_str, "len %u", size);
@ -8342,13 +8624,20 @@ struct nlmon *nlmon_open(uint16_t id, const char *pathname,
struct nlmon *nlmon;
struct l_io *pae_io;
struct pcap *pcap;
char path[PATH_MAX];
pae_io = open_pae();
if (!pae_io)
return NULL;
if (pathname) {
pcap = pcap_create(pathname);
if (config->pcap_file_count > 1)
next_pcap_name(path, sizeof(path), pathname,
0, config->pcap_file_count);
else
snprintf(path, sizeof(path), "%s", pathname);
pcap = pcap_create(path);
if (!pcap) {
l_io_destroy(pae_io);
return NULL;
@ -8361,6 +8650,7 @@ struct nlmon *nlmon_open(uint16_t id, const char *pathname,
nlmon->pae_io = pae_io;
nlmon->pcap = pcap;
nlmon->file_prefix = l_strdup(pathname);
l_io_set_read_handler(nlmon->pae_io, pae_receive, nlmon, NULL);
@ -8383,5 +8673,7 @@ void nlmon_close(struct nlmon *nlmon)
if (nlmon->pcap)
pcap_close(nlmon->pcap);
l_free(nlmon->file_prefix);
l_free(nlmon);
}

View File

@ -25,12 +25,22 @@
struct nlmon;
enum time_format {
TIME_FORMAT_DELTA,
TIME_FORMAT_UTC,
};
struct nlmon_config {
bool nortnl;
bool nowiphy;
bool noscan;
bool noies;
bool read_only;
enum time_format time_format;
/* File size in MB */
uint32_t pcap_file_size;
uint32_t pcap_file_count;
};
struct nlmon *nlmon_open(uint16_t id, const char *pathname,

View File

@ -60,6 +60,7 @@ struct pcap {
bool closed;
uint32_t type;
uint32_t snaplen;
size_t size;
};
struct pcap *pcap_open(const char *pathname)
@ -152,6 +153,8 @@ struct pcap *pcap_create(const char *pathname)
goto failed;
}
pcap->size += len;
return pcap;
failed:
@ -188,6 +191,11 @@ uint32_t pcap_get_snaplen(struct pcap *pcap)
return pcap->snaplen;
}
size_t pcap_get_size(struct pcap *pcap)
{
return pcap->size;
}
bool pcap_read(struct pcap *pcap, struct timeval *tv,
void *data, uint32_t size, uint32_t *len, uint32_t *real_len)
{
@ -279,5 +287,7 @@ bool pcap_write(struct pcap *pcap, const struct timeval *tv,
return false;
}
pcap->size += written;
return true;
}

View File

@ -36,6 +36,7 @@ void pcap_close(struct pcap *pcap);
uint32_t pcap_get_type(struct pcap *pcap);
uint32_t pcap_get_snaplen(struct pcap *pcap);
size_t pcap_get_size(struct pcap *pcap);
bool pcap_read(struct pcap *pcap, struct timeval *tv,
void *data, uint32_t size, uint32_t *len, uint32_t *real_len);

View File

@ -94,13 +94,13 @@ static void adhoc_sta_free(void *data)
eapol_sm_free(sta->sm);
if (sta->hs_sta)
handshake_state_free(sta->hs_sta);
handshake_state_unref(sta->hs_sta);
if (sta->sm_a)
eapol_sm_free(sta->sm_a);
if (sta->hs_auth)
handshake_state_free(sta->hs_auth);
handshake_state_unref(sta->hs_auth);
end:
l_free(sta);

View File

@ -234,7 +234,7 @@ uint32_t anqp_request(uint64_t wdev_id, const uint8_t *addr,
request->anqp_cb = cb;
request->anqp_destroy = destroy;
/*
* WPA3 Specificiation version 3, Section 9.4:
* WPA3 Specification version 3, Section 9.4:
* "A STA shall use a randomized dialog token for every new GAS
* exchange."
*/

View File

@ -131,7 +131,7 @@ char **anqp_parse_nai_realms(const unsigned char *anqp, unsigned int len)
uint16_t count;
if (len < 2)
return false;
return NULL;
count = l_get_le16(anqp);

108
src/ap.c
View File

@ -67,7 +67,7 @@ struct ap_state {
ap_stopped_func_t stopped_func;
void *user_data;
char ssid[33];
char ssid[SSID_MAX_SIZE + 1];
char passphrase[64];
uint8_t psk[32];
enum band_freq band;
@ -109,6 +109,8 @@ struct ap_state {
struct l_timeout *rekey_timeout;
unsigned int rekey_time;
uint32_t pre_scan_cmd_id;
bool started : 1;
bool gtk_set : 1;
bool netconfig_set_addr4 : 1;
@ -153,7 +155,7 @@ struct ap_wsc_pbc_probe_record {
};
struct ap_network {
char ssid[33];
char ssid[SSID_MAX_SIZE + 1];
int16_t signal;
enum security security;
};
@ -181,7 +183,7 @@ static int network_signal_compare(const void *a, const void *b, void *user)
static struct ap_network *ap_network_find(struct ap_state *ap,
struct scan_bss *bss)
{
char ssid[33];
char ssid[SSID_MAX_SIZE + 1];
memcpy(ssid, bss->ssid, bss->ssid_len);
ssid[bss->ssid_len] = '\0';
@ -230,7 +232,7 @@ static void ap_stop_handshake(struct sta_state *sta)
}
if (sta->hs) {
handshake_state_free(sta->hs);
handshake_state_unref(sta->hs);
sta->hs = NULL;
}
@ -354,6 +356,12 @@ static void ap_reset(struct ap_state *ap)
l_timeout_remove(ap->rekey_timeout);
ap->rekey_timeout = NULL;
}
if (ap->pre_scan_cmd_id) {
scan_cancel(netdev_get_wdev_id(ap->netdev),
ap->pre_scan_cmd_id);
ap->pre_scan_cmd_id = 0;
}
}
static bool ap_event_done(struct ap_state *ap, bool prev_in_event)
@ -3629,7 +3637,7 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
return -ENOMSG;
len = strlen(strval);
if (len < 1 || len > 32) {
if (len < 1 || len > SSID_MAX_SIZE) {
l_error("AP SSID length outside the [1, 32] range");
return -EINVAL;
}
@ -3852,6 +3860,70 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
return 0;
}
static void ap_pre_scan_trigger(int err, void *user_data)
{
struct ap_state *ap = user_data;
if (err < 0) {
l_error("AP pre-scan failed: %i", err);
ap_start_failed(ap, err);
return;
}
}
static bool ap_check_channel(struct ap_state *ap)
{
const struct band_freq_attrs *freq_attr;
freq_attr = wiphy_get_frequency_info(netdev_get_wiphy(ap->netdev),
band_channel_to_freq(ap->channel, ap->band));
if (L_WARN_ON(!freq_attr))
return false;
/* Check if disabled/no-IR */
if (freq_attr->disabled || freq_attr->no_ir)
return false;
return true;
}
static bool ap_pre_scan_notify(int err, struct l_queue *bss_list,
const struct scan_freq_set *freqs,
void *user_data)
{
struct ap_state *ap = user_data;
if (!ap_check_channel(ap)) {
l_error("Unable to channel %u even after pre-scan",
ap->channel);
goto error;
}
if (ap_start_send(ap))
return false;
error:
ap_start_failed(ap, -ENOTSUP);
return false;
}
static void ap_pre_scan_destroy(void *user_data)
{
struct ap_state *ap = user_data;
ap->pre_scan_cmd_id = 0;
}
static bool ap_pre_scan(struct ap_state *ap)
{
ap->pre_scan_cmd_id = scan_passive(netdev_get_wdev_id(ap->netdev),
NULL, ap_pre_scan_trigger,
ap_pre_scan_notify,
ap, ap_pre_scan_destroy);
return ap->pre_scan_cmd_id != 0;
}
/*
* Start a simple independent WPA2 AP on given netdev.
*
@ -3914,34 +3986,34 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST << 4),
NULL, 0, ap_assoc_req_cb, ap, NULL))
NULL, 0, false, ap_assoc_req_cb, ap, NULL))
goto error;
if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST << 4),
NULL, 0, ap_reassoc_req_cb, ap, NULL))
NULL, 0, false, ap_reassoc_req_cb, ap, NULL))
goto error;
if (!wiphy_supports_probe_resp_offload(wiphy)) {
if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST << 4),
NULL, 0, ap_probe_req_cb, ap, NULL))
NULL, 0, false, ap_probe_req_cb, ap, NULL))
goto error;
}
if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION << 4),
NULL, 0, ap_disassoc_cb, ap, NULL))
NULL, 0, false, ap_disassoc_cb, ap, NULL))
goto error;
if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION << 4),
NULL, 0, ap_auth_cb, ap, NULL))
NULL, 0, false, ap_auth_cb, ap, NULL))
goto error;
if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_DEAUTHENTICATION << 4),
NULL, 0, ap_deauth_cb, ap, NULL))
NULL, 0, false, ap_deauth_cb, ap, NULL))
goto error;
ap->mlme_watch = l_genl_family_register(ap->nl80211, "mlme",
@ -3962,6 +4034,20 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
return ap;
}
if (!ap_check_channel(ap)) {
l_debug("Channel %u is disabled/no-IR, pre-scanning",
ap->channel);
if (ap_pre_scan(ap)) {
if (err_out)
*err_out = 0;
return ap;
}
goto error;
}
if (ap_start_send(ap)) {
if (err_out)
*err_out = 0;

View File

@ -79,7 +79,7 @@ struct ap_ops {
void *user_data);
/*
* If not null, writes extra IEs to be added to the outgoing frame of
* given type and, if it's not a beacon frame, in reponse to a given
* given type and, if it's not a beacon frame, in response to a given
* client frame. May also react to the extra IEs in that frame.
* Returns the number of bytes written which must be less than or
* equal to the number returned by .get_extra_ies_len when called

View File

@ -54,7 +54,7 @@ void __iwd_backtrace_print(unsigned int offset)
int pathlen;
pid_t pid;
if (program_exec == NULL)
if (!program_exec)
return;
pathlen = strlen(program_path);
@ -186,7 +186,7 @@ void __iwd_backtrace_init(void)
}
}
if (program_exec == NULL)
if (!program_exec)
return;
program_path = getcwd(cwd, sizeof(cwd));

View File

@ -678,7 +678,7 @@ int band_estimate_he_rx_rate(const struct band *band, const uint8_t *hec,
}
if (!rate)
return -EBADMSG;
return -ENETUNREACH;
*out_data_rate = rate;
@ -896,7 +896,7 @@ static const struct operating_class_info e4_operating_classes[] = {
},
{
.operating_class = 136,
.starting_frequency = 5950,
.starting_frequency = 5925,
.channel_spacing = 20,
.center_frequencies = { 2 },
}
@ -1352,6 +1352,10 @@ check_e4:
const struct operating_class_info *info =
&e4_operating_classes[i];
if (band != band_oper_class_to_band(NULL,
info->operating_class))
continue;
if (e4_has_frequency(info, freq) == 0 ||
e4_has_ccfi(info, freq) == 0) {
if (out_band)
@ -1426,7 +1430,7 @@ static const char *const oper_class_eu_codes[] = {
"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK"
"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", "GB"
};
/* Annex E, table E-1 */
@ -1485,15 +1489,48 @@ static const uint8_t oper_class_cn_to_global[] = {
/* 128 - 130 is a 1 to 1 mapping */
};
enum band_freq band_oper_class_to_band(const uint8_t *country,
uint8_t oper_class)
/*
* Annex C describes the country string encoding.
*
* If it is a country, the first two octets of this string is the two character
* country code as described in document ISO 3166-1. The third octet is one of
* the following:
* 1. an ASCII space character, if the regulations under which the station is
* operating encompass all environments for the current frequency band in
* the country,
* 2. an ASCII 'O' character, if the regulations under which the station is
* operating are for an outdoor environment only, or
* 3. an ASCII 'I' character, if the regulations under which the station is
* operating are for an indoor environment only.
* 4. an ASCII 'X' character, if the station is operating under a noncountry
* entity. The first two octets of the noncountry entity is two ASCII 'XX'
* characters.
* 5. the hexadecimal representation of the Operating Class table number
* currently in use, from the set of tables defined in Annex E, e.g.,
* Table E-1 is represented as x'01'.
*/
static enum band_freq oper_class_to_band(const uint8_t *country,
uint8_t oper_class,
bool ignore_country3)
{
unsigned int i;
int table = 0;
if (country && country[2] >= 1 && country[2] <= 5)
/*
* If a country is set, and the 3rd byte maps to some E-* table in the
* spec use that (case 5). Only caveat here is some APs erroneously set
* this 3rd byte. To work around we can fall back to case 1, where only
* the first two characters are used to lookup the table.
*/
if (!ignore_country3 && country && country[2] >= 1 && country[2] <= 5)
table = country[2];
else if (country) {
/*
* Assuming case 1, although its unlikely you would handle
* cases 2 (O) or 3 (I) any differently. Case 4 (X) is unlikely
* and we really wouldn't have enough information to correctly
* determine the band in some obscure non-country domain.
*/
for (i = 0; i < L_ARRAY_SIZE(oper_class_us_codes); i++)
if (!memcmp(oper_class_us_codes[i], country, 2)) {
/* Use table E-1 */
@ -1542,6 +1579,25 @@ enum band_freq band_oper_class_to_band(const uint8_t *country,
return 0;
}
enum band_freq band_oper_class_to_band(const uint8_t *country,
uint8_t oper_class)
{
enum band_freq band = oper_class_to_band(country, oper_class, false);
if (!band) {
/* Fallback with no country string won't change anything */
if (!country)
return 0;
l_warn("Failed to find band with country string '%c%c %u' and "
"oper class %u, trying fallback",
country[0], country[1], country[2], oper_class);
return oper_class_to_band(country, oper_class, true);
}
return band;
}
const char *band_chandef_width_to_string(enum band_chandef_width width)
{
switch (width) {

View File

@ -45,22 +45,42 @@
static uint64_t blacklist_multiplier;
static uint64_t blacklist_initial_timeout;
static uint64_t blacklist_ap_busy_initial_timeout;
static uint64_t blacklist_max_timeout;
struct blacklist_entry {
uint8_t addr[6];
uint64_t added_time;
uint64_t expire_time;
enum blacklist_reason reason;
};
struct blacklist_search {
const uint8_t *addr;
enum blacklist_reason reason;
};
static struct l_queue *blacklist;
static uint64_t get_reason_timeout(enum blacklist_reason reason)
{
switch (reason) {
case BLACKLIST_REASON_CONNECT_FAILED:
return blacklist_initial_timeout;
case BLACKLIST_REASON_AP_BUSY:
return blacklist_ap_busy_initial_timeout;
default:
l_warn("Unhandled blacklist reason: %u", reason);
return 0;
}
}
static bool check_if_expired(void *data, void *user_data)
{
struct blacklist_entry *entry = data;
uint64_t now = l_get_u64(user_data);
if (l_time_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_free(entry);
return true;
@ -87,17 +107,53 @@ static bool match_addr(const void *a, const void *b)
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;
uint64_t timeout;
blacklist_prune();
timeout = get_reason_timeout(reason);
if (!timeout)
return;
entry = l_queue_find(blacklist, match_addr, addr);
if (entry) {
uint64_t offset = l_time_diff(entry->added_time,
entry->expire_time);
uint64_t offset;
if (reason < entry->reason) {
l_debug("Promoting "MAC" blacklist to reason %u",
MAC_STR(addr), reason);
/* Reset this to the new timeout and reason */
entry->reason = reason;
entry->added_time = l_time_now();
entry->expire_time = l_time_offset(entry->added_time,
timeout);
return;
} else if (reason > entry->reason) {
l_debug("Ignoring blacklist extension of "MAC", "
"current blacklist status is more severe!",
MAC_STR(addr));
return;
}
offset = l_time_diff(entry->added_time, entry->expire_time);
offset *= blacklist_multiplier;
@ -112,40 +168,36 @@ void blacklist_add_bss(const uint8_t *addr)
entry = l_new(struct blacklist_entry, 1);
entry->added_time = l_time_now();
entry->expire_time = l_time_offset(entry->added_time,
blacklist_initial_timeout);
entry->expire_time = l_time_offset(entry->added_time, timeout);
entry->reason = reason;
memcpy(entry->addr, addr, 6);
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;
uint64_t time_now;
struct blacklist_entry *entry;
struct blacklist_search search = {
.addr = addr,
.reason = reason
};
blacklist_prune();
entry = l_queue_find(blacklist, match_addr, addr);
if (!entry)
return false;
time_now = l_time_now();
ret = l_time_after(time_now, entry->expire_time) ? false : true;
return ret;
return l_queue_find(blacklist, match_addr_and_reason, &search) != NULL;
}
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_search search = {
.addr = addr,
.reason = reason
};
blacklist_prune();
entry = l_queue_remove_if(blacklist, match_addr, addr);
entry = l_queue_remove_if(blacklist, match_addr_and_reason, &search);
if (!entry)
return;
@ -162,19 +214,47 @@ static int blacklist_init(void)
blacklist_initial_timeout = BLACKLIST_DEFAULT_TIMEOUT;
/* For easier user configuration the timeout values are in seconds */
blacklist_initial_timeout *= 1000000;
blacklist_initial_timeout *= L_USEC_PER_SEC;
if (!l_settings_get_uint64(config, "Blacklist",
"InitialRoamRequestedTimeout",
&blacklist_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",
"Multiplier",
&blacklist_multiplier))
blacklist_multiplier = BLACKLIST_DEFAULT_MULTIPLIER;
if (blacklist_multiplier == 0) {
l_warn("[Blacklist].Multiplier cannot be zero, setting to 1");
blacklist_multiplier = 1;
}
if (!l_settings_get_uint64(config, "Blacklist",
"MaximumTimeout",
&blacklist_max_timeout))
blacklist_max_timeout = BLACKLIST_DEFAULT_MAX_TIMEOUT;
blacklist_max_timeout *= 1000000;
blacklist_max_timeout *= L_USEC_PER_SEC;
if (blacklist_initial_timeout > blacklist_max_timeout)
l_warn("[Blacklist].InitialTimeout exceeded "
"[Blacklist].MaximumTimeout!");
if (!blacklist_initial_timeout)
l_debug("initial timeout was zero, blacklist will be disabled");
blacklist = l_queue_new();

View File

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

View File

@ -35,6 +35,7 @@
#include "ell/useful.h"
#include "src/missing.h"
#include "src/defs.h"
#include "src/crypto.h"
#define ARC4_MIN_KEY_SIZE 1
@ -567,7 +568,7 @@ int crypto_psk_from_passphrase(const char *passphrase,
if (!crypto_passphrase_is_valid(passphrase))
return -ERANGE;
if (ssid_len == 0 || ssid_len > 32)
if (ssid_len == 0 || ssid_len > SSID_MAX_SIZE)
return -ERANGE;
result = l_cert_pkcs5_pbkdf2(L_CHECKSUM_SHA1, passphrase,
@ -1211,7 +1212,7 @@ struct l_ecc_point *crypto_derive_sae_pwe_from_pt_ecc(const uint8_t *mac1,
struct l_ecc_point *pwe;
if (!pt || !curve)
return false;
return NULL;
hash = crypto_sae_hash_from_ecc_prime_len(CRYPTO_SAE_HASH_TO_ELEMENT,
l_ecc_curve_get_scalar_bytes(curve));

View File

@ -134,6 +134,12 @@ struct l_dbus_message *dbus_error_not_hidden(struct l_dbus_message *msg)
"Not hidden");
}
struct l_dbus_message *dbus_error_permission_denied(struct l_dbus_message *msg)
{
return l_dbus_message_new_error(msg, IWD_SERVICE ".PermissionDenied",
"Permission Denied");
}
struct l_dbus_message *dbus_error_from_errno(int err,
struct l_dbus_message *msg)
{

Some files were not shown because too many files have changed in this diff Show More