3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2025-04-21 12:17:50 +02:00

Compare commits

...

102 Commits
2.22 ... master

Author SHA1 Message Date
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
119 changed files with 2976 additions and 514 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

1
.gitignore vendored
View File

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

View File

@ -1,3 +1,35 @@
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: ver 2.22:
Fix issue with handling the Affinities property. Fix issue with handling the Affinities property.
Fix issue with handling ConnectedAccessPoint signal when roaming. Fix issue with handling ConnectedAccessPoint signal when roaming.

View File

@ -65,12 +65,15 @@ ell_headers = ell/util.h \
ell/cleanup.h \ ell/cleanup.h \
ell/netconfig.h \ ell/netconfig.h \
ell/sysctl.h \ ell/sysctl.h \
ell/notifylist.h ell/notifylist.h \
ell/minheap.h
ell_sources = ell/private.h \ ell_sources = ell/private.h \
ell/missing.h \ ell/missing.h \
ell/util.c \ ell/util.c \
ell/test-private.h \
ell/test.c \ ell/test.c \
ell/test-dbus.c \
ell/strv.c \ ell/strv.c \
ell/utf8.c \ ell/utf8.c \
ell/queue.c \ ell/queue.c \
@ -147,7 +150,8 @@ ell_sources = ell/private.h \
ell/acd.c \ ell/acd.c \
ell/netconfig.c \ ell/netconfig.c \
ell/sysctl.c \ ell/sysctl.c \
ell/notifylist.c ell/notifylist.c \
ell/minheap.c
ell_shared = ell/useful.h ell/asn1-private.h ell_shared = ell/useful.h ell/asn1-private.h
@ -269,6 +273,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h \
src/json.h src/json.c \ src/json.h src/json.c \
src/dpp.c \ src/dpp.c \
src/udev.c \ src/udev.c \
src/pmksa.h src/pmksa.c \
$(eap_sources) \ $(eap_sources) \
$(builtin_sources) $(builtin_sources)
@ -435,7 +440,8 @@ unit_tests += unit/test-cmac-aes \
unit/test-ie unit/test-util unit/test-ssid-security \ unit/test-ie unit/test-util unit/test-ssid-security \
unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \ unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \
unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \ unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \
unit/test-dpp unit/test-json unit/test-nl80211util unit/test-dpp unit/test-json unit/test-nl80211util \
unit/test-pmksa unit/test-storage
endif endif
if CLIENT if CLIENT
@ -454,6 +460,7 @@ unit_test_eap_sim_SOURCES = unit/test-eap-sim.c \
src/eapol.h src/eapol.c \ src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \ src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \ src/handshake.h src/handshake.c \
src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \ src/eap.h src/eap.c src/eap-private.h \
src/util.h src/util.c \ src/util.h src/util.c \
src/simauth.h src/simauth.c \ src/simauth.h src/simauth.c \
@ -513,6 +520,7 @@ unit_test_eapol_SOURCES = unit/test-eapol.c \
src/eapol.h src/eapol.c \ src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \ src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \ src/handshake.h src/handshake.c \
src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \ src/eap.h src/eap.c src/eap-private.h \
src/eap-tls.c src/eap-ttls.c \ src/eap-tls.c src/eap-ttls.c \
src/eap-md5.c src/util.c \ src/eap-md5.c src/util.c \
@ -543,6 +551,7 @@ unit_test_wsc_SOURCES = unit/test-wsc.c src/wscutil.h src/wscutil.c \
src/eapol.h src/eapol.c \ src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \ src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \ src/handshake.h src/handshake.c \
src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \ src/eap.h src/eap.c src/eap-private.h \
src/util.h src/util.c \ src/util.h src/util.c \
src/erp.h src/erp.c \ src/erp.h src/erp.c \
@ -561,6 +570,7 @@ unit_test_sae_SOURCES = unit/test-sae.c \
src/crypto.h src/crypto.c \ src/crypto.h src/crypto.c \
src/ie.h src/ie.c \ src/ie.h src/ie.c \
src/handshake.h src/handshake.c \ src/handshake.h src/handshake.c \
src/pmksa.h src/pmksa.c \
src/erp.h src/erp.c \ src/erp.h src/erp.c \
src/band.h src/band.c \ src/band.h src/band.c \
src/util.h src/util.c \ src/util.h src/util.c \
@ -591,6 +601,15 @@ unit_test_nl80211util_SOURCES = unit/test-nl80211util.c \
src/ie.h src/ie.c \ src/ie.h src/ie.c \
src/util.h src/util.c src/util.h src/util.c
unit_test_nl80211util_LDADD = $(ell_ldadd) unit_test_nl80211util_LDADD = $(ell_ldadd)
unit_test_pmksa_SOURCES = unit/test-pmksa.c src/pmksa.c src/pmksa.h \
src/module.h src/util.h
unit_test_pmksa_LDADD = $(ell_ldadd)
unit_test_storage_SOURCES = unit/test-storage.c src/storage.c src/storage.h \
src/crypto.c src/crypto.h \
src/common.c src/common.h
unit_test_storage_LDADD = $(ell_ldadd)
endif endif
if CLIENT if CLIENT
@ -606,6 +625,9 @@ unit_test_client_SOURCES = unit/test-client.c \
unit_test_client_LDADD = $(ell_ldadd) $(client_ldadd) unit_test_client_LDADD = $(ell_ldadd) $(client_ldadd)
endif endif
LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \
$(top_srcdir)/build-aux/tap-driver.sh
TESTS = $(unit_tests) TESTS = $(unit_tests)
EXTRA_DIST = src/genbuiltin src/iwd.service.in src/net.connman.iwd.service \ EXTRA_DIST = src/genbuiltin src/iwd.service.in src/net.connman.iwd.service \

4
TODO
View File

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

View File

@ -11,52 +11,58 @@ from iwd import NetworkType
from hostapd import HostapdCLI from hostapd import HostapdCLI
class Test(unittest.TestCase): class Test(unittest.TestCase):
def initial_connection(self):
def validate(self, expect_roam=True): ordered_network = self.device.get_ordered_network('TestAPRoam')
wd = IWD()
devices = wd.list_devices(1)
device = devices[0]
ordered_network = device.get_ordered_network('TestAPRoam')
self.assertEqual(ordered_network.type, NetworkType.psk) self.assertEqual(ordered_network.type, NetworkType.psk)
condition = 'not obj.connected' condition = 'not obj.connected'
wd.wait_for_object_condition(ordered_network.network_object, condition) self.wd.wait_for_object_condition(ordered_network.network_object, condition)
device.connect_bssid(self.bss_hostapd[0].bssid) self.device.connect_bssid(self.bss_hostapd[0].bssid)
condition = 'obj.state == DeviceState.connected' condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_condition(device, condition) self.wd.wait_for_object_condition(self.device, condition)
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED') self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED')
self.assertFalse(self.bss_hostapd[1].list_sta()) self.assertFalse(self.bss_hostapd[1].list_sta())
self.bss_hostapd[0].send_bss_transition(device.address, def validate_roam(self, from_bss, to_bss, expect_roam=True):
[(self.bss_hostapd[1].bssid, '8f0000005102060603000000')], from_bss.send_bss_transition(self.device.address,
self.neighbor_list,
disassoc_imminent=expect_roam) disassoc_imminent=expect_roam)
if expect_roam: if expect_roam:
from_condition = 'obj.state == DeviceState.roaming' from_condition = 'obj.state == DeviceState.roaming'
to_condition = 'obj.state == DeviceState.connected' to_condition = 'obj.state == DeviceState.connected'
wd.wait_for_object_change(device, from_condition, to_condition) self.wd.wait_for_object_change(self.device, from_condition, to_condition)
self.bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % device.address) to_bss.wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
else: else:
device.wait_for_event("no-roam-candidates") self.device.wait_for_event("no-roam-candidates")
device.disconnect()
condition = 'not obj.connected'
wd.wait_for_object_condition(ordered_network.network_object, condition)
def test_disassoc_imminent(self): def test_disassoc_imminent(self):
self.validate(expect_roam=True) self.initial_connection()
self.validate_roam(self.bss_hostapd[0], self.bss_hostapd[1])
def test_no_candidates(self): def test_no_candidates(self):
self.validate(expect_roam=False) self.initial_connection()
# We now have BSS0 roam blacklisted
self.validate_roam(self.bss_hostapd[0], self.bss_hostapd[1])
# Try and trigger another roam back, which shouldn't happen since now
# both BSS's are roam blacklisted
self.validate_roam(self.bss_hostapd[1], self.bss_hostapd[0], expect_roam=False)
def setUp(self):
self.wd = IWD(True)
devices = self.wd.list_devices(1)
self.device = devices[0]
def tearDown(self):
self.wd = None
self.device = None
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@ -65,6 +71,10 @@ class Test(unittest.TestCase):
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'), cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
HostapdCLI(config='ssid2.conf'), HostapdCLI(config='ssid2.conf'),
HostapdCLI(config='ssid3.conf') ] HostapdCLI(config='ssid3.conf') ]
cls.neighbor_list = [
(cls.bss_hostapd[0].bssid, "8f0000005101060603000000"),
(cls.bss_hostapd[1].bssid, "8f0000005102060603000000"),
]
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):

View File

@ -1,5 +1,7 @@
[SETUP] [SETUP]
num_radios=4 num_radios=4
hwsim_medium=true
start_iwd=false
[HOSTAPD] [HOSTAPD]
rad0=ssid1.conf rad0=ssid1.conf

View File

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

View File

@ -0,0 +1,183 @@
#!/usr/bin/python3
import unittest
import sys
sys.path.append('../util')
import iwd
from iwd import IWD, IWD_CONFIG_DIR
from iwd import NetworkType
from hostapd import HostapdCLI
from hwsim import Hwsim
class Test(unittest.TestCase):
def validate_connected(self, hostapd):
ordered_network = self.device.get_ordered_network('TestAPRoam')
self.assertEqual(ordered_network.type, NetworkType.psk)
condition = 'not obj.connected'
self.wd.wait_for_object_condition(ordered_network.network_object, condition)
self.device.connect_bssid(hostapd.bssid)
condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_condition(self.device, condition)
hostapd.wait_for_event('AP-STA-CONNECTED')
def validate_ap_roamed(self, from_hostapd, to_hostapd):
from_hostapd.send_bss_transition(
self.device.address, self.neighbor_list, disassoc_imminent=True
)
from_condition = 'obj.state == DeviceState.roaming'
to_condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
to_hostapd.wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
self.device.wait_for_event("ap-roam-blacklist-added")
def test_roam_to_optimal_candidates(self):
# In this test IWD will naturally transition down the list after each
# BSS gets roam blacklisted. All BSS's are above the RSSI thresholds.
self.rule_ssid1.signal = -5000
self.rule_ssid2.signal = -6500
self.rule_ssid3.signal = -6900
# Connect to BSS0
self.validate_connected(self.bss_hostapd[0])
# AP directed roam to BSS1
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
# AP directed roam to BSS2
self.validate_ap_roamed(self.bss_hostapd[1], self.bss_hostapd[2])
def test_avoiding_under_threshold_bss(self):
# In this test IWD will blacklist BSS0, then roam the BSS1. BSS1 will
# then tell IWD to roam, but it should go back to BSS0 since the only
# non-blacklisted BSS is under the roam threshold.
self.rule_ssid1.signal = -5000
self.rule_ssid2.signal = -6500
self.rule_ssid3.signal = -7300
# Connect to BSS0
self.validate_connected(self.bss_hostapd[0])
# AP directed roam to BSS1
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
# AP directed roam, but IWD should choose BSS0 since BSS2 is -73dB
self.validate_ap_roamed(self.bss_hostapd[1], self.bss_hostapd[0])
def test_connect_to_roam_blacklisted_bss(self):
# In this test a BSS will be roam blacklisted, but all other options are
# below the RSSI threshold so IWD should roam back to the blacklisted
# BSS.
self.rule_ssid1.signal = -5000
self.rule_ssid2.signal = -8000
self.rule_ssid3.signal = -8500
# Connect to BSS0
self.validate_connected(self.bss_hostapd[0])
# AP directed roam, should connect to BSS1 as its the next best
self.validate_ap_roamed(self.bss_hostapd[0], self.bss_hostapd[1])
# Connected to BSS1, but the signal is bad, so IWD should try to roam
# again. BSS0 is still blacklisted, but its the only reasonable option
# since both BSS1 and BSS2 are below the set RSSI threshold (-72dB)
from_condition = 'obj.state == DeviceState.roaming'
to_condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
# IWD should have connected to BSS0, even though its roam blacklisted
self.bss_hostapd[0].wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
def test_blacklist_during_roam_scan(self):
# Tests that an AP roam request mid-roam results in the AP still being
# blacklisted even though the request itself doesn't directly trigger
# a roam.
self.rule_ssid1.signal = -7300
self.rule_ssid2.signal = -7500
self.rule_ssid3.signal = -8500
# Connect to BSS0 under the roam threshold so IWD will immediately try
# roaming elsewhere
self.validate_connected(self.bss_hostapd[0])
self.device.wait_for_event("roam-scan-triggered")
self.bss_hostapd[0].send_bss_transition(
self.device.address, self.neighbor_list, disassoc_imminent=True
)
self.device.wait_for_event("ap-roam-blacklist-added")
# BSS0 should have gotten blacklisted even though IWD was mid-roam,
# causing IWD to choose BSS1 when it gets is results.
from_condition = 'obj.state == DeviceState.roaming'
to_condition = 'obj.state == DeviceState.connected'
self.wd.wait_for_object_change(self.device, from_condition, to_condition)
self.bss_hostapd[1].wait_for_event('AP-STA-CONNECTED %s' % self.device.address)
def setUp(self):
self.wd = IWD(True)
devices = self.wd.list_devices(1)
self.device = devices[0]
def tearDown(self):
self.wd = None
self.device = None
@classmethod
def setUpClass(cls):
IWD.copy_to_storage("main.conf.roaming", IWD_CONFIG_DIR, "main.conf")
IWD.copy_to_storage('TestAPRoam.psk')
hwsim = Hwsim()
cls.bss_hostapd = [ HostapdCLI(config='ssid1.conf'),
HostapdCLI(config='ssid2.conf'),
HostapdCLI(config='ssid3.conf') ]
HostapdCLI.group_neighbors(*cls.bss_hostapd)
rad0 = hwsim.get_radio('rad0')
rad1 = hwsim.get_radio('rad1')
rad2 = hwsim.get_radio('rad2')
cls.neighbor_list = [
(cls.bss_hostapd[0].bssid, "8f0000005101060603000000"),
(cls.bss_hostapd[1].bssid, "8f0000005102060603000000"),
(cls.bss_hostapd[2].bssid, "8f0000005103060603000000"),
]
cls.rule_ssid1 = hwsim.rules.create()
cls.rule_ssid1.source = rad0.addresses[0]
cls.rule_ssid1.bidirectional = True
cls.rule_ssid1.enabled = True
cls.rule_ssid2 = hwsim.rules.create()
cls.rule_ssid2.source = rad1.addresses[0]
cls.rule_ssid2.bidirectional = True
cls.rule_ssid2.enabled = True
cls.rule_ssid3 = hwsim.rules.create()
cls.rule_ssid3.source = rad2.addresses[0]
cls.rule_ssid3.bidirectional = True
cls.rule_ssid3.enabled = True
@classmethod
def tearDownClass(cls):
IWD.clear_storage()
if __name__ == '__main__':
unittest.main(exit=True)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

@ -0,0 +1,12 @@
hw_mode=g
channel=1
ssid=ssidSAE
wpa=2
wpa_key_mgmt=SAE
wpa_pairwise=CCMP
sae_password=secret123
sae_groups=19
ieee80211w=2
sae_pwe=0
rsn_preauth=1

View File

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

View File

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

View File

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

View File

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

View File

@ -368,3 +368,7 @@ class HostapdCLI(object):
others = [h for h in args if h != hapd] others = [h for h in args if h != hapd]
hapd._add_neighbors(*others) hapd._add_neighbors(*others)
def pmksa_flush(self):
cmd = self.cmdline + ['pmksa_flush']
ctx.start_process(cmd).wait()

View File

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

View File

@ -450,13 +450,15 @@ class Device(IWDDBusAbstract):
self._wps_manager_if = None self._wps_manager_if = None
self._station_if = None self._station_if = None
self._station_props = None self._station_props = None
self._station_debug_obj = None
self._dpp_obj = None self._dpp_obj = None
self._sc_dpp_obj = None self._sc_dpp_obj = None
self._ap_obj = None self._ap_obj = None
IWDDBusAbstract.__init__(self, *args, **kwargs) IWDDBusAbstract.__init__(self, *args, **kwargs)
self._station_debug_obj = StationDebug(object_path=self._object_path,
namespace=self._namespace)
@property @property
def _wps_manager(self): def _wps_manager(self):
if self._wps_manager_if is None: if self._wps_manager_if is None:
@ -1481,10 +1483,10 @@ class IWD(AsyncOpAbstract):
@staticmethod @staticmethod
def create_in_storage(file_name, file_content, storage_dir=IWD_STORAGE_DIR): def create_in_storage(file_name, file_content, storage_dir=IWD_STORAGE_DIR):
fo = open(storage_dir + '/' + file_name, 'w') f = open(storage_dir + '/' + file_name, 'w')
fo.write(file_content) f.write(file_content)
fo.close() f.close()
@staticmethod @staticmethod
def _ensure_storage_dir_exists(storage_dir): def _ensure_storage_dir_exists(storage_dir):

View File

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

View File

@ -186,5 +186,5 @@ void diagnostic_display(struct l_dbus_message_iter *dict,
return; return;
parse_error: parse_error:
display_error("Error parsing dignostics"); display_error("Error parsing diagnostics");
} }

View File

@ -219,7 +219,7 @@ static void network_display_inline(const char *margin, const void *data)
display("%s%s %s %s\n", margin, network->name ? network->name : "", display("%s%s %s %s\n", margin, network->name ? network->name : "",
network->type ? network->type : "", network->type ? network->type : "",
network->connected ? "connected" : "diconnected"); network->connected ? "connected" : "disconnected");
} }
static void *network_create(void) static void *network_create(void)

View File

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

View File

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

View File

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

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. like Boingo. Lots of these services also allow you to roam between networks.
The underlying authentication is standard WPA2-Enterprise but Hotspot 2.0 adds a The underlying authentication is standard WPA2-Enterprise but Hotspot 2.0 adds a
'discovery' stage to identifiying networks. This discovery is done using ANQP, 'discovery' stage to identifying networks. This discovery is done using ANQP,
which queries the network for additional information to determine if the client which queries the network for additional information to determine if the client
has the credentials to connect. has the credentials to connect.

View File

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

View File

@ -166,7 +166,7 @@ Properties string State [readonly]
object ConnectedAccessPoint [readonly, optional] object ConnectedAccessPoint [readonly, optional]
net.connman.iwd.BasicServiceSet object represeting the net.connman.iwd.BasicServiceSet object representing the
BSS the device is currently connected to or to which BSS the device is currently connected to or to which
a connection is in progress. a connection is in progress.

View File

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

View File

@ -578,7 +578,7 @@ static int analyze_pcap(const char *pathname)
printf("\n"); printf("\n");
printf(" Number of packets: %lu\n", pkt_count); printf(" Number of packets: %lu\n", pkt_count);
printf(" Short packets: %lu\n", pkt_short); printf(" Short packets: %lu\n", pkt_short);
printf(" Tuncated packets: %lu\n", pkt_trunc); printf(" Truncated packets: %lu\n", pkt_trunc);
printf("\n"); printf("\n");
printf(" Ethernet packets: %lu\n", pkt_ether); printf(" Ethernet packets: %lu\n", pkt_ether);
printf(" PAE packets: %lu\n", pkt_pae); printf(" PAE packets: %lu\n", pkt_pae);
@ -718,29 +718,36 @@ static void usage(void)
"Usage:\n"); "Usage:\n");
printf("\tiwmon [options]\n"); printf("\tiwmon [options]\n");
printf("Options:\n" printf("Options:\n"
"\t-r, --read <file> Read netlink PCAP trace file\n" "\t-r, --read <file> Read netlink PCAP trace file\n"
"\t-w, --write <file> Write 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-a, --analyze <file> Analyze netlink PCAP trace file\n"
"\t-i, --interface <dev> Use specified netlink monitor\n" "\t-i, --interface <dev> Use specified netlink monitor\n"
"\t-n, --nortnl Don't show RTNL output\n" "\t-n, --nortnl Don't show RTNL output\n"
"\t-y, --nowiphy Don't show 'New Wiphy' output\n" "\t-y, --nowiphy Don't show 'New Wiphy' output\n"
"\t-s, --noscan Don't show scan result output\n" "\t-s, --noscan Don't show scan result output\n"
"\t-e, --noies Don't show IEs except SSID\n" "\t-e, --noies Don't show IEs except SSID\n"
"\t-h, --help Show help options\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[] = { static const struct option main_options[] = {
{ "read", required_argument, NULL, 'r' }, { "read", required_argument, NULL, 'r' },
{ "write", required_argument, NULL, 'w' }, { "write", required_argument, NULL, 'w' },
{ "analyze", required_argument, NULL, 'a' }, { "analyze", required_argument, NULL, 'a' },
{ "nl80211", required_argument, NULL, 'F' }, { "nl80211", required_argument, NULL, 'F' },
{ "interface", required_argument, NULL, 'i' }, { "interface", required_argument, NULL, 'i' },
{ "nortnl", no_argument, NULL, 'n' }, { "nortnl", no_argument, NULL, 'n' },
{ "nowiphy", no_argument, NULL, 'y' }, { "nowiphy", no_argument, NULL, 'y' },
{ "noscan", no_argument, NULL, 's' }, { "noscan", no_argument, NULL, 's' },
{ "noies", no_argument, NULL, 'e' }, { "noies", no_argument, NULL, 'e' },
{ "version", no_argument, NULL, 'v' }, { "time-format", required_argument, NULL, 't' },
{ "help", no_argument, NULL, 'h' }, { "pcap-count", required_argument, NULL, 'W' },
{ "pcap-size", required_argument, NULL, 'C' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ } { }
}; };
@ -754,7 +761,7 @@ int main(int argc, char *argv[])
for (;;) { for (;;) {
int opt; int opt;
opt = getopt_long(argc, argv, "r:w:a:i:nvhyse", opt = getopt_long(argc, argv, "r:w:a:i:t:W:C:nvhyse",
main_options, NULL); main_options, NULL);
if (opt < 0) if (opt < 0)
break; break;
@ -784,6 +791,35 @@ int main(int argc, char *argv[])
break; break;
case 'e': case 'e':
config.noies = true; config.noies = true;
break;
case 't':
if (!strcmp(optarg, "delta"))
config.time_format = TIME_FORMAT_DELTA;
else if (!strcmp(optarg, "utc"))
config.time_format = TIME_FORMAT_UTC;
else {
printf("Invalid time format '%s'", optarg);
return EXIT_FAILURE;
}
break;
case 'W':
if (l_safe_atou32(optarg,
&config.pcap_file_count) < 0 ||
config.pcap_file_count == 0) {
printf("Invalid file count '%s'\n", optarg);
return EXIT_FAILURE;
}
break;
case 'C':
if (l_safe_atou32(optarg,
&config.pcap_file_size) < 0 ||
config.pcap_file_size == 0) {
printf("Invalid file size '%s'\n", optarg);
return EXIT_FAILURE;
}
break; break;
case 'v': case 'v':
printf("%s\n", VERSION); printf("%s\n", VERSION);

View File

@ -29,6 +29,7 @@
#include <errno.h> #include <errno.h>
#include <ctype.h> #include <ctype.h>
#include <unistd.h> #include <unistd.h>
#include <time.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@ -42,6 +43,7 @@
#include <linux/genetlink.h> #include <linux/genetlink.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/filter.h> #include <linux/filter.h>
#include <linux/limits.h>
#include <ell/ell.h> #include <ell/ell.h>
#ifndef ARPHRD_NETLINK #ifndef ARPHRD_NETLINK
@ -93,6 +95,8 @@
#define BSS_CAPABILITY_APSD (1<<11) #define BSS_CAPABILITY_APSD (1<<11)
#define BSS_CAPABILITY_DSSS_OFDM (1<<13) #define BSS_CAPABILITY_DSSS_OFDM (1<<13)
#define BYTES_PER_MB 1000000
struct nlmon *cur_nlmon; struct nlmon *cur_nlmon;
enum msg_type { enum msg_type {
@ -113,6 +117,12 @@ struct nlmon {
bool noscan; bool noscan;
bool noies; bool noies;
bool read; bool read;
enum time_format time_format;
char *file_prefix;
unsigned int file_idx;
unsigned int max_files;
unsigned int max_size;
}; };
struct nlmon_req { struct nlmon_req {
@ -185,11 +195,15 @@ static void nlmon_req_free(void *data)
} }
static time_t time_offset = ((time_t) -1); static time_t time_offset = ((time_t) -1);
static enum time_format time_format;
static inline void update_time_offset(const struct timeval *tv) static inline void update_time_offset(const struct timeval *tv,
enum time_format tf)
{ {
if (tv && time_offset == ((time_t) -1)) if (tv && time_offset == ((time_t) -1)) {
time_offset = tv->tv_sec; time_offset = tv->tv_sec;
time_format = tf;
}
} }
#define print_indent(indent, color1, prefix, title, color2, fmt, args...) \ #define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
@ -225,15 +239,38 @@ static void print_packet(const struct timeval *tv, char ident,
int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0; int n, ts_len = 0, ts_pos = 0, len = 0, pos = 0;
if (tv) { if (tv) {
struct tm *tm;
if (use_color()) { if (use_color()) {
n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP); n = sprintf(ts_str + ts_pos, "%s", COLOR_TIMESTAMP);
if (n > 0) if (n > 0)
ts_pos += n; ts_pos += n;
} }
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_sec - time_offset,
(int64_t)tv->tv_usec); (int64_t)tv->tv_usec);
break;
case TIME_FORMAT_UTC:
tm = gmtime(&tv->tv_sec);
if (!tm) {
n = sprintf(ts_str + ts_pos, "%s",
"Time error");
break;
}
n = strftime(ts_str + ts_pos, sizeof(ts_str) - ts_pos,
"%b %d %H:%M:%S", tm);
break;
default:
/* Should never happen */
printf("Unknown time format");
l_main_quit();
return;
}
if (n > 0) { if (n > 0) {
ts_pos += n; ts_pos += n;
ts_len += n; ts_len += n;
@ -491,7 +528,30 @@ static void print_ie_country(unsigned int level, const char *label,
return; return;
} }
print_attr(level, "%s: %c%c%c", label, code[0], code[1], code[2]); print_attr(level, "%s: %c%c", label, code[0], code[1]);
switch (code[2]) {
case ' ':
print_attr(level + 1,
"3rd octet: 0x%02x: All environments", code[2]);
break;
case 'O':
print_attr(level + 1,
"3rd octet: 0x%02x: Outdoor environments", code[2]);
break;
case 'I':
print_attr(level + 1,
"3rd octet: 0x%02x: Indoor environments", code[2]);
break;
case 'X':
print_attr(level + 1,
"3rd octet: 0x%02x: Non-country entity", code[2]);
break;
default:
print_attr(level + 1,
"3rd octet: 0x%02x: Annex E table", code[2]);
break;
}
while (i < size) { while (i < size) {
if (code[i] > 200) { if (code[i] > 200) {
@ -1658,7 +1718,7 @@ static void print_ie_vht_capabilities(unsigned int level,
[21] = "TXOP PS", [21] = "TXOP PS",
[22] = "+HTC-VHT Capable", [22] = "+HTC-VHT Capable",
[23 ... 25] = "Maximum A-MPDU Length Exponent", [23 ... 25] = "Maximum A-MPDU Length Exponent",
[26 ... 27] = "VHT Link Adapation Capable", [26 ... 27] = "VHT Link Adaptation Capable",
[28] = "RX Antenna Pattern Consistency", [28] = "RX Antenna Pattern Consistency",
[29] = "TX Antenna Pattern Consistency", [29] = "TX Antenna Pattern Consistency",
[30 ... 31] = "Extended NSS BW Support", [30 ... 31] = "Extended NSS BW Support",
@ -1855,7 +1915,7 @@ static void print_ie_interworking(unsigned int level,
size--; size--;
ptr++; ptr++;
if (!size) if (size < 2)
return; return;
/* /*
@ -7341,6 +7401,64 @@ static bool nlmon_req_match(const void *a, const void *b)
return (req->seq == match->seq && req->pid == match->pid); return (req->seq == match->seq && req->pid == match->pid);
} }
/*
* Ensures that PCAP names are zero padded when needed. This makes the files
* sort correctly.
*/
static void next_pcap_name(char *buf, size_t size, const char *prefix,
unsigned int idx, unsigned int max)
{
unsigned int ndigits = 1;
while (max > 9) {
max /= 10;
ndigits++;
}
snprintf(buf, size, "%s%.*u", prefix, ndigits, idx);
}
static bool check_pcap(struct nlmon *nlmon, size_t next_size)
{
char path[PATH_MAX];
if (!nlmon->pcap)
return false;
if (!nlmon->max_size)
return true;
if (pcap_get_size(nlmon->pcap) + next_size <= nlmon->max_size)
return true;
pcap_close(nlmon->pcap);
/* Exhausted the single PCAP file */
if (nlmon->max_files < 2) {
printf("Reached maximum size of PCAP, exiting\n");
nlmon->pcap = NULL;
l_main_quit();
return false;
}
next_pcap_name(path, sizeof(path), nlmon->file_prefix,
++nlmon->file_idx, nlmon->max_files);
nlmon->pcap = pcap_create(path);
if (nlmon->max_files > nlmon->file_idx)
return true;
/* Remove oldest PCAP file */
next_pcap_name(path, sizeof(path), nlmon->file_prefix,
nlmon->file_idx - nlmon->max_files, nlmon->max_files);
if (remove(path) < 0)
printf("Failed to remove old PCAP file %s\n", path);
return true;
}
static void store_packet(struct nlmon *nlmon, const struct timeval *tv, static void store_packet(struct nlmon *nlmon, const struct timeval *tv,
uint16_t pkt_type, uint16_t pkt_type,
uint16_t arphrd_type, uint16_t arphrd_type,
@ -7349,7 +7467,7 @@ static void store_packet(struct nlmon *nlmon, const struct timeval *tv,
{ {
uint8_t sll_hdr[16], *buf = sll_hdr; uint8_t sll_hdr[16], *buf = sll_hdr;
if (!nlmon->pcap) if (!check_pcap(nlmon, sizeof(sll_hdr) + size))
return; return;
memset(sll_hdr, 0, sizeof(sll_hdr)); memset(sll_hdr, 0, sizeof(sll_hdr));
@ -7474,6 +7592,10 @@ struct nlmon *nlmon_create(uint16_t id, const struct nlmon_config *config)
nlmon->noscan = config->noscan; nlmon->noscan = config->noscan;
nlmon->noies = config->noies; nlmon->noies = config->noies;
nlmon->read = config->read_only; nlmon->read = config->read_only;
nlmon->time_format = config->time_format;
nlmon->max_files = config->pcap_file_count;
/* Command line expects MB, but use bytes internally */
nlmon->max_size = config->pcap_file_size * BYTES_PER_MB;
return nlmon; return nlmon;
} }
@ -8310,7 +8432,10 @@ void nlmon_print_rtnl(struct nlmon *nlmon, const struct timeval *tv,
int64_t aligned_size = NLMSG_ALIGN(size); int64_t aligned_size = NLMSG_ALIGN(size);
const struct nlmsghdr *nlmsg; const struct nlmsghdr *nlmsg;
update_time_offset(tv); if (nlmon->nortnl)
return;
update_time_offset(tv, nlmon->time_format);
for (nlmsg = data; NLMSG_OK(nlmsg, aligned_size); for (nlmsg = data; NLMSG_OK(nlmsg, aligned_size);
nlmsg = NLMSG_NEXT(nlmsg, aligned_size)) { nlmsg = NLMSG_NEXT(nlmsg, aligned_size)) {
@ -8348,7 +8473,7 @@ void nlmon_print_genl(struct nlmon *nlmon, const struct timeval *tv,
{ {
const struct nlmsghdr *nlmsg; const struct nlmsghdr *nlmsg;
update_time_offset(tv); update_time_offset(tv, nlmon->time_format);
for (nlmsg = data; NLMSG_OK(nlmsg, size); for (nlmsg = data; NLMSG_OK(nlmsg, size);
nlmsg = NLMSG_NEXT(nlmsg, size)) { nlmsg = NLMSG_NEXT(nlmsg, size)) {
@ -8371,7 +8496,7 @@ void nlmon_print_pae(struct nlmon *nlmon, const struct timeval *tv,
{ {
char extra_str[16]; char extra_str[16];
update_time_offset(tv); update_time_offset(tv, nlmon->time_format);
sprintf(extra_str, "len %u", size); sprintf(extra_str, "len %u", size);
@ -8498,13 +8623,20 @@ struct nlmon *nlmon_open(uint16_t id, const char *pathname,
struct nlmon *nlmon; struct nlmon *nlmon;
struct l_io *pae_io; struct l_io *pae_io;
struct pcap *pcap; struct pcap *pcap;
char path[PATH_MAX];
pae_io = open_pae(); pae_io = open_pae();
if (!pae_io) if (!pae_io)
return NULL; return NULL;
if (pathname) { if (pathname) {
pcap = pcap_create(pathname); if (config->pcap_file_count > 1)
next_pcap_name(path, sizeof(path), pathname,
0, config->pcap_file_count);
else
snprintf(path, sizeof(path), "%s", pathname);
pcap = pcap_create(path);
if (!pcap) { if (!pcap) {
l_io_destroy(pae_io); l_io_destroy(pae_io);
return NULL; return NULL;
@ -8517,6 +8649,7 @@ struct nlmon *nlmon_open(uint16_t id, const char *pathname,
nlmon->pae_io = pae_io; nlmon->pae_io = pae_io;
nlmon->pcap = pcap; nlmon->pcap = pcap;
nlmon->file_prefix = l_strdup(pathname);
l_io_set_read_handler(nlmon->pae_io, pae_receive, nlmon, NULL); l_io_set_read_handler(nlmon->pae_io, pae_receive, nlmon, NULL);
@ -8539,5 +8672,7 @@ void nlmon_close(struct nlmon *nlmon)
if (nlmon->pcap) if (nlmon->pcap)
pcap_close(nlmon->pcap); pcap_close(nlmon->pcap);
l_free(nlmon->file_prefix);
l_free(nlmon); l_free(nlmon);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -230,7 +230,7 @@ static void ap_stop_handshake(struct sta_state *sta)
} }
if (sta->hs) { if (sta->hs) {
handshake_state_free(sta->hs); handshake_state_unref(sta->hs);
sta->hs = NULL; sta->hs = NULL;
} }
@ -3914,34 +3914,34 @@ struct ap_state *ap_start(struct netdev *netdev, struct l_settings *config,
if (!frame_watch_add(wdev_id, 0, 0x0000 | if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST << 4), (MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST << 4),
NULL, 0, ap_assoc_req_cb, ap, NULL)) NULL, 0, false, ap_assoc_req_cb, ap, NULL))
goto error; goto error;
if (!frame_watch_add(wdev_id, 0, 0x0000 | if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST << 4), (MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST << 4),
NULL, 0, ap_reassoc_req_cb, ap, NULL)) NULL, 0, false, ap_reassoc_req_cb, ap, NULL))
goto error; goto error;
if (!wiphy_supports_probe_resp_offload(wiphy)) { if (!wiphy_supports_probe_resp_offload(wiphy)) {
if (!frame_watch_add(wdev_id, 0, 0x0000 | if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST << 4), (MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST << 4),
NULL, 0, ap_probe_req_cb, ap, NULL)) NULL, 0, false, ap_probe_req_cb, ap, NULL))
goto error; goto error;
} }
if (!frame_watch_add(wdev_id, 0, 0x0000 | if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION << 4), (MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION << 4),
NULL, 0, ap_disassoc_cb, ap, NULL)) NULL, 0, false, ap_disassoc_cb, ap, NULL))
goto error; goto error;
if (!frame_watch_add(wdev_id, 0, 0x0000 | if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION << 4), (MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION << 4),
NULL, 0, ap_auth_cb, ap, NULL)) NULL, 0, false, ap_auth_cb, ap, NULL))
goto error; goto error;
if (!frame_watch_add(wdev_id, 0, 0x0000 | if (!frame_watch_add(wdev_id, 0, 0x0000 |
(MPDU_MANAGEMENT_SUBTYPE_DEAUTHENTICATION << 4), (MPDU_MANAGEMENT_SUBTYPE_DEAUTHENTICATION << 4),
NULL, 0, ap_deauth_cb, ap, NULL)) NULL, 0, false, ap_deauth_cb, ap, NULL))
goto error; goto error;
ap->mlme_watch = l_genl_family_register(ap->nl80211, "mlme", ap->mlme_watch = l_genl_family_register(ap->nl80211, "mlme",

View File

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

View File

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

View File

@ -896,7 +896,7 @@ static const struct operating_class_info e4_operating_classes[] = {
}, },
{ {
.operating_class = 136, .operating_class = 136,
.starting_frequency = 5950, .starting_frequency = 5925,
.channel_spacing = 20, .channel_spacing = 20,
.center_frequencies = { 2 }, .center_frequencies = { 2 },
} }
@ -1352,6 +1352,10 @@ check_e4:
const struct operating_class_info *info = const struct operating_class_info *info =
&e4_operating_classes[i]; &e4_operating_classes[i];
if (band != band_oper_class_to_band(NULL,
info->operating_class))
continue;
if (e4_has_frequency(info, freq) == 0 || if (e4_has_frequency(info, freq) == 0 ||
e4_has_ccfi(info, freq) == 0) { e4_has_ccfi(info, freq) == 0) {
if (out_band) if (out_band)
@ -1426,7 +1430,7 @@ static const char *const oper_class_eu_codes[] = {
"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK" "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", "GB"
}; };
/* Annex E, table E-1 */ /* Annex E, table E-1 */

View File

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

View File

@ -20,6 +20,21 @@
* *
*/ */
void blacklist_add_bss(const uint8_t *addr); enum blacklist_reason {
bool blacklist_contains_bss(const uint8_t *addr); /*
void blacklist_remove_bss(const uint8_t *addr); * When a BSS is blacklisted using this reason IWD will refuse to
* connect to it via autoconnect
*/
BLACKLIST_REASON_CONNECT_FAILED,
/*
* This type of blacklist is added when a BSS requests IWD roams
* elsewhere. This is to aid in preventing IWD from roaming/connecting
* back to that BSS in the future unless there are no other "good"
* candidates to connect to.
*/
BLACKLIST_REASON_ROAM_REQUESTED,
};
void blacklist_add_bss(const uint8_t *addr, enum blacklist_reason reason);
bool blacklist_contains_bss(const uint8_t *addr, enum blacklist_reason reason);
void blacklist_remove_bss(const uint8_t *addr, enum blacklist_reason reason);

View File

@ -1212,7 +1212,7 @@ struct l_ecc_point *crypto_derive_sae_pwe_from_pt_ecc(const uint8_t *mac1,
struct l_ecc_point *pwe; struct l_ecc_point *pwe;
if (!pt || !curve) if (!pt || !curve)
return false; return NULL;
hash = crypto_sae_hash_from_ecc_prime_len(CRYPTO_SAE_HASH_TO_ELEMENT, hash = crypto_sae_hash_from_ecc_prime_len(CRYPTO_SAE_HASH_TO_ELEMENT,
l_ecc_curve_get_scalar_bytes(curve)); l_ecc_curve_get_scalar_bytes(curve));

View File

@ -1166,21 +1166,34 @@ struct dpp_uri_info *dpp_parse_uri(const char *uri)
switch (*pos) { switch (*pos) {
case 'C': case 'C':
if (L_WARN_ON(info->freqs))
goto free_info;
info->freqs = dpp_parse_class_and_channel(pos + 2, len); info->freqs = dpp_parse_class_and_channel(pos + 2, len);
if (!info->freqs) if (!info->freqs)
goto free_info; goto free_info;
break; break;
case 'M': case 'M':
if (L_WARN_ON(!l_memeqzero(info->mac,
sizeof(info->mac))))
goto free_info;
ret = dpp_parse_mac(pos + 2, len, info->mac); ret = dpp_parse_mac(pos + 2, len, info->mac);
if (ret < 0) if (ret < 0)
goto free_info; goto free_info;
break; break;
case 'V': case 'V':
if (L_WARN_ON(info->version != 0))
goto free_info;
ret = dpp_parse_version(pos + 2, len, &info->version); ret = dpp_parse_version(pos + 2, len, &info->version);
if (ret < 0) if (ret < 0)
goto free_info; goto free_info;
break; break;
case 'K': case 'K':
if (L_WARN_ON(info->boot_public))
goto free_info;
info->boot_public = dpp_parse_key(pos + 2, len); info->boot_public = dpp_parse_key(pos + 2, len);
if (!info->boot_public) if (!info->boot_public)
goto free_info; goto free_info;

163
src/dpp.c
View File

@ -66,7 +66,6 @@ static struct l_genl_family *nl80211;
static uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
static struct l_queue *dpp_list; static struct l_queue *dpp_list;
static uint32_t mlme_watch; static uint32_t mlme_watch;
static uint32_t unicast_watch;
static uint8_t dpp_prefix[] = { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x1a, 0x01 }; static uint8_t dpp_prefix[] = { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x1a, 0x01 };
@ -552,6 +551,8 @@ static void dpp_reset(struct dpp_sm *dpp)
dpp_property_changed_notify(dpp); dpp_property_changed_notify(dpp);
frame_watch_group_remove(dpp->wdev_id, FRAME_GROUP_DPP);
dpp->interface = DPP_INTERFACE_UNBOUND; dpp->interface = DPP_INTERFACE_UNBOUND;
if (station) { if (station) {
@ -2000,7 +2001,7 @@ static void dpp_offchannel_timeout(int error, void *user_data)
/* /*
* We have a pending agent request but it did not arrive in * We have a pending agent request but it did not arrive in
* time, we cant assume the enrollee will be waiting around * time, we can't assume the enrollee will be waiting around
* for our response so cancel the request and continue waiting * for our response so cancel the request and continue waiting
* for another request * for another request
*/ */
@ -2721,7 +2722,7 @@ static void dpp_handle_pkex_exchange_response(struct dpp_sm *dpp,
} }
if (version && version != dpp->pkex_version) { if (version && version != dpp->pkex_version) {
l_debug("PKEX version does not match, igoring"); l_debug("PKEX version does not match, ignoring");
return; return;
} }
@ -3437,7 +3438,7 @@ static void dpp_handle_pkex_commit_reveal_request(struct dpp_sm *dpp,
dpp->peer_boot_public = l_ecc_point_from_data(dpp->curve, dpp->peer_boot_public = l_ecc_point_from_data(dpp->curve,
L_ECC_POINT_TYPE_FULL, key, key_len); L_ECC_POINT_TYPE_FULL, key, key_len);
if (!dpp->peer_boot_public) { if (!dpp->peer_boot_public) {
l_debug("peers boostrapping key did not validate"); l_debug("peers bootstrapping key did not validate");
goto failed; goto failed;
} }
@ -3475,10 +3476,11 @@ failed:
dpp_reset(dpp); dpp_reset(dpp);
} }
static void dpp_handle_frame(struct dpp_sm *dpp, static void dpp_handle_frame(const struct mmpdu_header *frame,
const struct mmpdu_header *frame, const void *body, size_t body_len,
const void *body, size_t body_len) int rssi, void *user_data)
{ {
struct dpp_sm *dpp = user_data;
const uint8_t *ptr; const uint8_t *ptr;
/* /*
@ -3596,7 +3598,7 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data)
/* /*
* Only want to handle the no-ACK case. Re-transmitting an ACKed * Only want to handle the no-ACK case. Re-transmitting an ACKed
* frame likely wont do any good, at least in the case of DPP. * frame likely won't do any good, at least in the case of DPP.
*/ */
if (!ack) if (!ack)
goto retransmit; goto retransmit;
@ -3638,99 +3640,6 @@ retransmit:
dpp_frame_timeout, dpp, NULL); dpp_frame_timeout, dpp, NULL);
} }
static void dpp_unicast_notify(struct l_genl_msg *msg, void *user_data)
{
struct dpp_sm *dpp;
const uint64_t *wdev_id = NULL;
struct l_genl_attr attr;
uint16_t type, len, frame_len;
const void *data;
const struct mmpdu_header *mpdu = NULL;
const uint8_t *body;
size_t body_len;
if (l_genl_msg_get_command(msg) != NL80211_CMD_FRAME)
return;
if (!l_genl_attr_init(&attr, msg))
return;
while (l_genl_attr_next(&attr, &type, &len, &data)) {
switch (type) {
case NL80211_ATTR_WDEV:
if (len != 8)
break;
wdev_id = data;
break;
case NL80211_ATTR_FRAME:
mpdu = mpdu_validate(data, len);
if (!mpdu) {
l_warn("Frame didn't validate as MMPDU");
return;
}
frame_len = len;
break;
}
}
if (!wdev_id) {
l_warn("Bad wdev attribute");
return;
}
dpp = l_queue_find(dpp_list, match_wdev, wdev_id);
if (!dpp)
return;
if (!mpdu) {
l_warn("Missing frame data");
return;
}
body = mmpdu_body(mpdu);
body_len = (const uint8_t *) mpdu + frame_len - body;
if (body_len < sizeof(dpp_prefix) ||
memcmp(body, dpp_prefix, sizeof(dpp_prefix)) != 0)
return;
dpp_handle_frame(dpp, mpdu, body, body_len);
}
static void dpp_frame_watch_cb(struct l_genl_msg *msg, void *user_data)
{
if (l_genl_msg_get_error(msg) < 0)
l_error("Could not register frame watch type %04x: %i",
L_PTR_TO_UINT(user_data), l_genl_msg_get_error(msg));
}
/*
* Special case the frame watch which includes the presence frames since they
* require multicast support. This is only supported by ath9k, so adding
* general support to frame-xchg isn't desireable.
*/
static void dpp_frame_watch(struct dpp_sm *dpp, uint16_t frame_type,
const uint8_t *prefix, size_t prefix_len)
{
struct l_genl_msg *msg;
msg = l_genl_msg_new_sized(NL80211_CMD_REGISTER_FRAME, 32 + prefix_len);
l_genl_msg_append_attr(msg, NL80211_ATTR_WDEV, 8, &dpp->wdev_id);
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_TYPE, 2, &frame_type);
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_MATCH,
prefix_len, prefix);
if (dpp->mcast_support)
l_genl_msg_append_attr(msg, NL80211_ATTR_RECEIVE_MULTICAST,
0, NULL);
l_genl_family_send(nl80211, msg, dpp_frame_watch_cb,
L_UINT_TO_PTR(frame_type), NULL);
}
static void dpp_start_enrollee(struct dpp_sm *dpp); static void dpp_start_enrollee(struct dpp_sm *dpp);
static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp); static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp);
@ -3820,8 +3729,6 @@ static void dpp_create(struct netdev *netdev)
{ {
struct l_dbus *dbus = dbus_get_bus(); struct l_dbus *dbus = dbus_get_bus();
struct dpp_sm *dpp = l_new(struct dpp_sm, 1); struct dpp_sm *dpp = l_new(struct dpp_sm, 1);
uint8_t dpp_conf_response_prefix[] = { 0x04, 0x0b };
uint8_t dpp_conf_request_prefix[] = { 0x04, 0x0a };
uint64_t wdev_id = netdev_get_wdev_id(netdev); uint64_t wdev_id = netdev_get_wdev_id(netdev);
dpp->netdev = netdev; dpp->netdev = netdev;
@ -3832,9 +3739,8 @@ static void dpp_create(struct netdev *netdev)
dpp->key_len = l_ecc_curve_get_scalar_bytes(dpp->curve); dpp->key_len = l_ecc_curve_get_scalar_bytes(dpp->curve);
dpp->nonce_len = dpp_nonce_len_from_key_len(dpp->key_len); dpp->nonce_len = dpp_nonce_len_from_key_len(dpp->key_len);
dpp->max_roc = wiphy_get_max_roc_duration(wiphy_find_by_wdev(wdev_id)); dpp->max_roc = wiphy_get_max_roc_duration(wiphy_find_by_wdev(wdev_id));
dpp->mcast_support = wiphy_has_ext_feature( dpp->mcast_support = wiphy_supports_multicast_rx(
wiphy_find_by_wdev(dpp->wdev_id), wiphy_find_by_wdev(dpp->wdev_id));
NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS);
l_ecdh_generate_key_pair(dpp->curve, &dpp->boot_private, l_ecdh_generate_key_pair(dpp->curve, &dpp->boot_private,
&dpp->boot_public); &dpp->boot_public);
@ -3857,17 +3763,6 @@ static void dpp_create(struct netdev *netdev)
*/ */
dpp->refcount = 2; dpp->refcount = 2;
dpp_frame_watch(dpp, 0x00d0, dpp_prefix, sizeof(dpp_prefix));
frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
dpp_conf_response_prefix,
sizeof(dpp_conf_response_prefix),
dpp_handle_config_response_frame, dpp, NULL);
frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
dpp_conf_request_prefix,
sizeof(dpp_conf_request_prefix),
dpp_handle_config_request_frame, dpp, NULL);
dpp->known_network_watch = known_networks_watch_add( dpp->known_network_watch = known_networks_watch_add(
dpp_known_network_watch, dpp, NULL); dpp_known_network_watch, dpp, NULL);
@ -4012,6 +3907,26 @@ done:
return 0; return 0;
} }
static void dpp_add_frame_watches(struct dpp_sm *dpp, bool multicast_rx)
{
uint8_t dpp_conf_response_prefix[] = { 0x04, 0x0b };
uint8_t dpp_conf_request_prefix[] = { 0x04, 0x0a };
frame_watch_add(dpp->wdev_id, FRAME_GROUP_DPP, 0x00d0,
dpp_prefix, sizeof(dpp_prefix), multicast_rx,
dpp_handle_frame, dpp, NULL);
frame_watch_add(dpp->wdev_id, FRAME_GROUP_DPP, 0x00d0,
dpp_conf_response_prefix,
sizeof(dpp_conf_response_prefix), false,
dpp_handle_config_response_frame, dpp, NULL);
frame_watch_add(dpp->wdev_id, FRAME_GROUP_DPP, 0x00d0,
dpp_conf_request_prefix,
sizeof(dpp_conf_request_prefix), false,
dpp_handle_config_request_frame, dpp, NULL);
}
static void dpp_start_enrollee(struct dpp_sm *dpp) static void dpp_start_enrollee(struct dpp_sm *dpp)
{ {
uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ); uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ);
@ -4051,6 +3966,8 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
dpp->role = DPP_CAPABILITY_ENROLLEE; dpp->role = DPP_CAPABILITY_ENROLLEE;
dpp->interface = DPP_INTERFACE_DPP; dpp->interface = DPP_INTERFACE_DPP;
dpp_add_frame_watches(dpp, false);
ret = dpp_try_disconnect_station(dpp, &wait_for_disconnect); ret = dpp_try_disconnect_station(dpp, &wait_for_disconnect);
if (ret < 0) { if (ret < 0) {
dpp_reset(dpp); dpp_reset(dpp);
@ -4188,6 +4105,8 @@ static struct l_dbus_message *dpp_start_configurator_common(
} else } else
dpp->current_freq = bss->frequency; dpp->current_freq = bss->frequency;
dpp_add_frame_watches(dpp, responder && dpp->mcast_support);
dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2, dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2,
netdev_get_address(dpp->netdev), netdev_get_address(dpp->netdev),
&bss->frequency, 1, NULL, NULL); &bss->frequency, 1, NULL, NULL);
@ -4517,6 +4436,8 @@ static struct l_dbus_message *dpp_dbus_pkex_start_enrollee(struct l_dbus *dbus,
dpp->state = DPP_STATE_PKEX_EXCHANGE; dpp->state = DPP_STATE_PKEX_EXCHANGE;
dpp->interface = DPP_INTERFACE_PKEX; dpp->interface = DPP_INTERFACE_PKEX;
dpp_add_frame_watches(dpp, false);
ret = dpp_try_disconnect_station(dpp, &wait_for_disconnect); ret = dpp_try_disconnect_station(dpp, &wait_for_disconnect);
if (ret < 0) { if (ret < 0) {
dpp_reset(dpp); dpp_reset(dpp);
@ -4613,6 +4534,7 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp,
dpp->config = dpp_configuration_new(network_get_settings(network), dpp->config = dpp_configuration_new(network_get_settings(network),
network_get_ssid(network), network_get_ssid(network),
hs->akm_suite); hs->akm_suite);
dpp_add_frame_watches(dpp, dpp->mcast_support);
dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT); dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT);
dpp_property_changed_notify(dpp); dpp_property_changed_notify(dpp);
@ -4741,11 +4663,6 @@ static int dpp_init(void)
mlme_watch = l_genl_family_register(nl80211, "mlme", dpp_mlme_notify, mlme_watch = l_genl_family_register(nl80211, "mlme", dpp_mlme_notify,
NULL, NULL); NULL, NULL);
unicast_watch = l_genl_add_unicast_watch(iwd_get_genl(),
NL80211_GENL_NAME,
dpp_unicast_notify,
NULL, NULL);
dpp_list = l_queue_new(); dpp_list = l_queue_new();
return 0; return 0;
@ -4760,8 +4677,6 @@ static void dpp_exit(void)
netdev_watch_remove(netdev_watch); netdev_watch_remove(netdev_watch);
l_genl_remove_unicast_watch(iwd_get_genl(), unicast_watch);
l_genl_family_unregister(nl80211, mlme_watch); l_genl_family_unregister(nl80211, mlme_watch);
mlme_watch = 0; mlme_watch = 0;

View File

@ -544,7 +544,8 @@ static bool eap_mschapv2_load_settings(struct eap_state *eap,
return true; return true;
error: error:
free(state); l_free(state->user);
l_free(state);
return false; return false;
} }

View File

@ -165,7 +165,7 @@ static void __eap_tls_common_state_reset(struct eap_state *eap)
* *
* Drop the cache even if we have no indication that the * Drop the cache even if we have no indication that the
* method failed but it just didn't succeed, to handle cases like * method failed but it just didn't succeed, to handle cases like
* the server getting stuck and a timout occuring at a higher * the server getting stuck and a timeout occurring at a higher
* layer. The risk is that we may occasionally flush the session * layer. The risk is that we may occasionally flush the session
* data when there was only a momentary radio issue, invalid * data when there was only a momentary radio issue, invalid
* phase2 credentials or decision to abort. Those are not hot * phase2 credentials or decision to abort. Those are not hot

View File

@ -974,7 +974,7 @@ static void __eap_method_enable(struct eap_method_desc *start,
l_debug(""); l_debug("");
if (start == NULL || stop == NULL) if (!start || !stop)
return; return;
for (desc = start; desc < stop; desc++) { for (desc = start; desc < stop; desc++) {
@ -992,7 +992,7 @@ static void __eap_method_disable(struct eap_method_desc *start,
l_debug(""); l_debug("");
if (start == NULL || stop == NULL) if (!start || !stop)
return; return;
for (desc = start; desc < stop; desc++) { for (desc = start; desc < stop; desc++) {

View File

@ -342,8 +342,7 @@ static bool frame_watch_group_io_read(struct l_io *io, void *user_data)
nlmsg_len = bytes_read; nlmsg_len = bytes_read;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
struct nl_pktinfo pktinfo; struct nl_pktinfo pktinfo;
if (cmsg->cmsg_level != SOL_NETLINK) if (cmsg->cmsg_level != SOL_NETLINK)
@ -575,6 +574,7 @@ drop:
bool frame_watch_add(uint64_t wdev_id, uint32_t group_id, uint16_t frame_type, bool frame_watch_add(uint64_t wdev_id, uint32_t group_id, uint16_t frame_type,
const uint8_t *prefix, size_t prefix_len, const uint8_t *prefix, size_t prefix_len,
bool multicast_rx,
frame_watch_cb_t handler, void *user_data, frame_watch_cb_t handler, void *user_data,
frame_xchg_destroy_func_t destroy) frame_xchg_destroy_func_t destroy)
{ {
@ -614,6 +614,10 @@ bool frame_watch_add(uint64_t wdev_id, uint32_t group_id, uint16_t frame_type,
l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_MATCH, l_genl_msg_append_attr(msg, NL80211_ATTR_FRAME_MATCH,
prefix_len, prefix); prefix_len, prefix);
if (multicast_rx)
l_genl_msg_append_attr(msg, NL80211_ATTR_RECEIVE_MULTICAST,
0, NULL);
if (group->id == 0) if (group->id == 0)
l_genl_family_send(nl80211, msg, frame_watch_register_cb, l_genl_family_send(nl80211, msg, frame_watch_register_cb,
L_UINT_TO_PTR(frame_type), NULL); L_UINT_TO_PTR(frame_type), NULL);
@ -1194,7 +1198,7 @@ uint32_t frame_xchg_startv(uint64_t wdev_id, struct iovec *frame, uint32_t freq,
watch->prefix = prefix; watch->prefix = prefix;
watch->cb = va_arg(resp_args, void *); watch->cb = va_arg(resp_args, void *);
frame_watch_add(wdev_id, group_id, prefix->frame_type, frame_watch_add(wdev_id, group_id, prefix->frame_type,
prefix->data, prefix->len, prefix->data, prefix->len, false,
frame_xchg_resp_cb, fx, NULL); frame_xchg_resp_cb, fx, NULL);
if (!fx->rx_watches) if (!fx->rx_watches)

View File

@ -41,10 +41,12 @@ enum frame_xchg_group {
FRAME_GROUP_DEFAULT = 0, FRAME_GROUP_DEFAULT = 0,
FRAME_GROUP_P2P_LISTEN, FRAME_GROUP_P2P_LISTEN,
FRAME_GROUP_P2P_CONNECT, FRAME_GROUP_P2P_CONNECT,
FRAME_GROUP_DPP,
}; };
bool frame_watch_add(uint64_t wdev_id, uint32_t group, uint16_t frame_type, bool frame_watch_add(uint64_t wdev_id, uint32_t group, uint16_t frame_type,
const uint8_t *prefix, size_t prefix_len, const uint8_t *prefix, size_t prefix_len,
bool multicast_rx,
frame_watch_cb_t handler, void *user_data, frame_watch_cb_t handler, void *user_data,
frame_xchg_destroy_func_t destroy); frame_xchg_destroy_func_t destroy);
bool frame_watch_group_remove(uint64_t wdev_id, uint32_t group); bool frame_watch_group_remove(uint64_t wdev_id, uint32_t group);

View File

@ -43,6 +43,7 @@
#include "src/handshake.h" #include "src/handshake.h"
#include "src/erp.h" #include "src/erp.h"
#include "src/band.h" #include "src/band.h"
#include "src/pmksa.h"
static inline unsigned int n_ecc_groups(void) static inline unsigned int n_ecc_groups(void)
{ {
@ -103,7 +104,14 @@ void __handshake_set_install_ext_tk_func(handshake_install_ext_tk_func_t func)
install_ext_tk = func; install_ext_tk = func;
} }
void handshake_state_free(struct handshake_state *s) struct handshake_state *handshake_state_ref(struct handshake_state *s)
{
__sync_fetch_and_add(&s->refcount, 1);
return s;
}
void handshake_state_unref(struct handshake_state *s)
{ {
__typeof__(s->free) destroy; __typeof__(s->free) destroy;
@ -117,6 +125,9 @@ void handshake_state_free(struct handshake_state *s)
return; return;
} }
if (__sync_sub_and_fetch(&s->refcount, 1))
return;
l_free(s->authenticator_ie); l_free(s->authenticator_ie);
l_free(s->supplicant_ie); l_free(s->supplicant_ie);
l_free(s->authenticator_rsnxe); l_free(s->authenticator_rsnxe);
@ -128,6 +139,9 @@ void handshake_state_free(struct handshake_state *s)
l_free(s->fils_ip_resp_ie); l_free(s->fils_ip_resp_ie);
l_free(s->vendor_ies); l_free(s->vendor_ies);
if (s->have_pmksa)
l_free(s->pmksa);
if (s->erp_cache) if (s->erp_cache)
erp_cache_put(s->erp_cache); erp_cache_put(s->erp_cache);
@ -691,6 +705,11 @@ void handshake_state_install_ptk(struct handshake_state *s)
{ {
s->ptk_complete = true; s->ptk_complete = true;
if (!s->have_pmksa && IE_AKM_IS_SAE(s->akm_suite)) {
l_debug("Adding PMKSA expiration");
s->expiration = l_time_now() + pmksa_lifetime();
}
if (install_tk) { if (install_tk) {
uint32_t cipher = ie_rsn_cipher_suite_to_cipher( uint32_t cipher = ie_rsn_cipher_suite_to_cipher(
s->pairwise_cipher); s->pairwise_cipher);
@ -1193,3 +1212,89 @@ done:
return r; return r;
} }
bool handshake_state_set_pmksa(struct handshake_state *s,
struct pmksa *pmksa)
{
/* checks for both expiration || pmksa being set */
if (s->expiration)
return false;
s->pmksa = pmksa;
s->have_pmksa = true;
handshake_state_set_pmkid(s, pmksa->pmkid);
handshake_state_set_pmk(s, pmksa->pmk, pmksa->pmk_len);
return true;
}
static struct pmksa *handshake_state_steal_pmksa(struct handshake_state *s)
{
struct pmksa *pmksa;
uint64_t now = l_time_now();
if (s->have_pmksa) {
pmksa = l_steal_ptr(s->pmksa);
s->have_pmksa = false;
if (l_time_after(now, pmksa->expiration)) {
pmksa_cache_free(pmksa);
pmksa = NULL;
}
return pmksa;
}
if (s->expiration && l_time_after(now, s->expiration)) {
s->expiration = 0;
return NULL;
}
if (!s->have_pmkid)
return NULL;
pmksa = l_new(struct pmksa, 1);
pmksa->expiration = s->expiration;
memcpy(pmksa->spa, s->spa, sizeof(s->spa));
memcpy(pmksa->aa, s->aa, sizeof(s->aa));
memcpy(pmksa->ssid, s->ssid, s->ssid_len);
pmksa->ssid_len = s->ssid_len;
pmksa->akm = s->akm_suite;
memcpy(pmksa->pmkid, s->pmkid, sizeof(s->pmkid));
pmksa->pmk_len = s->pmk_len;
memcpy(pmksa->pmk, s->pmk, s->pmk_len);
return pmksa;
}
void handshake_state_cache_pmksa(struct handshake_state *s)
{
struct pmksa *pmksa = handshake_state_steal_pmksa(s);
if (!pmksa) {
l_debug("No PMKSA for "MAC, MAC_STR(s->aa));
return;
}
l_debug("Caching PMKSA for "MAC, MAC_STR(s->aa));
if (L_WARN_ON(pmksa_cache_put(pmksa) < 0))
pmksa_cache_free(pmksa);
}
bool handshake_state_remove_pmksa(struct handshake_state *s)
{
struct pmksa *pmksa;
if (!s->have_pmksa)
return false;
pmksa = handshake_state_steal_pmksa(s);
if (!pmksa)
return false;
pmksa_cache_free(pmksa);
return true;
}

View File

@ -29,6 +29,7 @@
struct handshake_state; struct handshake_state;
enum crypto_cipher; enum crypto_cipher;
struct eapol_frame; struct eapol_frame;
struct pmksa;
enum handshake_kde { enum handshake_kde {
/* 802.11-2020 Table 12-9 in section 12.7.2 */ /* 802.11-2020 Table 12-9 in section 12.7.2 */
@ -141,6 +142,11 @@ struct handshake_state {
bool supplicant_ocvc : 1; bool supplicant_ocvc : 1;
bool ext_key_id_capable : 1; bool ext_key_id_capable : 1;
bool force_default_ecc_group : 1; bool force_default_ecc_group : 1;
bool have_pmksa : 1;
union {
struct pmksa *pmksa;
uint64_t expiration;
};
uint8_t ssid[SSID_MAX_SIZE]; uint8_t ssid[SSID_MAX_SIZE];
size_t ssid_len; size_t ssid_len;
char *passphrase; char *passphrase;
@ -170,6 +176,8 @@ struct handshake_state {
bool in_event; bool in_event;
handshake_event_func_t event_func; handshake_event_func_t event_func;
int refcount;
}; };
#define HSID(x) UNIQUE_ID(handshake_, x) #define HSID(x) UNIQUE_ID(handshake_, x)
@ -186,7 +194,7 @@ struct handshake_state {
##__VA_ARGS__); \ ##__VA_ARGS__); \
\ \
if (!HSID(hs)->in_event) { \ if (!HSID(hs)->in_event) { \
handshake_state_free(HSID(hs)); \ handshake_state_unref(HSID(hs)); \
HSID(freed) = true; \ HSID(freed) = true; \
} else \ } else \
HSID(hs)->in_event = false; \ HSID(hs)->in_event = false; \
@ -194,7 +202,8 @@ struct handshake_state {
HSID(freed); \ HSID(freed); \
}) })
void handshake_state_free(struct handshake_state *s); struct handshake_state *handshake_state_ref(struct handshake_state *s);
void handshake_state_unref(struct handshake_state *s);
void handshake_state_set_supplicant_address(struct handshake_state *s, void handshake_state_set_supplicant_address(struct handshake_state *s,
const uint8_t *spa); const uint8_t *spa);
@ -299,6 +308,10 @@ void handshake_state_set_chandef(struct handshake_state *s,
int handshake_state_verify_oci(struct handshake_state *s, const uint8_t *oci, int handshake_state_verify_oci(struct handshake_state *s, const uint8_t *oci,
size_t oci_len); size_t oci_len);
bool handshake_state_set_pmksa(struct handshake_state *s, struct pmksa *pmksa);
void handshake_state_cache_pmksa(struct handshake_state *s);
bool handshake_state_remove_pmksa(struct handshake_state *s);
bool handshake_util_ap_ie_matches(const struct ie_rsn_info *msg_info, bool handshake_util_ap_ie_matches(const struct ie_rsn_info *msg_info,
const uint8_t *scan_ie, bool is_wpa); const uint8_t *scan_ie, bool is_wpa);
@ -316,4 +329,4 @@ void handshake_util_build_gtk_kde(enum crypto_cipher cipher, const uint8_t *key,
void handshake_util_build_igtk_kde(enum crypto_cipher cipher, const uint8_t *key, void handshake_util_build_igtk_kde(enum crypto_cipher cipher, const uint8_t *key,
unsigned int key_index, uint8_t *to); unsigned int key_index, uint8_t *to);
DEFINE_CLEANUP_FUNC(handshake_state_free); DEFINE_CLEANUP_FUNC(handshake_state_unref);

View File

@ -598,6 +598,23 @@ static inline const unsigned char *ie_tlv_iter_get_data(
return iter->data; return iter->data;
} }
static inline bool ie_tlv_iter_data_eq(struct ie_tlv_iter *a,
struct ie_tlv_iter *b)
{
if (a == b)
return true;
if (a == NULL || b == NULL)
return false;
if (ie_tlv_iter_get_length(a) != ie_tlv_iter_get_length(b))
return false;
return memcmp(ie_tlv_iter_get_data(a),
ie_tlv_iter_get_data(b),
ie_tlv_iter_get_length(a)) == 0;
}
void *ie_tlv_extract_wsc_payload(const uint8_t *ies, size_t len, void *ie_tlv_extract_wsc_payload(const uint8_t *ies, size_t len,
ssize_t *out_len); ssize_t *out_len);
void *ie_tlv_encapsulate_wsc_payload(const uint8_t *data, size_t len, void *ie_tlv_encapsulate_wsc_payload(const uint8_t *data, size_t len,

View File

@ -225,6 +225,11 @@ The group ``[General]`` contains general settings.
request is just a 'hint' and ultimately left up to the kernel to set the request is just a 'hint' and ultimately left up to the kernel to set the
country. country.
* - DisablePMKSA
- Value: **false**, true
Disable PMKSA support in IWD
Network Network
------- -------
@ -285,9 +290,17 @@ control how long a misbehaved BSS spends on the blacklist.
* - InitialTimeout * - InitialTimeout
- Values: uint64 value in seconds (default: **60**) - Values: uint64 value in seconds (default: **60**)
The initial time that a BSS spends on the blacklist. The initial time that a BSS spends on the blacklist. Setting this to zero
will disable blacklisting functionality in IWD.
* - InitialRoamRequestedTimeout
- Values: uint64 value in seconds (default: **30**)
The initial time that a BSS will be marked after a BSS requests a roam.
This is to aid in avoiding roaming back to BSS's which are likely
overloaded. Setting this to zero will disabled this form of blacklisting.
* - Multiplier * - Multiplier
- Values: unsigned int value in seconds (default: **30**) - Values: unsigned int value greater than zero, in seconds
(default: **30**)
If the BSS was blacklisted previously and another connection attempt If the BSS was blacklisted previously and another connection attempt
has failed after the initial timeout has expired, then the BSS blacklist has failed after the initial timeout has expired, then the BSS blacklist
@ -341,6 +354,28 @@ autoconnect purposes.
A value of 0.0 will disable the 6GHz band and prevent scanning or A value of 0.0 will disable the 6GHz band and prevent scanning or
connecting on those frequencies. connecting on those frequencies.
* - HighUtilizationThreshold
- Values: unsigned integer value 0 - 255 (default: **0**, disabled)
**Warning: This is an experimental feature**
The BSS utilization threshold at which a negative rank factor begins to
be applied to the BSS. As the load increases for a BSS the ranking factor
decays exponentially, meaning the ranking factor will decrease
exponentially. Setting this can have very drastic effects on the BSS rank
if its utilization is high, use with care.
* - HighStationCountThreshold
- Values: unsigned integer value 0 - 255 (default: **0**, disabled)
**Warning: This is an experimental feature**
The BSS station count threshold at which a negative rank factor begins to
be applied to the BSS. As the station count increases for a BSS the
ranking factor decays exponentially, meaning the ranking factor will
decrease exponentially. Setting this can have very drastic effects on the
BSS rank if its station count is high, use with care.
Scan Scan
---- ----
@ -432,6 +467,20 @@ are buggy or just don't behave similar enough to the majority of other drivers.
If a driver in user matches one in this list power save will be disabled. If a driver in user matches one in this list power save will be disabled.
* - MulticastRxDisable
- Values: comma-separated list of drivers or glob matches
If a driver in use matches one in this list, multicast RX will be
disabled.
* - SaeDisable
- Values: comma-separated list of drivers or glob matches
If a driver in use matches one in this list, SAE/WPA3 will be disabled
for connections. This will prevent connections to WPA3-only networks, but
will allow for connections to WPA3/WPA2 hybrid networks by utilizing
WPA2.
SEE ALSO SEE ALSO
======== ========

View File

@ -209,7 +209,7 @@ connect to that network.
* - PasswordIdentifier * - PasswordIdentifier
- string - string
An identifer string to be used with the passphrase. This is used for An identifier string to be used with the passphrase. This is used for
WPA3-Personal (SAE) networks if the security has enabled password WPA3-Personal (SAE) networks if the security has enabled password
identifiers for clients. identifiers for clients.
* - PreSharedKey * - PreSharedKey

View File

@ -894,11 +894,11 @@ static int manager_init(void)
} }
if (!l_settings_get_bool(config, "General", if (!l_settings_get_bool(config, "General",
"UseDefaultInterface", &use_default)) { "UseDefaultInterface", &use_default))
use_default = false;
else
l_warn("[General].UseDefaultInterface is deprecated, please " l_warn("[General].UseDefaultInterface is deprecated, please "
"use [DriverQuirks].DefaultInterface instead"); "use [DriverQuirks].DefaultInterface instead");
use_default = false;
}
return 0; return 0;

View File

@ -398,9 +398,16 @@ static bool validate_mgmt_ies(const uint8_t *ies, size_t ies_len,
memcpy(&clone, &iter, sizeof(clone)); memcpy(&clone, &iter, sizeof(clone));
/*
* Some APs send completely identical duplicate IEs:
* Since these are harmless (and ignored by us) we're
* going to allow them here for interoperability.
*/
while (ie_tlv_iter_next(&clone)) { while (ie_tlv_iter_next(&clone)) {
if (ie_tlv_iter_get_tag(&clone) != tag) if (ie_tlv_iter_get_tag(&clone) != tag)
continue; continue;
else if (ie_tlv_iter_data_eq(&iter, &clone))
continue;
return false; return false;
} }

View File

@ -82,7 +82,7 @@ bool mschap_challenge_response(const uint8_t *challenge,
/** /**
* Hash the utf8 encoded nt password. * Hash the utf8 encoded nt password.
* It is asumed, that the password is valid utf8! * It is assumed, that the password is valid utf8!
* The rfc says "unicode-char", but never specifies which encoding. * The rfc says "unicode-char", but never specifies which encoding.
* This function converts the password to ucs-2. * This function converts the password to ucs-2.
* The example in the code uses LE for the unicode chars, so it is forced here. * The example in the code uses LE for the unicode chars, so it is forced here.

View File

@ -622,7 +622,7 @@ bool netconfig_get_fils_ip_req(struct netconfig *netconfig,
struct ie_fils_ip_addr_request_info *info) struct ie_fils_ip_addr_request_info *info)
{ {
/* /*
* Fill in the fields used for building the FILS IP Address Assigment * Fill in the fields used for building the FILS IP Address Assignment
* IE during connection if we're configured to do automatic network * IE during connection if we're configured to do automatic network
* configuration (usually DHCP). If we're configured with static * configuration (usually DHCP). If we're configured with static
* values return false to mean the IE should not be sent. * values return false to mean the IE should not be sent.
@ -710,7 +710,7 @@ struct netconfig *netconfig_new(uint32_t ifindex)
netconfig_commit_init(netconfig); netconfig_commit_init(netconfig);
debug_level = getenv("IWD_DHCP_DEBUG"); debug_level = getenv("IWD_DHCP_DEBUG");
if (debug_level != NULL) { if (debug_level) {
if (!strcmp("debug", debug_level)) if (!strcmp("debug", debug_level))
dhcp_priority = L_LOG_DEBUG; dhcp_priority = L_LOG_DEBUG;
else if (!strcmp("info", debug_level)) else if (!strcmp("info", debug_level))

View File

@ -65,6 +65,7 @@
#include "src/frame-xchg.h" #include "src/frame-xchg.h"
#include "src/diagnostic.h" #include "src/diagnostic.h"
#include "src/band.h" #include "src/band.h"
#include "src/pmksa.h"
#ifndef ENOTSUPP #ifndef ENOTSUPP
#define ENOTSUPP 524 #define ENOTSUPP 524
@ -192,6 +193,7 @@ struct netdev {
bool in_reassoc : 1; bool in_reassoc : 1;
bool privacy : 1; bool privacy : 1;
bool cqm_poll_fallback : 1; bool cqm_poll_fallback : 1;
bool external_auth : 1;
}; };
struct netdev_preauth_state { struct netdev_preauth_state {
@ -375,6 +377,7 @@ struct handshake_state *netdev_handshake_state_new(struct netdev *netdev)
nhs->super.ifindex = netdev->index; nhs->super.ifindex = netdev->index;
nhs->super.free = netdev_handshake_state_free; nhs->super.free = netdev_handshake_state_free;
nhs->super.refcount = 1;
nhs->netdev = netdev; nhs->netdev = netdev;
/* /*
@ -460,6 +463,14 @@ uint8_t netdev_get_rssi_level_idx(struct netdev *netdev)
return netdev->cur_rssi_level_idx; return netdev->cur_rssi_level_idx;
} }
int netdev_get_low_signal_threshold(uint32_t frequency)
{
if (frequency > 4000)
return LOW_SIGNAL_THRESHOLD_5GHZ;
return LOW_SIGNAL_THRESHOLD;
}
static void netdev_set_powered_result(int error, uint16_t type, static void netdev_set_powered_result(int error, uint16_t type,
const void *data, const void *data,
uint32_t len, void *user_data) uint32_t len, void *user_data)
@ -602,7 +613,7 @@ static bool netdev_parse_sta_info(struct l_genl_attr *attr,
if (!netdev_parse_bitrate(&nested, &info->rx_mcs_type, if (!netdev_parse_bitrate(&nested, &info->rx_mcs_type,
&info->rx_bitrate, &info->rx_bitrate,
&info->rx_mcs)) &info->rx_mcs))
return false; continue;
info->have_rx_bitrate = true; info->have_rx_bitrate = true;
@ -618,7 +629,7 @@ static bool netdev_parse_sta_info(struct l_genl_attr *attr,
if (!netdev_parse_bitrate(&nested, &info->tx_mcs_type, if (!netdev_parse_bitrate(&nested, &info->tx_mcs_type,
&info->tx_bitrate, &info->tx_bitrate,
&info->tx_mcs)) &info->tx_mcs))
return false; continue;
info->have_tx_bitrate = true; info->have_tx_bitrate = true;
@ -768,11 +779,12 @@ static void netdev_rssi_poll(struct l_timeout *timeout, void *user_data)
/* To be called whenever operational or rssi_levels_num are updated */ /* To be called whenever operational or rssi_levels_num are updated */
static void netdev_rssi_polling_update(struct netdev *netdev) static void netdev_rssi_polling_update(struct netdev *netdev)
{ {
if (wiphy_has_ext_feature(netdev->wiphy, if (!netdev->cqm_poll_fallback && wiphy_has_ext_feature(netdev->wiphy,
NL80211_EXT_FEATURE_CQM_RSSI_LIST)) NL80211_EXT_FEATURE_CQM_RSSI_LIST))
return; return;
if (netdev->operational && netdev->rssi_levels_num > 0) { if (netdev->operational && (netdev->rssi_levels_num > 0 ||
netdev->cqm_poll_fallback)) {
if (netdev->rssi_poll_timeout) if (netdev->rssi_poll_timeout)
return; return;
@ -826,7 +838,7 @@ static void netdev_connect_free(struct netdev *netdev)
eapol_preauth_cancel(netdev->index); eapol_preauth_cancel(netdev->index);
if (netdev->handshake) { if (netdev->handshake) {
handshake_state_free(netdev->handshake); handshake_state_unref(netdev->handshake);
netdev->handshake = NULL; netdev->handshake = NULL;
} }
@ -871,6 +883,7 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->expect_connect_failure = false; netdev->expect_connect_failure = false;
netdev->cur_rssi_low = false; netdev->cur_rssi_low = false;
netdev->privacy = false; netdev->privacy = false;
netdev->external_auth = false;
if (netdev->connect_cmd) { if (netdev->connect_cmd) {
l_genl_msg_unref(netdev->connect_cmd); l_genl_msg_unref(netdev->connect_cmd);
@ -1096,6 +1109,7 @@ static void netdev_free(void *data)
l_timeout_remove(netdev->rssi_poll_timeout); l_timeout_remove(netdev->rssi_poll_timeout);
scan_wdev_remove(netdev->wdev_id); scan_wdev_remove(netdev->wdev_id);
frame_watch_wdev_remove(netdev->wdev_id);
watchlist_destroy(&netdev->station_watches); watchlist_destroy(&netdev->station_watches);
@ -1493,6 +1507,105 @@ static void netdev_setting_keys_failed(struct netdev_handshake_state *nhs,
handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err); handshake_event(&nhs->super, HANDSHAKE_EVENT_SETTING_KEYS_FAILED, &err);
} }
static bool netdev_match_addr(const void *a, const void *b)
{
const struct netdev *netdev = a;
const uint8_t *addr = b;
return memcmp(netdev->addr, addr, ETH_ALEN) == 0;
}
static struct netdev *netdev_find_by_address(const uint8_t *addr)
{
return l_queue_find(netdev_list, netdev_match_addr, addr);
}
static void netdev_pmksa_driver_add(const struct pmksa *pmksa)
{
struct l_genl_msg *msg;
struct netdev *netdev = netdev_find_by_address(pmksa->spa);
uint32_t expiration = (uint32_t)pmksa->expiration;
if (!netdev)
return;
/* Only need to set the PMKSA into the kernel for fullmac drivers */
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
return;
l_debug("Adding PMKSA to kernel");
msg = l_genl_msg_new(NL80211_CMD_SET_PMKSA);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
l_genl_msg_append_attr(msg, NL80211_ATTR_PMKID, 16, pmksa->pmkid);
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, pmksa->aa);
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
pmksa->ssid_len, pmksa->ssid);
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK_LIFETIME, 4, &expiration);
l_genl_msg_append_attr(msg, NL80211_ATTR_PMK,
pmksa->pmk_len, pmksa->pmk);
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
l_error("error sending SET_PMKSA");
}
static void netdev_pmksa_driver_remove(const struct pmksa *pmksa)
{
struct l_genl_msg *msg;
struct netdev *netdev = netdev_find_by_address(pmksa->spa);
if (!netdev)
return;
/* Only need to set the PMKSA into the kernel for fullmac drivers */
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
return;
l_debug("Removing PMKSA from kernel");
msg = l_genl_msg_new(NL80211_CMD_DEL_PMKSA);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
l_genl_msg_append_attr(msg, NL80211_ATTR_PMKID, 16, pmksa->pmkid);
l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, pmksa->aa);
l_genl_msg_append_attr(msg, NL80211_ATTR_SSID,
pmksa->ssid_len, pmksa->ssid);
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
l_error("error sending DEL_PMKSA");
}
static void netdev_flush_pmksa(struct netdev *netdev)
{
struct l_genl_msg *msg;
/*
* We only utilize the kernel's PMKSA cache for fullmac cards,
* so no need to flush if this is a softmac.
*/
if (wiphy_supports_cmds_auth_assoc(netdev->wiphy))
return;
msg = l_genl_msg_new(NL80211_CMD_FLUSH_PMKSA);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index);
if (!l_genl_family_send(nl80211, msg, NULL, NULL, NULL))
l_error("Failed to flush PMKSA for %u", netdev->index);
}
static void netdev_pmksa_driver_flush(void)
{
const struct l_queue_entry *e;
for (e = l_queue_get_entries(netdev_list); e; e = e->next) {
struct netdev *netdev = e->data;
netdev_flush_pmksa(netdev);
}
}
static void try_handshake_complete(struct netdev_handshake_state *nhs) static void try_handshake_complete(struct netdev_handshake_state *nhs)
{ {
l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u", l_debug("ptk_installed: %u, gtk_installed: %u, igtk_installed: %u",
@ -1513,6 +1626,8 @@ static void try_handshake_complete(struct netdev_handshake_state *nhs)
l_debug("Invoking handshake_event()"); l_debug("Invoking handshake_event()");
handshake_state_cache_pmksa(&nhs->super);
if (handshake_event(&nhs->super, HANDSHAKE_EVENT_COMPLETE)) if (handshake_event(&nhs->super, HANDSHAKE_EVENT_COMPLETE))
return; return;
@ -2166,7 +2281,7 @@ static void netdev_set_pmk(struct handshake_state *hs, const uint8_t *pmk,
struct netdev_handshake_state, super); struct netdev_handshake_state, super);
struct netdev *netdev = nhs->netdev; struct netdev *netdev = nhs->netdev;
/* Only relevent for 8021x offload */ /* Only relevant for 8021x offload */
if (nhs->type != CONNECTION_TYPE_8021X_OFFLOAD) if (nhs->type != CONNECTION_TYPE_8021X_OFFLOAD)
return; return;
@ -2278,7 +2393,7 @@ static void netdev_qos_map_cb(struct l_genl_msg *msg, void *user_data)
return; return;
ext_error = l_genl_msg_get_extended_error(msg); ext_error = l_genl_msg_get_extended_error(msg);
l_error("Couuld not set QoS Map in kernel: %s", l_error("Could not set QoS Map in kernel: %s",
ext_error ? ext_error : strerror(-err)); ext_error ? ext_error : strerror(-err));
} }
@ -2351,7 +2466,7 @@ static void netdev_get_oci_cb(struct l_genl_msg *msg, void *user_data)
done: done:
if (netdev->ap) { if (netdev->ap) {
/* /*
* Cant do much here. IWD assumes every kernel/driver supports * Can't do much here. IWD assumes every kernel/driver supports
* this. There is no way of detecting support either. * this. There is no way of detecting support either.
*/ */
if (L_WARN_ON(err < 0)) if (L_WARN_ON(err < 0))
@ -2454,7 +2569,19 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
{ {
struct netdev_handshake_state *nhs = struct netdev_handshake_state *nhs =
l_container_of(hs, struct netdev_handshake_state, super); l_container_of(hs, struct netdev_handshake_state, super);
uint32_t auth_type = IE_AKM_IS_SAE(hs->akm_suite) ? /*
* Choose Open system auth type if PMKSA caching is used for an SAE AKM:
*
* IEEE 802.11-2020 Table 9-151
* - SAE authentication:
* 3 (SAE) for SAE Authentication
* 0 (open) for PMKSA caching
* - FT authentication over SAE:
* 3 (SAE) for FT Initial Mobility Domain Association
* 0 (open) for FT Initial Mobility Domain Association over
* PMKSA caching
*/
uint32_t auth_type = IE_AKM_IS_SAE(hs->akm_suite) && !hs->have_pmksa ?
NL80211_AUTHTYPE_SAE : NL80211_AUTHTYPE_SAE :
NL80211_AUTHTYPE_OPEN_SYSTEM; NL80211_AUTHTYPE_OPEN_SYSTEM;
enum mpdu_management_subtype subtype = prev_bssid ? enum mpdu_management_subtype subtype = prev_bssid ?
@ -2478,7 +2605,10 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
switch (nhs->type) { switch (nhs->type) {
case CONNECTION_TYPE_SOFTMAC: case CONNECTION_TYPE_SOFTMAC:
break;
case CONNECTION_TYPE_FULLMAC: case CONNECTION_TYPE_FULLMAC:
l_genl_msg_append_attr(msg,
NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, 0, NULL);
break; break;
case CONNECTION_TYPE_SAE_OFFLOAD: case CONNECTION_TYPE_SAE_OFFLOAD:
l_genl_msg_append_attr(msg, NL80211_ATTR_SAE_PASSWORD, l_genl_msg_append_attr(msg, NL80211_ATTR_SAE_PASSWORD,
@ -3392,6 +3522,84 @@ static void netdev_fils_tx_associate(struct iovec *fils_iov, size_t n_fils_iov,
} }
} }
static void netdev_external_auth_frame_cb(struct l_genl_msg *msg,
void *user_data)
{
int error = l_genl_msg_get_error(msg);
if (error < 0)
l_debug("Failed to send External Auth Frame: %s(%d)",
strerror(-error), -error);
}
static void netdev_external_auth_sae_tx_authenticate(const uint8_t *body,
size_t body_len, void *user_data)
{
struct netdev *netdev = user_data;
struct handshake_state *hs = netdev->handshake;
uint16_t frame_type = MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION << 4;
struct iovec iov[2];
struct l_genl_msg *msg;
uint8_t algorithm[2] = { 0x03, 0x00 };
l_debug("");
iov[0].iov_base = &algorithm;
iov[0].iov_len = sizeof(algorithm);
iov[1].iov_base = (void *) body;
iov[1].iov_len = body_len;
msg = nl80211_build_cmd_frame(netdev->index, frame_type,
hs->spa, hs->aa, 0, iov, 2);
if (l_genl_family_send(nl80211, msg, netdev_external_auth_frame_cb,
netdev, NULL) > 0)
return;
l_genl_msg_unref(msg);
}
static void netdev_external_auth_cb(struct l_genl_msg *msg, void *user_data)
{
int error = l_genl_msg_get_error(msg);
if (error < 0)
l_debug("Failed to send External Auth: %s(%d)",
strerror(-error), -error);
}
static void netdev_send_external_auth(struct netdev *netdev,
uint16_t status_code)
{
struct handshake_state *hs = netdev->handshake;
struct l_genl_msg *msg =
nl80211_build_external_auth(netdev->index, status_code,
hs->ssid, hs->ssid_len, hs->aa);
if (l_genl_family_send(nl80211, msg, netdev_external_auth_cb,
netdev, NULL) > 0)
return;
l_genl_msg_unref(msg);
}
static void netdev_external_auth_sae_tx_associate(void *user_data)
{
struct netdev *netdev = user_data;
l_debug("");
netdev_send_external_auth(netdev, MMPDU_STATUS_CODE_SUCCESS);
netdev_ensure_eapol_registered(netdev);
/*
* Free the auth proto now. With external auth there is no associate
* event which is where this normally gets cleaned up.
*/
auth_proto_free(netdev->ap);
netdev->ap = NULL;
}
struct rtnl_data { struct rtnl_data {
struct netdev *netdev; struct netdev *netdev;
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
@ -3400,6 +3608,10 @@ struct rtnl_data {
static int netdev_begin_connection(struct netdev *netdev) static int netdev_begin_connection(struct netdev *netdev)
{ {
struct netdev_handshake_state *nhs =
l_container_of(netdev->handshake,
struct netdev_handshake_state, super);
if (netdev->connect_cmd) { if (netdev->connect_cmd) {
netdev->connect_cmd_id = l_genl_family_send(nl80211, netdev->connect_cmd_id = l_genl_family_send(nl80211,
netdev->connect_cmd, netdev->connect_cmd,
@ -3419,7 +3631,7 @@ static int netdev_begin_connection(struct netdev *netdev)
*/ */
handshake_state_set_supplicant_address(netdev->handshake, netdev->addr); handshake_state_set_supplicant_address(netdev->handshake, netdev->addr);
if (netdev->ap) { if (netdev->ap && nhs->type == CONNECTION_TYPE_SOFTMAC) {
if (!auth_proto_start(netdev->ap)) if (!auth_proto_start(netdev->ap))
goto failed; goto failed;
@ -3712,6 +3924,15 @@ static void netdev_cmd_set_cqm_cb(struct l_genl_msg *msg, void *user_data)
static int netdev_cqm_rssi_update(struct netdev *netdev) static int netdev_cqm_rssi_update(struct netdev *netdev)
{ {
struct l_genl_msg *msg; struct l_genl_msg *msg;
struct netdev_handshake_state *nhs = l_container_of(netdev->handshake,
struct netdev_handshake_state, super);
/*
* Fullmac cards handle roaming in firmware, there is no need to set
* CQM thresholds
*/
if (nhs->type == CONNECTION_TYPE_FULLMAC)
return 0;
l_debug(""); l_debug("");
@ -3870,7 +4091,11 @@ static int netdev_handshake_state_setup_connection_type(
if (softmac && wiphy_has_feature(wiphy, NL80211_FEATURE_SAE)) if (softmac && wiphy_has_feature(wiphy, NL80211_FEATURE_SAE))
goto softmac; goto softmac;
return -EINVAL; /* FullMAC uses EXTERNAL_AUTH and reuses this feature bit */
if (wiphy_has_feature(wiphy, NL80211_FEATURE_SAE))
goto fullmac;
return -ENOTSUP;
case IE_RSN_AKM_SUITE_FILS_SHA256: case IE_RSN_AKM_SUITE_FILS_SHA256:
case IE_RSN_AKM_SUITE_FILS_SHA384: case IE_RSN_AKM_SUITE_FILS_SHA384:
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256: case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256:
@ -3941,40 +4166,55 @@ static void netdev_connect_common(struct netdev *netdev,
goto done; goto done;
} }
if (nhs->type != CONNECTION_TYPE_SOFTMAC) /*
* If SAE, and we have a valid PMKSA cache we can skip the entire SAE
* protocol and authenticate using the cached keys.
*/
if (IE_AKM_IS_SAE(hs->akm_suite) && hs->have_pmksa) {
l_debug("Skipping SAE by using PMKSA cache");
goto build_cmd_connect;
}
if (!IE_AKM_IS_SAE(hs->akm_suite) ||
nhs->type == CONNECTION_TYPE_SAE_OFFLOAD)
goto build_cmd_connect; goto build_cmd_connect;
switch (hs->akm_suite) { if (nhs->type == CONNECTION_TYPE_SOFTMAC)
case IE_RSN_AKM_SUITE_SAE_SHA256:
case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256:
netdev->ap = sae_sm_new(hs, netdev_sae_tx_authenticate, netdev->ap = sae_sm_new(hs, netdev_sae_tx_authenticate,
netdev_sae_tx_associate, netdev_sae_tx_associate,
netdev); netdev);
else {
netdev->ap =
sae_sm_new(hs, netdev_external_auth_sae_tx_authenticate,
netdev_external_auth_sae_tx_associate,
netdev);
sae_sm_force_default_group(netdev->ap);
sae_sm_force_hunt_and_peck(netdev->ap);
}
if (sae_sm_is_h2e(netdev->ap)) { if (sae_sm_is_h2e(netdev->ap)) {
uint8_t own_rsnxe[20]; uint8_t own_rsnxe[20];
if (wiphy_get_rsnxe(netdev->wiphy, if (wiphy_get_rsnxe(netdev->wiphy,
own_rsnxe, sizeof(own_rsnxe))) { own_rsnxe, sizeof(own_rsnxe))) {
set_bit(own_rsnxe + 2, IE_RSNX_SAE_H2E); set_bit(own_rsnxe + 2, IE_RSNX_SAE_H2E);
handshake_state_set_supplicant_rsnxe(hs, handshake_state_set_supplicant_rsnxe(hs,
own_rsnxe); own_rsnxe);
}
}
break;
default:
build_cmd_connect:
cmd_connect = netdev_build_cmd_connect(netdev, hs, prev_bssid);
if (!is_offload(hs) && (is_rsn || hs->settings_8021x)) {
sm = eapol_sm_new(hs);
if (nhs->type == CONNECTION_TYPE_8021X_OFFLOAD)
eapol_sm_set_require_handshake(sm, false);
} }
} }
if (nhs->type == CONNECTION_TYPE_SOFTMAC)
goto done;
build_cmd_connect:
cmd_connect = netdev_build_cmd_connect(netdev, hs, prev_bssid);
if (!is_offload(hs) && (is_rsn || hs->settings_8021x)) {
sm = eapol_sm_new(hs);
if (nhs->type == CONNECTION_TYPE_8021X_OFFLOAD)
eapol_sm_set_require_handshake(sm, false);
}
done: done:
netdev->connect_cmd = cmd_connect; netdev->connect_cmd = cmd_connect;
netdev->event_filter = event_filter; netdev->event_filter = event_filter;
@ -4148,7 +4388,7 @@ int netdev_reassociate(struct netdev *netdev, const struct scan_bss *target_bss,
eapol_sm_free(old_sm); eapol_sm_free(old_sm);
if (old_hs) if (old_hs)
handshake_state_free(old_hs); handshake_state_unref(old_hs);
return 0; return 0;
} }
@ -4483,6 +4723,52 @@ static void netdev_qos_map_frame_event(const struct mmpdu_header *hdr,
netdev_send_qos_map_set(netdev, body + 4, body_len - 4); netdev_send_qos_map_set(netdev, body + 4, body_len - 4);
} }
static void netdev_sae_external_auth_frame_event(const struct mmpdu_header *hdr,
const void *body, size_t body_len,
int rssi, void *user_data)
{
struct netdev *netdev = user_data;
const struct mmpdu_authentication *auth;
uint16_t status_code = MMPDU_STATUS_CODE_UNSPECIFIED;
int ret;
if (!netdev->external_auth)
return;
if (!netdev->ap)
return;
auth = mmpdu_body(hdr);
/*
* Allows station to persist settings so it does not retry
* the higher order ECC group again
*/
if (L_CPU_TO_LE16(auth->status) ==
MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP &&
netdev->event_filter)
netdev->event_filter(netdev, NETDEV_EVENT_ECC_GROUP_RETRY,
NULL, netdev->user_data);
ret = auth_proto_rx_authenticate(netdev->ap, (const void *) hdr,
mmpdu_header_len(hdr) + body_len);
switch (ret) {
case 0:
case -EAGAIN:
return;
case -ENOMSG:
case -EBADMSG:
return;
default:
break;
}
if (ret > 0)
status_code = (uint16_t)ret;
netdev_send_external_auth(netdev, status_code);
}
static void netdev_preauth_cb(const uint8_t *pmk, void *user_data) static void netdev_preauth_cb(const uint8_t *pmk, void *user_data)
{ {
struct netdev_preauth_state *preauth = user_data; struct netdev_preauth_state *preauth = user_data;
@ -5307,6 +5593,58 @@ static void netdev_control_port_frame_event(struct l_genl_msg *msg,
frame, frame_len, unencrypted); frame, frame_len, unencrypted);
} }
static void netdev_external_auth_event(struct l_genl_msg *msg,
struct netdev *netdev)
{
const uint8_t *bssid;
struct iovec ssid;
uint32_t akm;
uint32_t action;
struct handshake_state *hs = netdev->handshake;
if (L_WARN_ON(nl80211_parse_attrs(msg, NL80211_ATTR_AKM_SUITES, &akm,
NL80211_ATTR_EXTERNAL_AUTH_ACTION, &action,
NL80211_ATTR_BSSID, &bssid,
NL80211_ATTR_SSID, &ssid,
NL80211_ATTR_UNSPEC) < 0))
return;
if (!L_IN_SET(action, NL80211_EXTERNAL_AUTH_START,
NL80211_EXTERNAL_AUTH_ABORT))
return;
/* kernel sends SAE_SHA256 AKM in BE order for legacy reasons */
if (!L_IN_SET(akm, CRYPTO_AKM_SAE_SHA256, CRYPTO_AKM_FT_OVER_SAE_SHA256,
L_CPU_TO_BE32(CRYPTO_AKM_SAE_SHA256))) {
l_warn("Unknown AKM: %08x", akm);
return;
}
if (action == NL80211_EXTERNAL_AUTH_ABORT) {
l_warn("External Auth Aborted");
goto error;
}
if (hs->ssid_len != ssid.iov_len ||
memcmp(hs->ssid, ssid.iov_base, hs->ssid_len)) {
l_warn("Target SSID mismatch");
goto error;
}
if (memcmp(hs->aa, bssid, ETH_ALEN)) {
l_warn("Target BSSID mismatch");
goto error;
}
if (auth_proto_start(netdev->ap)) {
netdev->external_auth = true;
return;
}
error:
netdev_send_external_auth(netdev, MMPDU_STATUS_CODE_UNSPECIFIED);
}
static void netdev_unicast_notify(struct l_genl_msg *msg, void *user_data) static void netdev_unicast_notify(struct l_genl_msg *msg, void *user_data)
{ {
struct netdev *netdev = NULL; struct netdev *netdev = NULL;
@ -5344,6 +5682,9 @@ static void netdev_unicast_notify(struct l_genl_msg *msg, void *user_data)
case NL80211_CMD_CONTROL_PORT_FRAME: case NL80211_CMD_CONTROL_PORT_FRAME:
netdev_control_port_frame_event(msg, netdev); netdev_control_port_frame_event(msg, netdev);
break; break;
case NL80211_CMD_EXTERNAL_AUTH:
netdev_external_auth_event(msg, netdev);
break;
} }
} }
@ -5518,33 +5859,41 @@ static void netdev_add_station_frame_watches(struct netdev *netdev)
static const uint8_t action_ft_response_prefix[] = { 0x06, 0x02 }; static const uint8_t action_ft_response_prefix[] = { 0x06, 0x02 };
static const uint8_t auth_ft_response_prefix[] = { 0x02, 0x00 }; static const uint8_t auth_ft_response_prefix[] = { 0x02, 0x00 };
static const uint8_t action_qos_map_prefix[] = { 0x01, 0x04 }; static const uint8_t action_qos_map_prefix[] = { 0x01, 0x04 };
static const uint8_t auth_sae_prefix[] = { 0x03, 0x00 };
uint64_t wdev = netdev->wdev_id; uint64_t wdev = netdev->wdev_id;
/* Subscribe to Management -> Action -> RM -> Neighbor Report frames */ /* Subscribe to Management -> Action -> RM -> Neighbor Report frames */
frame_watch_add(wdev, 0, 0x00d0, action_neighbor_report_prefix, frame_watch_add(wdev, 0, 0x00d0, action_neighbor_report_prefix,
sizeof(action_neighbor_report_prefix), sizeof(action_neighbor_report_prefix), false,
netdev_neighbor_report_frame_event, netdev, NULL); netdev_neighbor_report_frame_event, netdev, NULL);
frame_watch_add(wdev, 0, 0x00d0, action_sa_query_resp_prefix, frame_watch_add(wdev, 0, 0x00d0, action_sa_query_resp_prefix,
sizeof(action_sa_query_resp_prefix), sizeof(action_sa_query_resp_prefix), false,
netdev_sa_query_resp_frame_event, netdev, NULL); netdev_sa_query_resp_frame_event, netdev, NULL);
frame_watch_add(wdev, 0, 0x00d0, action_sa_query_req_prefix, frame_watch_add(wdev, 0, 0x00d0, action_sa_query_req_prefix,
sizeof(action_sa_query_req_prefix), sizeof(action_sa_query_req_prefix), false,
netdev_sa_query_req_frame_event, netdev, NULL); netdev_sa_query_req_frame_event, netdev, NULL);
frame_watch_add(wdev, 0, 0x00d0, action_ft_response_prefix, frame_watch_add(wdev, 0, 0x00d0, action_ft_response_prefix,
sizeof(action_ft_response_prefix), sizeof(action_ft_response_prefix), false,
netdev_ft_response_frame_event, netdev, NULL); netdev_ft_response_frame_event, netdev, NULL);
frame_watch_add(wdev, 0, 0x00b0, auth_ft_response_prefix, frame_watch_add(wdev, 0, 0x00b0, auth_ft_response_prefix,
sizeof(auth_ft_response_prefix), sizeof(auth_ft_response_prefix), false,
netdev_ft_auth_response_frame_event, netdev, NULL); netdev_ft_auth_response_frame_event, netdev, NULL);
if (wiphy_supports_qos_set_map(netdev->wiphy)) if (wiphy_supports_qos_set_map(netdev->wiphy))
frame_watch_add(wdev, 0, 0x00d0, action_qos_map_prefix, frame_watch_add(wdev, 0, 0x00d0, action_qos_map_prefix,
sizeof(action_qos_map_prefix), sizeof(action_qos_map_prefix), false,
netdev_qos_map_frame_event, netdev, NULL); netdev_qos_map_frame_event, netdev, NULL);
if (!wiphy_supports_cmds_auth_assoc(netdev->wiphy) &&
wiphy_has_feature(netdev->wiphy, NL80211_FEATURE_SAE))
frame_watch_add(wdev, 0, 0x00b0,
auth_sae_prefix, sizeof(auth_sae_prefix),
false, netdev_sae_external_auth_frame_event,
netdev, NULL);
} }
static void netdev_setup_interface(struct netdev *netdev) static void netdev_setup_interface(struct netdev *netdev)
@ -6292,6 +6641,16 @@ struct netdev *netdev_create_from_genl(struct l_genl_msg *msg,
netdev_get_link(netdev); netdev_get_link(netdev);
/*
* Call the netdev-specific variant to flush only this devices PMKSA
* cache in the kernel. This will make IWD's cache and the kernel's
* cache consistent, i.e. no entries
*
* TODO: If we ever are storing PMKSA's on disk we would first need to
* flush, then add all the PMKSA entries at this time.
*/
netdev_flush_pmksa(netdev);
return netdev; return netdev;
} }
@ -6407,6 +6766,10 @@ static int netdev_init(void)
__ft_set_tx_frame_func(netdev_tx_ft_frame); __ft_set_tx_frame_func(netdev_tx_ft_frame);
__pmksa_set_driver_callbacks(netdev_pmksa_driver_add,
netdev_pmksa_driver_remove,
netdev_pmksa_driver_flush);
unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME, unicast_watch = l_genl_add_unicast_watch(genl, NL80211_GENL_NAME,
netdev_unicast_notify, netdev_unicast_notify,
NULL, NULL); NULL, NULL);

View File

@ -158,6 +158,7 @@ const char *netdev_get_name(struct netdev *netdev);
bool netdev_get_is_up(struct netdev *netdev); bool netdev_get_is_up(struct netdev *netdev);
const char *netdev_get_path(struct netdev *netdev); const char *netdev_get_path(struct netdev *netdev);
uint8_t netdev_get_rssi_level_idx(struct netdev *netdev); uint8_t netdev_get_rssi_level_idx(struct netdev *netdev);
int netdev_get_low_signal_threshold(uint32_t frequency);
struct handshake_state *netdev_handshake_state_new(struct netdev *netdev); struct handshake_state *netdev_handshake_state_new(struct netdev *netdev);
struct handshake_state *netdev_get_handshake(struct netdev *netdev); struct handshake_state *netdev_get_handshake(struct netdev *netdev);

View File

@ -56,6 +56,7 @@
#include "src/erp.h" #include "src/erp.h"
#include "src/handshake.h" #include "src/handshake.h"
#include "src/band.h" #include "src/band.h"
#include "src/util.h"
#define SAE_PT_SETTING "SAE-PT-Group%u" #define SAE_PT_SETTING "SAE-PT-Group%u"
@ -212,31 +213,10 @@ void network_disconnected(struct network *network)
station_hide_network(network->station, network); station_hide_network(network->station, network);
} }
/* First 64 entries calculated by 1 / pow(n, 0.3) for n >= 1 */
static const double rankmod_table[] = {
1.0000000000, 0.8122523964, 0.7192230933, 0.6597539554,
0.6170338627, 0.5841906811, 0.5577898253, 0.5358867313,
0.5172818580, 0.5011872336, 0.4870596972, 0.4745102806,
0.4632516708, 0.4530661223, 0.4437850034, 0.4352752816,
0.4274303178, 0.4201634287, 0.4134032816, 0.4070905315,
0.4011753236, 0.3956154062, 0.3903746872, 0.3854221125,
0.3807307877, 0.3762772797, 0.3720410580, 0.3680040435,
0.3641502401, 0.3604654325, 0.3569369365, 0.3535533906,
0.3503045821, 0.3471812999, 0.3441752105, 0.3412787518,
0.3384850430, 0.3357878061, 0.3331812996, 0.3306602598,
0.3282198502, 0.3258556179, 0.3235634544, 0.3213395618,
0.3191804229, 0.3170827751, 0.3150435863, 0.3130600345,
0.3111294892, 0.3092494947, 0.3074177553, 0.3056321221,
0.3038905808, 0.3021912409, 0.3005323264, 0.2989121662,
0.2973291870, 0.2957819051, 0.2942689208, 0.2927889114,
0.2913406263, 0.2899228820, 0.2885345572, 0.2871745887,
};
bool network_rankmod(const struct network *network, double *rankmod) bool network_rankmod(const struct network *network, double *rankmod)
{ {
struct network_info *info = network->info; struct network_info *info = network->info;
int n; int n;
int nmax;
/* /*
* Current policy is that only networks successfully connected * Current policy is that only networks successfully connected
@ -250,12 +230,7 @@ bool network_rankmod(const struct network *network, double *rankmod)
if (n < 0) if (n < 0)
return false; return false;
nmax = L_ARRAY_SIZE(rankmod_table); *rankmod = util_exponential_decay(n);
if (n >= nmax)
n = nmax - 1;
*rankmod = rankmod_table[n];
return true; return true;
} }
@ -911,6 +886,9 @@ int network_can_connect_bss(struct network *network, const struct scan_bss *bss)
return ret; return ret;
} }
if (IE_AKM_IS_OWE(rsn.akm_suites) && wiphy_owe_disabled(wiphy))
return -EPERM;
if (!config || !config->have_transition_disable) { if (!config || !config->have_transition_disable) {
if (band == BAND_FREQ_6_GHZ) if (band == BAND_FREQ_6_GHZ)
goto mfp_no_tkip; goto mfp_no_tkip;
@ -1278,6 +1256,7 @@ struct scan_bss *network_bss_select(struct network *network,
struct l_queue *bss_list = network->bss_list; struct l_queue *bss_list = network->bss_list;
const struct l_queue_entry *bss_entry; const struct l_queue_entry *bss_entry;
struct scan_bss *candidate = NULL; struct scan_bss *candidate = NULL;
bool skipped_open = false;
for (bss_entry = l_queue_get_entries(bss_list); bss_entry; for (bss_entry = l_queue_get_entries(bss_list); bss_entry;
bss_entry = bss_entry->next) { bss_entry = bss_entry->next) {
@ -1297,30 +1276,35 @@ struct scan_bss *network_bss_select(struct network *network,
if (!candidate) if (!candidate)
candidate = bss; candidate = bss;
/* check if temporarily blacklisted */
if (l_queue_find(network->blacklist, match_bss, bss))
continue;
if (blacklist_contains_bss(bss->addr,
BLACKLIST_REASON_CONNECT_FAILED))
continue;
/* OWE Transition BSS */ /* OWE Transition BSS */
if (bss->owe_trans) { if (bss->owe_trans) {
/* Don't want to connect to the Open BSS if possible */ /* Don't want to connect to the Open BSS if possible */
if (!bss->rsne) if (!bss->rsne) {
skipped_open = true;
continue; continue;
}
/* Candidate is not OWE, set this as new candidate */ /* Candidate is not OWE, set this as new candidate */
if (!(candidate->owe_trans && candidate->rsne)) if (!(candidate->owe_trans && candidate->rsne))
candidate = bss; candidate = bss;
} }
/* check if temporarily blacklisted */ return bss;
if (l_queue_find(network->blacklist, match_bss, bss))
continue;
if (!blacklist_contains_bss(bss->addr))
return bss;
} }
/* /*
* No BSS was found, but if we are falling back to blacklisted BSS's we * No BSS was found, but if we are falling back to blacklisted BSS's we
* can just use the first connectable candidate found above. * can just use the first connectable candidate found above.
*/ */
if (fallback_to_blacklist) if (fallback_to_blacklist || skipped_open)
return candidate; return candidate;
return NULL; return NULL;
@ -2010,10 +1994,8 @@ void network_rank_update(struct network *network, bool connected)
L_WARN_ON(n < 0); L_WARN_ON(n < 0);
if (n >= (int) L_ARRAY_SIZE(rankmod_table)) network->rank =
n = L_ARRAY_SIZE(rankmod_table) - 1; util_exponential_decay(n) * best_bss->rank + USHRT_MAX;
network->rank = rankmod_table[n] * best_bss->rank + USHRT_MAX;
} else } else
network->rank = best_bss->rank; network->rank = best_bss->rank;

View File

@ -648,7 +648,9 @@ struct l_genl_msg *nl80211_build_cmd_frame(uint32_t ifindex,
msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + 512); msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + 512);
l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex); l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &freq);
if (freq)
l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &freq);
l_genl_msg_append_attrv(msg, NL80211_ATTR_FRAME, iovs, iov_len + 1); l_genl_msg_append_attrv(msg, NL80211_ATTR_FRAME, iovs, iov_len + 1);
return msg; return msg;
@ -695,8 +697,7 @@ int nl80211_parse_chandef(struct l_genl_msg *msg, struct band_chandef *out)
int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs, int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
struct scan_freq_set *supported_list, struct scan_freq_set *supported_list,
struct band_freq_attrs *list, struct band *band)
size_t num_channels)
{ {
uint16_t type, len; uint16_t type, len;
const void *data; const void *data;
@ -710,6 +711,7 @@ int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
while (l_genl_attr_next(&nested, NULL, NULL, NULL)) { while (l_genl_attr_next(&nested, NULL, NULL, NULL)) {
uint32_t freq = 0; uint32_t freq = 0;
struct band_freq_attrs freq_attr = { 0 }; struct band_freq_attrs freq_attr = { 0 };
enum band_freq out_band;
if (!l_genl_attr_recurse(&nested, &attr)) if (!l_genl_attr_recurse(&nested, &attr))
continue; continue;
@ -750,17 +752,20 @@ int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
if (!freq) if (!freq)
continue; continue;
channel = band_freq_to_channel(freq, NULL); channel = band_freq_to_channel(freq, &out_band);
if (!channel) if (!channel)
continue; continue;
if (L_WARN_ON(channel > num_channels)) if (L_WARN_ON(out_band != band->freq))
continue;
if (L_WARN_ON(channel > band->freqs_len))
continue; continue;
if (supported_list) if (supported_list)
scan_freq_set_add(supported_list, freq); scan_freq_set_add(supported_list, freq);
list[channel] = freq_attr; band->freq_attrs[channel] = freq_attr;
} }
return 0; return 0;

View File

@ -26,6 +26,7 @@ struct band_chandef;
struct scan_freq_set; struct scan_freq_set;
struct band_freq_attrs; struct band_freq_attrs;
struct handshake_state; struct handshake_state;
struct band;
int nl80211_parse_attrs(struct l_genl_msg *msg, int tag, ...); int nl80211_parse_attrs(struct l_genl_msg *msg, int tag, ...);
int nl80211_parse_nested(struct l_genl_attr *attr, int type, int tag, ...); int nl80211_parse_nested(struct l_genl_attr *attr, int type, int tag, ...);
@ -95,8 +96,7 @@ struct l_genl_msg *nl80211_build_external_auth(uint32_t ifindex,
int nl80211_parse_chandef(struct l_genl_msg *msg, struct band_chandef *out); int nl80211_parse_chandef(struct l_genl_msg *msg, struct band_chandef *out);
int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs, int nl80211_parse_supported_frequencies(struct l_genl_attr *band_freqs,
struct scan_freq_set *supported_list, struct scan_freq_set *supported_list,
struct band_freq_attrs *list, struct band *band);
size_t num_channels);
void nl80211_append_rsn_attributes(struct l_genl_msg *msg, void nl80211_append_rsn_attributes(struct l_genl_msg *msg,
struct handshake_state *hs); struct handshake_state *hs);

View File

@ -293,7 +293,7 @@ static void offchannel_mlme_notify(struct l_genl_msg *msg, void *user_data)
* - an event coming from an external ROC request (we just * - an event coming from an external ROC request (we just
* happened to have also sent an ROC request). * happened to have also sent an ROC request).
* *
* We can't tell where the event originated until we recieve our * We can't tell where the event originated until we receive our
* ACK so set early_cookie to track it. * ACK so set early_cookie to track it.
*/ */
if (i->roc_cmd_id != 0 && l_genl_family_request_sent(nl80211, if (i->roc_cmd_id != 0 && l_genl_family_request_sent(nl80211,

View File

@ -1497,7 +1497,7 @@ static void p2p_handshake_event(struct handshake_state *hs,
static void p2p_try_connect_group(struct p2p_device *dev) static void p2p_try_connect_group(struct p2p_device *dev)
{ {
struct scan_bss *bss = dev->conn_wsc_bss; struct scan_bss *bss = dev->conn_wsc_bss;
_auto_(handshake_state_free) struct handshake_state *hs = NULL; _auto_(handshake_state_unref) struct handshake_state *hs = NULL;
struct iovec ie_iov[16]; struct iovec ie_iov[16];
int ie_num = 0; int ie_num = 0;
int r; int r;
@ -2217,7 +2217,7 @@ static bool p2p_go_negotiation_confirm_cb(const struct mmpdu_header *mpdu,
/* /*
* Start setting the group up right away and we'll add the * Start setting the group up right away and we'll add the
* client's Configuation Timeout to the WSC start timeout's * client's Configuration Timeout to the WSC start timeout's
* value. * value.
*/ */
p2p_device_interface_create(dev); p2p_device_interface_create(dev);
@ -2549,7 +2549,7 @@ static void p2p_go_negotiation_confirm_done(int error, void *user_data)
/* /*
* Frame was ACKed. On the GO start setting the group up right * Frame was ACKed. On the GO start setting the group up right
* away and we'll add the client's Configuation Timeout to the * away and we'll add the client's Configuration Timeout to the
* WSC start timeout's value. On the client wait idly the * WSC start timeout's value. On the client wait idly the
* maximum amount of time indicated by the peer in the GO * maximum amount of time indicated by the peer in the GO
* Negotiation Response's Configuration Timeout attribute and * Negotiation Response's Configuration Timeout attribute and
@ -2951,7 +2951,7 @@ static bool p2p_provision_disc_resp_cb(const struct mmpdu_header *mpdu,
} }
/* /*
* Indended P2P Interface address is optional, we don't have the * Intended P2P Interface address is optional, we don't have the
* BSSID of the group here. * BSSID of the group here.
* *
* We might want to make sure that Group Formation is false but the * We might want to make sure that Group Formation is false but the
@ -4163,10 +4163,11 @@ static void p2p_device_discovery_start(struct p2p_device *dev)
L_ARRAY_SIZE(channels_social)]; L_ARRAY_SIZE(channels_social)];
frame_watch_add(dev->wdev_id, FRAME_GROUP_P2P_LISTEN, 0x0040, frame_watch_add(dev->wdev_id, FRAME_GROUP_P2P_LISTEN, 0x0040,
(uint8_t *) "", 0, p2p_device_probe_cb, dev, NULL); (uint8_t *) "", 0, false,
p2p_device_probe_cb, dev, NULL);
frame_watch_add(dev->wdev_id, FRAME_GROUP_P2P_LISTEN, 0x00d0, frame_watch_add(dev->wdev_id, FRAME_GROUP_P2P_LISTEN, 0x00d0,
p2p_frame_go_neg_req.data, p2p_frame_go_neg_req.len, p2p_frame_go_neg_req.data, p2p_frame_go_neg_req.len,
p2p_device_go_negotiation_req_cb, dev, NULL); false, p2p_device_go_negotiation_req_cb, dev, NULL);
p2p_device_scan_start(dev); p2p_device_scan_start(dev);
} }

269
src/pmksa.c Normal file
View File

@ -0,0 +1,269 @@
/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2023 Cruise LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <stdint.h>
#include <errno.h>
#include <ell/ell.h>
#include "ell/useful.h"
#include "src/module.h"
#include "src/pmksa.h"
#define PMKID "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
#define PMKID_STR(x) x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], \
x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
static uint64_t dot11RSNAConfigPMKLifetime = 43200ULL * L_USEC_PER_SEC;
static uint32_t pmksa_cache_capacity = 255;
static pmksa_cache_add_func_t driver_add;
static pmksa_cache_remove_func_t driver_remove;
static pmksa_cache_flush_func_t driver_flush;
struct min_heap {
struct pmksa **data;
uint32_t capacity;
uint32_t used;
};
static struct min_heap cache;
static __always_inline void swap_ptr(void *l, void *r)
{
struct pmksa **lp = l;
struct pmksa **rp = r;
SWAP(*lp, *rp);
}
static __always_inline
bool pmksa_compare_expiration(const void *l, const void *r)
{
const struct pmksa * const *lp = l;
const struct pmksa * const *rp = r;
return (*lp)->expiration < (*rp)->expiration;
}
static struct l_minheap_ops ops = {
.elem_size = sizeof(struct pmksa *),
.swap = swap_ptr,
.less = pmksa_compare_expiration,
};
static int pmksa_cache_find(const uint8_t spa[static 6],
const uint8_t aa[static 6],
const uint8_t *ssid, size_t ssid_len,
uint32_t akm)
{
unsigned int i;
for (i = 0; i < cache.used; i++) {
struct pmksa *pmksa = cache.data[i];
if (memcmp(pmksa->spa, spa, sizeof(pmksa->spa)))
continue;
if (memcmp(pmksa->aa, aa, sizeof(pmksa->aa)))
continue;
if (ssid_len != pmksa->ssid_len)
continue;
if (memcmp(pmksa->ssid, ssid, ssid_len))
continue;
if (akm & pmksa->akm)
return i;
}
return -ENOENT;
}
/*
* Try to obtain a PMKSA entry from the cache. If the the entry matching
* the parameters is found, it is removed from the cache and returned to the
* caller. The caller is responsible for managing the returned pmksa
* structure
*/
struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
const uint8_t aa[static 6],
const uint8_t *ssid, size_t ssid_len,
uint32_t akm)
{
struct pmksa *pmksa;
int r = pmksa_cache_find(spa, aa, ssid, ssid_len, akm);
if (r < 0)
return NULL;
cache.used -= 1;
if ((uint32_t) r == cache.used)
goto done;
SWAP(cache.data[r], cache.data[cache.used]);
__minheap_sift_down(cache.data, cache.used, r, &ops);
done:
pmksa = cache.data[cache.used];
l_debug("Returning entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
return pmksa;
}
/*
* Put a PMKSA into the cache. It will be sorted in soonest-to-expire order.
* If the cache is full, then soonest-to-expire entry is removed first.
*/
int pmksa_cache_put(struct pmksa *pmksa)
{
l_debug("Adding entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
if (cache.used == cache.capacity) {
pmksa_cache_free(cache.data[0]);
cache.data[0] = pmksa;
__minheap_sift_down(cache.data, cache.used, 0, &ops);
return 0;
}
cache.data[cache.used] = pmksa;
__minheap_sift_up(cache.data, cache.used, &ops);
cache.used += 1;
if (driver_add)
driver_add(pmksa);
return 0;
}
/*
* Expire all PMKSA entries with expiration time smaller or equal to the cutoff
* time.
*/
int pmksa_cache_expire(uint64_t cutoff)
{
int i;
int used = cache.used;
int remaining = 0;
for (i = 0; i < used; i++) {
if (cache.data[i]->expiration <= cutoff) {
pmksa_cache_free(cache.data[i]);
continue;
}
cache.data[remaining] = cache.data[i];
remaining += 1;
}
cache.used = remaining;
for (i = cache.used >> 1; i >= 0; i--)
__minheap_sift_down(cache.data, cache.used, i, &ops);
return used - remaining;
}
/*
* Flush all PMKSA entries from the cache, regardless of expiration time.
*/
int pmksa_cache_flush(void)
{
uint32_t i;
/*
* The driver flush operation is done via a single kernel API call which
* is why below we use l_free instead of pmksa_cache_free as to not
* induce a DEL_PMKSA kernel call for each entry.
*/
if (driver_flush)
driver_flush();
for (i = 0; i < cache.used; i++)
l_free(cache.data[i]);
memset(cache.data, 0, cache.capacity * sizeof(struct pmksa *));
cache.used = 0;
return 0;
}
int pmksa_cache_free(struct pmksa *pmksa)
{
if (driver_remove)
driver_remove(pmksa);
l_free(pmksa);
return 0;
}
struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries)
{
if (out_n_entries)
*out_n_entries = cache.used;
return cache.data;
}
uint64_t pmksa_lifetime(void)
{
return dot11RSNAConfigPMKLifetime;
}
void __pmksa_set_config(const struct l_settings *config)
{
l_settings_get_uint(config, "PMKSA", "Capacity",
&pmksa_cache_capacity);
}
void __pmksa_set_driver_callbacks(pmksa_cache_add_func_t add,
pmksa_cache_remove_func_t remove,
pmksa_cache_flush_func_t flush)
{
driver_add = add;
driver_remove = remove;
driver_flush = flush;
}
static int pmksa_init(void)
{
cache.capacity = pmksa_cache_capacity;
cache.used = 0;
cache.data = l_new(struct pmksa *, cache.capacity);
return 0;
}
static void pmksa_exit(void)
{
pmksa_cache_flush();
l_free(cache.data);
}
IWD_MODULE(pmksa, pmksa_init, pmksa_exit);

55
src/pmksa.h Normal file
View File

@ -0,0 +1,55 @@
/*
*
* Wireless daemon for Linux
*
* Copyright (C) 2023 Cruise, LLC. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
struct pmksa {
uint64_t expiration;
uint8_t spa[6];
uint8_t aa[6];
uint8_t ssid[32];
size_t ssid_len;
uint32_t akm;
uint8_t pmkid[16];
uint8_t pmk[64];
size_t pmk_len;
};
typedef void (*pmksa_cache_add_func_t)(const struct pmksa *pmksa);
typedef void (*pmksa_cache_remove_func_t)(const struct pmksa *pmksa);
typedef void (*pmksa_cache_flush_func_t)(void);
struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries);
struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
const uint8_t aa[static 6],
const uint8_t *ssid, size_t ssid_len,
uint32_t akm);
int pmksa_cache_put(struct pmksa *pmksa);
int pmksa_cache_expire(uint64_t cutoff);
int pmksa_cache_flush(void);
int pmksa_cache_free(struct pmksa *pmksa);
uint64_t pmksa_lifetime(void);
void __pmksa_set_config(const struct l_settings *config);
void __pmksa_set_driver_callbacks(pmksa_cache_add_func_t add,
pmksa_cache_remove_func_t remove,
pmksa_cache_flush_func_t flush);

View File

@ -798,7 +798,7 @@ static void rrm_add_frame_watches(struct rrm_state *rrm)
l_debug(""); l_debug("");
frame_watch_add(rrm->wdev_id, 0, frame_type, prefix, sizeof(prefix), frame_watch_add(rrm->wdev_id, 0, frame_type, prefix, sizeof(prefix),
rrm_frame_watch_cb, rrm, NULL); false, rrm_frame_watch_cb, rrm, NULL);
} }
static struct rrm_state *rrm_new_state(struct netdev *netdev) static struct rrm_state *rrm_new_state(struct netdev *netdev)

View File

@ -258,7 +258,7 @@ static struct l_ecc_scalar *sae_pwd_value(const struct l_ecc_curve *curve,
is_in_range = util_secure_fill_with_msb(is_in_range); is_in_range = util_secure_fill_with_msb(is_in_range);
/* /*
* libell has public Legendre symbol only for l_ecc_scalar, but they * ELL has public Legendre symbol only for l_ecc_scalar, but they
* cannot be created if the coordinate is greater than the p. Hence, * cannot be created if the coordinate is greater than the p. Hence,
* to avoid control flow dependencies, we replace pwd_value by a dummy * to avoid control flow dependencies, we replace pwd_value by a dummy
* quadratic non residue if we generate a value >= prime. * quadratic non residue if we generate a value >= prime.
@ -1550,6 +1550,26 @@ struct auth_proto *sae_sm_new(struct handshake_state *hs,
return &sm->ap; return &sm->ap;
} }
bool sae_sm_force_hunt_and_peck(struct auth_proto *ap)
{
struct sae_sm *sm = l_container_of(ap, struct sae_sm, ap);
sae_debug("Forcing SAE Hunting and Pecking");
sm->sae_type = CRYPTO_SAE_LOOPING;
return true;
}
bool sae_sm_force_default_group(struct auth_proto *ap)
{
struct sae_sm *sm = l_container_of(ap, struct sae_sm, ap);
sae_debug("Forcing Default Group");
sm->force_default_group = true;
return true;
}
static int sae_init(void) static int sae_init(void)
{ {
if (getenv("IWD_SAE_DEBUG")) if (getenv("IWD_SAE_DEBUG"))

View File

@ -34,3 +34,6 @@ struct auth_proto *sae_sm_new(struct handshake_state *hs,
sae_tx_authenticate_func_t tx_auth, sae_tx_authenticate_func_t tx_auth,
sae_tx_associate_func_t tx_assoc, sae_tx_associate_func_t tx_assoc,
void *user_data); void *user_data);
bool sae_sm_force_hunt_and_peck(struct auth_proto *ap);
bool sae_sm_force_default_group(struct auth_proto *ap);

View File

@ -56,6 +56,8 @@
static double RANK_2G_FACTOR; static double RANK_2G_FACTOR;
static double RANK_5G_FACTOR; static double RANK_5G_FACTOR;
static double RANK_6G_FACTOR; static double RANK_6G_FACTOR;
static uint32_t RANK_HIGH_UTILIZATION;
static uint32_t RANK_HIGH_STATION_COUNT;
static uint32_t SCAN_MAX_INTERVAL; static uint32_t SCAN_MAX_INTERVAL;
static uint32_t SCAN_INIT_INTERVAL; static uint32_t SCAN_INIT_INTERVAL;
@ -141,9 +143,9 @@ struct scan_survey {
}; };
struct scan_survey_results { struct scan_survey_results {
struct scan_survey survey_2_4[14]; struct scan_survey survey_2_4[15];
struct scan_survey survey_5[196]; struct scan_survey survey_5[197];
struct scan_survey survey_6[233]; struct scan_survey survey_6[234];
}; };
struct scan_results { struct scan_results {
@ -1343,8 +1345,8 @@ static bool scan_parse_bss_information_elements(struct scan_bss *bss,
iter.len + 2); iter.len + 2);
break; break;
case IE_TYPE_BSS_LOAD: case IE_TYPE_BSS_LOAD:
if (ie_parse_bss_load(&iter, NULL, &bss->utilization, if (ie_parse_bss_load(&iter, &bss->sta_count,
NULL) < 0) &bss->utilization, NULL) < 0)
l_warn("Unable to parse BSS Load IE for " l_warn("Unable to parse BSS Load IE for "
MAC, MAC_STR(bss->addr)); MAC, MAC_STR(bss->addr));
else else
@ -1681,10 +1683,77 @@ static struct scan_bss *scan_parse_result(struct l_genl_msg *msg,
return bss; return bss;
} }
static void scan_bss_compute_rank(struct scan_bss *bss) static double scan_legacy_utilization_factor(uint8_t utilization)
{ {
static const double RANK_HIGH_UTILIZATION_FACTOR = 0.8; static const double RANK_HIGH_UTILIZATION_FACTOR = 0.8;
static const double RANK_LOW_UTILIZATION_FACTOR = 1.2; static const double RANK_LOW_UTILIZATION_FACTOR = 1.2;
if (utilization >= 192)
return RANK_HIGH_UTILIZATION_FACTOR;
else if (utilization <= 63)
return RANK_LOW_UTILIZATION_FACTOR;
return 1.0;
}
static double scan_get_load_factors(uint8_t utilization, uint16_t sta_count)
{
double n;
double factor = 1.0;
/*
* The exponential decay table has 64 entries (0 <= n <= 63) which range
* from 1.0 to 0.28. For the utilization and station count factors we
* likely don't want to adjust the rank so drastically (potentially a
* 78% reduction in the worse case) so cap the index at 30 which equates
* to ~64% at the worst case.
*/
static const uint8_t LOAD_DECAY_OFFSET = 30;
if (!RANK_HIGH_UTILIZATION) {
/*
* To maintain existing behavior, if the utilization factor is
* unset (default) fall back to the static thresholds and
* factor weights.
*/
factor = scan_legacy_utilization_factor(utilization);
goto sta_count;
} else if (utilization < RANK_HIGH_UTILIZATION)
goto sta_count;
/* Map the utilization threshold -> 255 to rankmod_table indexes */
if (L_WARN_ON(!util_linear_map(utilization, RANK_HIGH_UTILIZATION,
255, 0, LOAD_DECAY_OFFSET, &n)))
goto sta_count;
factor = util_exponential_decay(n);
sta_count:
if (!RANK_HIGH_STATION_COUNT || sta_count < RANK_HIGH_STATION_COUNT)
return factor;
/*
* The station count is a uint16 so in theory it could be excessively
* large. In practice APs generally can't handle anywhere near this so
* put a cap at 255. If at some time in the future APs begin to handle
* this level of capacity we could increase this.
*
* TODO: A warning is used here to make this visible. If we see cases
* where this is happening we may need to give this another look.
*/
if (L_WARN_ON(sta_count > 255))
sta_count = 255;
if (L_WARN_ON(!util_linear_map(sta_count, RANK_HIGH_STATION_COUNT,
255, 0, LOAD_DECAY_OFFSET, &n)))
return factor;
factor *= util_exponential_decay(n);
return factor;
}
static void scan_bss_compute_rank(struct scan_bss *bss)
{
static const double RANK_HIGH_SNR_FACTOR = 1.2; static const double RANK_HIGH_SNR_FACTOR = 1.2;
static const double RANK_LOW_SNR_FACTOR = 0.8; static const double RANK_LOW_SNR_FACTOR = 0.8;
double rank; double rank;
@ -1708,12 +1777,8 @@ static void scan_bss_compute_rank(struct scan_bss *bss)
rank *= RANK_6G_FACTOR; rank *= RANK_6G_FACTOR;
/* Rank loaded APs lower and lightly loaded APs higher */ /* Rank loaded APs lower and lightly loaded APs higher */
if (bss->have_utilization) { if (bss->have_utilization)
if (bss->utilization >= 192) rank *= scan_get_load_factors(bss->utilization, bss->sta_count);
rank *= RANK_HIGH_UTILIZATION_FACTOR;
else if (bss->utilization <= 63)
rank *= RANK_LOW_UTILIZATION_FACTOR;
}
if (bss->have_snr) { if (bss->have_snr) {
if (bss->snr <= 15) if (bss->snr <= 15)
@ -2599,6 +2664,20 @@ static int scan_init(void)
if (SCAN_MAX_INTERVAL > UINT16_MAX) if (SCAN_MAX_INTERVAL > UINT16_MAX)
SCAN_MAX_INTERVAL = UINT16_MAX; SCAN_MAX_INTERVAL = UINT16_MAX;
if (!l_settings_get_uint(config, "Rank", "HighUtilizationThreshold",
&RANK_HIGH_UTILIZATION))
RANK_HIGH_UTILIZATION = 0;
if (L_WARN_ON(RANK_HIGH_UTILIZATION > 255))
RANK_HIGH_UTILIZATION = 255;
if (!l_settings_get_uint(config, "Rank", "HighStationCountThreshold",
&RANK_HIGH_STATION_COUNT))
RANK_HIGH_STATION_COUNT = 0;
if (L_WARN_ON(RANK_HIGH_STATION_COUNT > 255))
RANK_HIGH_STATION_COUNT = 255;
return 0; return 0;
} }

View File

@ -67,6 +67,7 @@ struct scan_bss {
uint8_t ssid[SSID_MAX_SIZE]; uint8_t ssid[SSID_MAX_SIZE];
uint8_t ssid_len; uint8_t ssid_len;
uint8_t utilization; uint8_t utilization;
uint16_t sta_count;
uint8_t cc[3]; uint8_t cc[3];
uint16_t rank; uint16_t rank;
uint64_t time_stamp; uint64_t time_stamp;

View File

@ -63,6 +63,7 @@
#include "src/eap.h" #include "src/eap.h"
#include "src/eap-tls-common.h" #include "src/eap-tls-common.h"
#include "src/storage.h" #include "src/storage.h"
#include "src/pmksa.h"
#define STATION_RECENT_NETWORK_LIMIT 5 #define STATION_RECENT_NETWORK_LIMIT 5
#define STATION_RECENT_FREQS_LIMIT 5 #define STATION_RECENT_FREQS_LIMIT 5
@ -74,6 +75,11 @@ static uint32_t roam_retry_interval;
static bool anqp_disabled; static bool anqp_disabled;
static bool supports_arp_evict_nocarrier; static bool supports_arp_evict_nocarrier;
static bool supports_ndisc_evict_nocarrier; static bool supports_ndisc_evict_nocarrier;
static bool supports_drop_gratuitous_arp;
static bool supports_drop_unsolicited_na;
static bool supports_ipv4_drop_unicast_in_l2_multicast;
static bool supports_ipv6_drop_unicast_in_l2_multicast;
static bool pmksa_disabled;
static struct watchlist event_watches; static struct watchlist event_watches;
static uint32_t known_networks_watch; static uint32_t known_networks_watch;
static uint32_t allowed_bands; static uint32_t allowed_bands;
@ -131,6 +137,8 @@ struct station {
unsigned int affinity_watch; unsigned int affinity_watch;
char *affinity_client; char *affinity_client;
struct handshake_state *hs;
bool preparing_roam : 1; bool preparing_roam : 1;
bool roam_scan_full : 1; bool roam_scan_full : 1;
bool signal_low : 1; bool signal_low : 1;
@ -147,6 +155,55 @@ struct anqp_entry {
uint32_t pending; uint32_t pending;
}; };
/*
* Rather than sorting BSS's purely based on ranking a higher level grouping
* is used. The purpose of this higher order grouping is the consider the BSS's
* roam blacklist status. The roam blacklist is a "soft" blacklist in that we
* still should connect to these BSS's if they are the only "good" option.
* The question here is: what makes a BSS "good" vs "bad".
*
* For an initial (probably naive) approach here we can use the
* RoamThreshold[5G] which indicates the signal level that would not
* be of an acceptable connection quality. BSS can then be sorted either
* above or below this threshold. Within each of these groups a BSS may be
* blacklisted, meaning it should get sorted lower on the list compared to
* others within the same group.
*
* This sorting is achieved by extending rank to a uint32_t where the first 16
* bits are the standard rank calculated by scan.c. Above that bits can be
* reserved for this higher level grouping:
*
* Bit 16 indicates the BSS is not blacklisted
* Bit 17 indicates the BSS is above the critical signal threshold
*/
#define ABOVE_THRESHOLD_BIT 17
#define NOT_BLACKLISTED_BIT 16
static uint32_t evaluate_bss_group_rank(const uint8_t *addr, uint32_t freq,
int16_t signal_strength, uint16_t rank)
{
int signal = signal_strength / 100;
bool roam_blacklist;
bool good_signal;
uint32_t rank_out = (uint32_t) rank;
if (blacklist_contains_bss(addr, BLACKLIST_REASON_CONNECT_FAILED))
return 0;
roam_blacklist = blacklist_contains_bss(addr,
BLACKLIST_REASON_ROAM_REQUESTED);
good_signal = signal >= netdev_get_low_signal_threshold(freq);
if (good_signal)
set_bit(&rank_out, ABOVE_THRESHOLD_BIT);
if (!roam_blacklist)
set_bit(&rank_out, NOT_BLACKLISTED_BIT);
return rank_out;
}
/* /*
* Used as entries for the roam list since holding scan_bss pointers directly * Used as entries for the roam list since holding scan_bss pointers directly
* from station->bss_list is not 100% safe due to the possibility of the * from station->bss_list is not 100% safe due to the possibility of the
@ -154,13 +211,13 @@ struct anqp_entry {
*/ */
struct roam_bss { struct roam_bss {
uint8_t addr[6]; uint8_t addr[6];
uint16_t rank; uint32_t rank;
int32_t signal_strength; int32_t signal_strength;
bool ft_failed: 1; bool ft_failed: 1;
}; };
static struct roam_bss *roam_bss_from_scan_bss(const struct scan_bss *bss, static struct roam_bss *roam_bss_from_scan_bss(const struct scan_bss *bss,
uint16_t rank) uint32_t rank)
{ {
struct roam_bss *rbss = l_new(struct roam_bss, 1); struct roam_bss *rbss = l_new(struct roam_bss, 1);
@ -421,7 +478,8 @@ static void station_print_scan_bss(const struct scan_bss *bss)
ptr += sprintf(ptr, ", snr: %d", bss->snr); ptr += sprintf(ptr, ", snr: %d", bss->snr);
if (bss->have_utilization) if (bss->have_utilization)
ptr += sprintf(ptr, ", load: %u/255", bss->utilization); ptr += sprintf(ptr, ", load: %u/255, clients: %u",
bss->utilization, bss->sta_count);
l_debug("Processing BSS '%s' with SSID: %s, freq: %u, rank: %u, " l_debug("Processing BSS '%s' with SSID: %s, freq: %u, rank: %u, "
"strength: %i, data_rate: %u.%u%s", "strength: %i, data_rate: %u.%u%s",
@ -1150,6 +1208,7 @@ static int station_build_handshake_rsn(struct handshake_state *hs,
struct network *network, struct network *network,
struct scan_bss *bss) struct scan_bss *bss)
{ {
struct netdev *netdev = netdev_find(hs->ifindex);
const struct l_settings *settings = iwd_get_config(); const struct l_settings *settings = iwd_get_config();
enum security security = network_get_security(network); enum security security = network_get_security(network);
bool add_mde = false; bool add_mde = false;
@ -1160,6 +1219,7 @@ static int station_build_handshake_rsn(struct handshake_state *hs,
uint8_t *ap_ie; uint8_t *ap_ie;
bool disable_ocv; bool disable_ocv;
enum band_freq band; enum band_freq band;
struct pmksa *pmksa;
memset(&info, 0, sizeof(info)); memset(&info, 0, sizeof(info));
@ -1293,6 +1353,17 @@ build_ie:
IE_CIPHER_IS_GCMP_CCMP(info.pairwise_ciphers)) IE_CIPHER_IS_GCMP_CCMP(info.pairwise_ciphers))
info.extended_key_id = true; info.extended_key_id = true;
if (IE_AKM_IS_SAE(info.akm_suites) && !pmksa_disabled) {
pmksa = pmksa_cache_get(netdev_get_address(netdev), bss->addr,
bss->ssid, bss->ssid_len,
info.akm_suites);
if (pmksa) {
handshake_state_set_pmksa(hs, pmksa);
info.num_pmkids = 1;
info.pmkids = hs->pmksa->pmkid;
}
}
/* RSN takes priority */ /* RSN takes priority */
if (bss->rsne) { if (bss->rsne) {
ap_ie = bss->rsne; ap_ie = bss->rsne;
@ -1377,7 +1448,7 @@ static struct handshake_state *station_handshake_setup(struct station *station,
handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems); handshake_state_set_vendor_ies(hs, vendor_ies, iov_elems);
/* /*
* It can't hurt to try the FILS IP Address Assigment independent of * It can't hurt to try the FILS IP Address Assignment independent of
* which auth-proto is actually used. * which auth-proto is actually used.
*/ */
if (station->netconfig && netconfig_get_fils_ip_req(station->netconfig, if (station->netconfig && netconfig_get_fils_ip_req(station->netconfig,
@ -1389,7 +1460,7 @@ static struct handshake_state *station_handshake_setup(struct station *station,
return hs; return hs;
not_supported: not_supported:
handshake_state_free(hs); handshake_state_unref(hs);
return NULL; return NULL;
} }
@ -1641,10 +1712,13 @@ static void station_set_drop_neighbor_discovery(struct station *station,
{ {
char *v = value ? "1" : "0"; char *v = value ? "1" : "0";
sysfs_write_ipv4_setting(netdev_get_name(station->netdev), if (supports_drop_gratuitous_arp)
"drop_gratuitous_arp", v); sysfs_write_ipv4_setting(netdev_get_name(station->netdev),
sysfs_write_ipv6_setting(netdev_get_name(station->netdev), "drop_gratuitous_arp", v);
"drop_unsolicited_na", v);
if (supports_drop_unsolicited_na)
sysfs_write_ipv6_setting(netdev_get_name(station->netdev),
"drop_unsolicited_na", v);
} }
static void station_set_drop_unicast_l2_multicast(struct station *station, static void station_set_drop_unicast_l2_multicast(struct station *station,
@ -1652,10 +1726,13 @@ static void station_set_drop_unicast_l2_multicast(struct station *station,
{ {
char *v = value ? "1" : "0"; char *v = value ? "1" : "0";
sysfs_write_ipv4_setting(netdev_get_name(station->netdev), if (supports_ipv4_drop_unicast_in_l2_multicast)
"drop_unicast_in_l2_multicast", v); sysfs_write_ipv4_setting(netdev_get_name(station->netdev),
sysfs_write_ipv6_setting(netdev_get_name(station->netdev), "drop_unicast_in_l2_multicast", v);
"drop_unicast_in_l2_multicast", v);
if (supports_ipv6_drop_unicast_in_l2_multicast)
sysfs_write_ipv6_setting(netdev_get_name(station->netdev),
"drop_unicast_in_l2_multicast", v);
} }
static void station_signal_agent_notify(struct station *station); static void station_signal_agent_notify(struct station *station);
@ -1760,6 +1837,11 @@ static void station_enter_state(struct station *station,
station->affinity_watch = 0; station->affinity_watch = 0;
} }
if (station->hs) {
handshake_state_unref(station->hs);
station->hs = NULL;
}
break; break;
case STATION_STATE_DISCONNECTING: case STATION_STATE_DISCONNECTING:
case STATION_STATE_NETCONFIG: case STATION_STATE_NETCONFIG:
@ -1905,7 +1987,7 @@ static void station_reset_connection_state(struct station *station)
/* /*
* Perform this step last since calling network_disconnected() might * Perform this step last since calling network_disconnected() might
* result in the removal of the network (for example if provisioning * result in the removal of the network (for example if provisioning
* a new hidden network fails with an incorrect pasword). * a new hidden network fails with an incorrect password).
*/ */
if (station->state == STATION_STATE_CONNECTED || if (station->state == STATION_STATE_CONNECTED ||
station->state == STATION_STATE_CONNECTING || station->state == STATION_STATE_CONNECTING ||
@ -2473,9 +2555,12 @@ static void station_preauthenticate_cb(struct netdev *netdev,
} }
if (station_transition_reassociate(station, bss, new_hs) < 0) { if (station_transition_reassociate(station, bss, new_hs) < 0) {
handshake_state_free(new_hs); handshake_state_unref(new_hs);
station_roam_failed(station); station_roam_failed(station);
} }
handshake_state_unref(station->hs);
station->hs = handshake_state_ref(new_hs);
} }
static void station_transition_start(struct station *station); static void station_transition_start(struct station *station);
@ -2676,10 +2761,13 @@ static bool station_try_next_transition(struct station *station,
} }
if (station_transition_reassociate(station, bss, new_hs) < 0) { if (station_transition_reassociate(station, bss, new_hs) < 0) {
handshake_state_free(new_hs); handshake_state_unref(new_hs);
return false; return false;
} }
handshake_state_unref(station->hs);
station->hs = handshake_state_ref(new_hs);
return true; return true;
} }
@ -2766,7 +2854,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
struct handshake_state *hs = netdev_get_handshake(station->netdev); struct handshake_state *hs = netdev_get_handshake(station->netdev);
struct scan_bss *current_bss = station->connected_bss; struct scan_bss *current_bss = station->connected_bss;
struct scan_bss *bss; struct scan_bss *bss;
double cur_bss_rank = 0.0; uint32_t cur_bss_group_rank = 0;
static const double RANK_FT_FACTOR = 1.3; static const double RANK_FT_FACTOR = 1.3;
uint16_t mdid; uint16_t mdid;
enum security orig_security, security; enum security orig_security, security;
@ -2795,10 +2883,15 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
*/ */
bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr); bss = l_queue_find(bss_list, bss_match_bssid, current_bss->addr);
if (bss && !station->ap_directed_roaming) { if (bss && !station->ap_directed_roaming) {
cur_bss_rank = bss->rank; double cur_bss_rank = bss->rank;
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid) if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
cur_bss_rank *= RANK_FT_FACTOR; cur_bss_rank *= RANK_FT_FACTOR;
cur_bss_group_rank = evaluate_bss_group_rank(bss->addr,
bss->frequency,
bss->signal_strength,
(uint16_t) cur_bss_rank);
} }
/* /*
@ -2820,6 +2913,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
while ((bss = l_queue_pop_head(bss_list))) { while ((bss = l_queue_pop_head(bss_list))) {
double rank; double rank;
struct roam_bss *rbss; struct roam_bss *rbss;
uint32_t group_rank;
station_print_scan_bss(bss); station_print_scan_bss(bss);
@ -2841,7 +2935,8 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
if (network_can_connect_bss(network, bss) < 0) if (network_can_connect_bss(network, bss) < 0)
goto next; goto next;
if (blacklist_contains_bss(bss->addr)) if (blacklist_contains_bss(bss->addr,
BLACKLIST_REASON_CONNECT_FAILED))
goto next; goto next;
rank = bss->rank; rank = bss->rank;
@ -2849,7 +2944,11 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid) if (hs->mde && bss->mde_present && l_get_le16(bss->mde) == mdid)
rank *= RANK_FT_FACTOR; rank *= RANK_FT_FACTOR;
if (rank <= cur_bss_rank) group_rank = evaluate_bss_group_rank(bss->addr, bss->frequency,
bss->signal_strength,
(uint16_t) rank);
if (group_rank <= cur_bss_group_rank)
goto next; goto next;
/* /*
@ -2858,7 +2957,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
*/ */
station_update_roam_bss(station, bss); station_update_roam_bss(station, bss);
rbss = roam_bss_from_scan_bss(bss, rank); rbss = roam_bss_from_scan_bss(bss, group_rank);
l_queue_insert(station->roam_bss_list, rbss, l_queue_insert(station->roam_bss_list, rbss,
roam_bss_rank_compare, NULL); roam_bss_rank_compare, NULL);
@ -3085,7 +3184,7 @@ static void station_roam_timeout_rearm(struct station *station, int seconds)
if (!station->roam_trigger_timeout) if (!station->roam_trigger_timeout)
goto new_timeout; goto new_timeout;
/* If we cant get the remaining time just create a new timer */ /* If we can't get the remaining time just create a new timer */
if (L_WARN_ON(!l_timeout_remaining(station->roam_trigger_timeout, if (L_WARN_ON(!l_timeout_remaining(station->roam_trigger_timeout,
&remaining))) { &remaining))) {
l_timeout_remove(station->roam_trigger_timeout); l_timeout_remove(station->roam_trigger_timeout);
@ -3126,12 +3225,10 @@ static void station_ap_directed_roam(struct station *station,
uint8_t req_mode; uint8_t req_mode;
uint16_t dtimer; uint16_t dtimer;
uint8_t valid_interval; uint8_t valid_interval;
bool can_roam = !station_cannot_roam(station);
l_debug("ifindex: %u", netdev_get_ifindex(station->netdev)); l_debug("ifindex: %u", netdev_get_ifindex(station->netdev));
if (station_cannot_roam(station))
return;
if (station->state != STATION_STATE_CONNECTED) { if (station->state != STATION_STATE_CONNECTED) {
l_debug("roam: unexpected AP directed roam -- ignore"); l_debug("roam: unexpected AP directed roam -- ignore");
return; return;
@ -3195,8 +3292,13 @@ static void station_ap_directed_roam(struct station *station,
* disassociating us. If either of these bits are set, set the * disassociating us. If either of these bits are set, set the
* ap_directed_roaming flag. Otherwise still try roaming but don't * ap_directed_roaming flag. Otherwise still try roaming but don't
* treat it any different than a normal roam. * treat it any different than a normal roam.
*
* The only exception here is if we are in the middle of roaming
* (can_roam == false) since we cannot reliably know if the roam scan
* included frequencies from potential candidates in this request,
* forcing a roam in this case might result in unintended behavior.
*/ */
if (req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT | if (can_roam && req_mode & (WNM_REQUEST_MODE_DISASSOCIATION_IMMINENT |
WNM_REQUEST_MODE_TERMINATION_IMMINENT | WNM_REQUEST_MODE_TERMINATION_IMMINENT |
WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT)) WNM_REQUEST_MODE_ESS_DISASSOCIATION_IMMINENT))
station->ap_directed_roaming = true; station->ap_directed_roaming = true;
@ -3223,6 +3325,19 @@ static void station_ap_directed_roam(struct station *station,
pos += url_len; pos += url_len;
} }
blacklist_add_bss(station->connected_bss->addr,
BLACKLIST_REASON_ROAM_REQUESTED);
station_debug_event(station, "ap-roam-blacklist-added");
/*
* Validating the frame and blacklisting should still be done even if
* we are mid-roam. Its important to track the BSS requesting the
* transition so when the current roam completes IWD will be less likely
* to roam back to the current BSS.
*/
if (!can_roam)
return;
station->preparing_roam = true; station->preparing_roam = true;
l_timeout_remove(station->roam_trigger_timeout); l_timeout_remove(station->roam_trigger_timeout);
@ -3361,12 +3476,53 @@ static bool station_retry_with_reason(struct station *station,
break; break;
} }
blacklist_add_bss(station->connected_bss->addr); blacklist_add_bss(station->connected_bss->addr,
BLACKLIST_REASON_CONNECT_FAILED);
/*
* Network blacklist the BSS as well, since the timeout blacklist could
* be disabled
*/
network_blacklist_add(station->connected_network,
station->connected_bss);
try_next: try_next:
return station_try_next_bss(station); return station_try_next_bss(station);
} }
static bool station_pmksa_fallback(struct station *station, uint16_t status)
{
/*
* IEEE 802.11-2020 12.6.10.3 Cached PMKSAs and RSNA key management
*
* "If the Authenticator does not have a PMKSA for the PMKIDs in the
* (re)association request or the AKM does not match, its behavior
* depends on how the PMKSA was established. If SAE authentication was
* used to establish the PMKSA, then the AP shall reject (re)association
* by sending a (Re)Association Response frame with status code
* STATUS_INVALID_PMKID. Note that this allows the non-AP STA to fall
* back to full SAE authentication to establish another PMKSA"
*/
if (status != MMPDU_STATUS_CODE_INVALID_PMKID)
return false;
if (L_WARN_ON(!station->hs))
return false;
if (!IE_AKM_IS_SAE(station->hs->akm_suite) || !station->hs->have_pmksa)
return false;
/*
* Remove the PMKSA from the handshake and return true to re-try the
* same BSS without PMKSA.
*/
handshake_state_remove_pmksa(station->hs);
station_debug_event(station, "pmksa-invalid-pmkid");
return true;
}
/* A bit more concise for trying to fit these into 80 characters */ /* A bit more concise for trying to fit these into 80 characters */
#define IS_TEMPORARY_STATUS(code) \ #define IS_TEMPORARY_STATUS(code) \
((code) == MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH || \ ((code) == MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH || \
@ -3377,6 +3533,10 @@ try_next:
static bool station_retry_with_status(struct station *station, static bool station_retry_with_status(struct station *station,
uint16_t status_code) uint16_t status_code)
{ {
/* If PMKSA failed don't blacklist so we can retry this BSS */
if (station_pmksa_fallback(station, status_code))
goto try_next;
/* /*
* Certain Auth/Assoc failures should not cause a timeout blacklist. * Certain Auth/Assoc failures should not cause a timeout blacklist.
* In these cases we want to only temporarily blacklist the BSS until * In these cases we want to only temporarily blacklist the BSS until
@ -3387,12 +3547,19 @@ static bool station_retry_with_status(struct station *station,
* specific BSS on our next attempt. There is currently no way to * specific BSS on our next attempt. There is currently no way to
* obtain that IE, but this should be done in the future. * obtain that IE, but this should be done in the future.
*/ */
if (IS_TEMPORARY_STATUS(status_code)) if (!IS_TEMPORARY_STATUS(status_code))
network_blacklist_add(station->connected_network, blacklist_add_bss(station->connected_bss->addr,
station->connected_bss); BLACKLIST_REASON_CONNECT_FAILED);
else
blacklist_add_bss(station->connected_bss->addr);
/*
* Unconditionally network blacklist the BSS if we are retrying. This
* will allow network_bss_select to traverse the BSS list and ignore
* BSS's which have previously failed
*/
network_blacklist_add(station->connected_network,
station->connected_bss);
try_next:
iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code); iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code);
return station_try_next_bss(station); return station_try_next_bss(station);
@ -3477,7 +3644,8 @@ static void station_connect_cb(struct netdev *netdev, enum netdev_result result,
switch (result) { switch (result) {
case NETDEV_RESULT_OK: case NETDEV_RESULT_OK:
blacklist_remove_bss(station->connected_bss->addr); blacklist_remove_bss(station->connected_bss->addr,
BLACKLIST_REASON_CONNECT_FAILED);
station_connect_ok(station); station_connect_ok(station);
return; return;
case NETDEV_RESULT_DISCONNECTED: case NETDEV_RESULT_DISCONNECTED:
@ -3710,6 +3878,15 @@ int __station_connect_network(struct station *station, struct network *network,
struct handshake_state *hs; struct handshake_state *hs;
int r; int r;
/*
* If we already have a handshake_state ref this is due to a retry,
* unref that now
*/
if (station->hs) {
handshake_state_unref(station->hs);
station->hs = NULL;
}
if (station->netconfig && !netconfig_load_settings( if (station->netconfig && !netconfig_load_settings(
station->netconfig, station->netconfig,
network_get_settings(network))) network_get_settings(network)))
@ -3723,7 +3900,7 @@ int __station_connect_network(struct station *station, struct network *network,
station_netdev_event, station_netdev_event,
station_connect_cb, station); station_connect_cb, station);
if (r < 0) { if (r < 0) {
handshake_state_free(hs); handshake_state_unref(hs);
return r; return r;
} }
@ -3736,6 +3913,7 @@ int __station_connect_network(struct station *station, struct network *network,
station->connected_bss = bss; station->connected_bss = bss;
station->connected_network = network; station->connected_network = network;
station->hs = handshake_state_ref(hs);
if (station->state != state) if (station->state != state)
station_enter_state(station, state); station_enter_state(station, state);
@ -4658,7 +4836,8 @@ static struct l_dbus_message *station_property_set_affinities(
return dbus_error_invalid_args(message); return dbus_error_invalid_args(message);
/* Get first entry, there should be only one */ /* Get first entry, there should be only one */
l_dbus_message_iter_next_entry(&array, &new_path); if (!l_dbus_message_iter_next_entry(&array, &new_path))
return dbus_error_invalid_args(message);
if (l_dbus_message_iter_next_entry(&array, &new_path)) if (l_dbus_message_iter_next_entry(&array, &new_path))
return dbus_error_invalid_args(message); return dbus_error_invalid_args(message);
@ -5028,6 +5207,11 @@ static void station_free(struct station *station)
l_queue_destroy(station->owe_hidden_scan_ids, NULL); l_queue_destroy(station->owe_hidden_scan_ids, NULL);
} }
if (station->hs) {
handshake_state_unref(station->hs);
station->hs = NULL;
}
station_roam_state_clear(station); station_roam_state_clear(station);
l_queue_destroy(station->networks_sorted, NULL); l_queue_destroy(station->networks_sorted, NULL);
@ -5683,7 +5867,7 @@ static void add_frame_watches(struct netdev *netdev)
*/ */
frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0, frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0,
action_ap_roam_prefix, sizeof(action_ap_roam_prefix), action_ap_roam_prefix, sizeof(action_ap_roam_prefix),
ap_roam_frame_event, false, ap_roam_frame_event,
L_UINT_TO_PTR(netdev_get_ifindex(netdev)), NULL); L_UINT_TO_PTR(netdev_get_ifindex(netdev)), NULL);
} }
@ -5791,6 +5975,10 @@ static int station_init(void)
&anqp_disabled)) &anqp_disabled))
anqp_disabled = true; anqp_disabled = true;
if (!l_settings_get_bool(iwd_get_config(), "General", "DisablePMKSA",
&pmksa_disabled))
pmksa_disabled = false;
if (!netconfig_enabled()) if (!netconfig_enabled())
l_info("station: Network configuration is disabled."); l_info("station: Network configuration is disabled.");
@ -5798,6 +5986,16 @@ static int station_init(void)
"arp_evict_nocarrier"); "arp_evict_nocarrier");
supports_ndisc_evict_nocarrier = sysfs_supports_ipv6_setting("all", supports_ndisc_evict_nocarrier = sysfs_supports_ipv6_setting("all",
"ndisc_evict_nocarrier"); "ndisc_evict_nocarrier");
supports_drop_gratuitous_arp = sysfs_supports_ipv4_setting("all",
"drop_gratuitous_arp");
supports_drop_unsolicited_na = sysfs_supports_ipv6_setting("all",
"drop_unsolicited_na");
supports_ipv4_drop_unicast_in_l2_multicast =
sysfs_supports_ipv4_setting("all",
"drop_unicast_in_l2_multicast");
supports_ipv6_drop_unicast_in_l2_multicast =
sysfs_supports_ipv6_setting("all",
"drop_unicast_in_l2_multicast");
watchlist_init(&event_watches, NULL); watchlist_init(&event_watches, NULL);

View File

@ -500,6 +500,13 @@ int __storage_decrypt(struct l_settings *settings, const char *ssid,
return 0; return 0;
} }
/*
* It should likely be far larger than this, but that will get caught
* later when reloading the decrypted data.
*/
if (elen < 16)
return -EBADMSG;
/* /*
* AES-SIV automatically verifies the IV (16 bytes) and returns only * AES-SIV automatically verifies the IV (16 bytes) and returns only
* the decrypted data portion. We add one here for the NULL terminator * the decrypted data portion. We add one here for the NULL terminator
@ -535,7 +542,7 @@ int __storage_decrypt(struct l_settings *settings, const char *ssid,
/* /*
* Load decrypted data into existing settings. This is not how the API * Load decrypted data into existing settings. This is not how the API
* is indended to be used (since this could result in duplicate groups) * is intended to be used (since this could result in duplicate groups)
* but since the Security group was just removed and EncryptedSecurity * but since the Security group was just removed and EncryptedSecurity
* should only contain a Security group its safe to use it this way. * should only contain a Security group its safe to use it this way.
*/ */
@ -596,7 +603,7 @@ struct l_settings *storage_network_open(enum security type, const char *ssid)
struct l_settings *settings; struct l_settings *settings;
_auto_(l_free) char *path = NULL; _auto_(l_free) char *path = NULL;
if (ssid == NULL) if (!ssid)
return NULL; return NULL;
path = storage_get_network_file_path(type, ssid); path = storage_get_network_file_path(type, ssid);
@ -623,7 +630,7 @@ int storage_network_touch(enum security type, const char *ssid)
char *path; char *path;
int ret; int ret;
if (ssid == NULL) if (!ssid)
return -EINVAL; return -EINVAL;
path = storage_get_network_file_path(type, ssid); path = storage_get_network_file_path(type, ssid);

View File

@ -234,8 +234,7 @@ static bool can_read_data(struct l_io *io, void *user_data)
if (len < 0) if (len < 0)
return false; return false;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
struct nl_pktinfo *pktinfo; struct nl_pktinfo *pktinfo;
if (cmsg->cmsg_level != SOL_NETLINK) if (cmsg->cmsg_level != SOL_NETLINK)

View File

@ -276,7 +276,7 @@ bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix_out,
/* 'i' will be at most INET_ADDRSTRLEN - 1 */ /* 'i' will be at most INET_ADDRSTRLEN - 1 */
l_strlcpy(no_prefix, ip, i + 1); l_strlcpy(no_prefix, ip, i + 1);
/* Check if IP preceeding prefix is valid */ /* Check if IP preceding prefix is valid */
if (inet_pton(AF_INET, no_prefix, &ia) != 1 || ia.s_addr == 0) if (inet_pton(AF_INET, no_prefix, &ia) != 1 || ia.s_addr == 0)
return false; return false;
@ -312,6 +312,36 @@ bool util_ip_prefix_tohl(const char *ip, uint8_t *prefix_out,
return true; return true;
} }
/*
* Linearly maps @value (expected to be within range @a_start and @a_end) to
* a new value between @b_start and @b_end.
*
* Returns: false if
* @value is not between @a_start and @a_end
* @a_start/@a_end or @b_start/@b_end are equal.
*/
bool util_linear_map(double value, double a_start, double a_end,
double b_start, double b_end, double *mapped_value)
{
/* Check value is within a's range */
if (a_start < a_end) {
if (value < a_start || value > a_end)
return false;
} else if (a_start > a_end) {
if (value > a_start || value < a_end)
return false;
} else
return false;
if (b_start == b_end)
return false;
*mapped_value = b_start + (((b_end - b_start) / (a_end - a_start)) *
(value - a_start));
return true;
}
struct scan_freq_set { struct scan_freq_set {
uint16_t channels_2ghz; uint16_t channels_2ghz;
struct l_uintset *channels_5ghz; struct l_uintset *channels_5ghz;
@ -464,6 +494,11 @@ static void scan_channels_foreach(uint32_t channel, void *user_data)
uint32_t freq; uint32_t freq;
freq = band_channel_to_freq(channel, channels_data->band); freq = band_channel_to_freq(channel, channels_data->band);
if (!freq) {
l_warn("invalid channel %u for band %u", channel,
channels_data->band);
return;
}
channels_data->func(freq, channels_data->user_data); channels_data->func(freq, channels_data->user_data);
} }
@ -601,3 +636,31 @@ struct scan_freq_set *scan_freq_set_clone(const struct scan_freq_set *set,
return new; return new;
} }
/* First 64 entries calculated by 1 / pow(n, 0.3) for n >= 1 */
static const double rankmod_table[] = {
1.0000000000, 0.8122523964, 0.7192230933, 0.6597539554,
0.6170338627, 0.5841906811, 0.5577898253, 0.5358867313,
0.5172818580, 0.5011872336, 0.4870596972, 0.4745102806,
0.4632516708, 0.4530661223, 0.4437850034, 0.4352752816,
0.4274303178, 0.4201634287, 0.4134032816, 0.4070905315,
0.4011753236, 0.3956154062, 0.3903746872, 0.3854221125,
0.3807307877, 0.3762772797, 0.3720410580, 0.3680040435,
0.3641502401, 0.3604654325, 0.3569369365, 0.3535533906,
0.3503045821, 0.3471812999, 0.3441752105, 0.3412787518,
0.3384850430, 0.3357878061, 0.3331812996, 0.3306602598,
0.3282198502, 0.3258556179, 0.3235634544, 0.3213395618,
0.3191804229, 0.3170827751, 0.3150435863, 0.3130600345,
0.3111294892, 0.3092494947, 0.3074177553, 0.3056321221,
0.3038905808, 0.3021912409, 0.3005323264, 0.2989121662,
0.2973291870, 0.2957819051, 0.2942689208, 0.2927889114,
0.2913406263, 0.2899228820, 0.2885345572, 0.2871745887,
};
double util_exponential_decay(unsigned int n)
{
if (n >= L_ARRAY_SIZE(rankmod_table))
return rankmod_table[L_ARRAY_SIZE(rankmod_table) - 1];
return rankmod_table[n];
}

View File

@ -106,6 +106,17 @@ static inline bool util_ip_subnet_match(uint8_t prefix_len,
~((1u << (8 - (prefix_len % 8))) - 1)); ~((1u << (8 - (prefix_len % 8))) - 1));
} }
/*
* Linearly maps (interpolates) 'value' from range 'a' to range 'b'
*
* Fails if:
* - value is not between a and b
* - a_start == a_end
* - b_start == b_end
*/
bool util_linear_map(double value, double a_start, double a_end,
double b_start, double b_end, double *mapped_value);
typedef void (*scan_freq_set_func_t)(uint32_t freq, void *userdata); typedef void (*scan_freq_set_func_t)(uint32_t freq, void *userdata);
struct scan_freq_set *scan_freq_set_new(void); struct scan_freq_set *scan_freq_set_new(void);
@ -130,4 +141,6 @@ struct scan_freq_set *scan_freq_set_clone(const struct scan_freq_set *set,
DEFINE_CLEANUP_FUNC(scan_freq_set_free); DEFINE_CLEANUP_FUNC(scan_freq_set_free);
double util_exponential_decay(unsigned int n);
#endif /* __UTIL_H */ #endif /* __UTIL_H */

View File

@ -72,6 +72,9 @@ enum driver_flag {
DEFAULT_IF = 0x1, DEFAULT_IF = 0x1,
FORCE_PAE = 0x2, FORCE_PAE = 0x2,
POWER_SAVE_DISABLE = 0x4, POWER_SAVE_DISABLE = 0x4,
OWE_DISABLE = 0x8,
MULTICAST_RX_DISABLE = 0x10,
SAE_DISABLE = 0x20,
}; };
struct driver_flag_name { struct driver_flag_name {
@ -100,9 +103,12 @@ static const struct driver_info driver_infos[] = {
}; };
static const struct driver_flag_name driver_flag_names[] = { static const struct driver_flag_name driver_flag_names[] = {
{ "DefaultInterface", DEFAULT_IF }, { "DefaultInterface", DEFAULT_IF },
{ "ForcePae", FORCE_PAE }, { "ForcePae", FORCE_PAE },
{ "PowerSaveDisable", POWER_SAVE_DISABLE }, { "PowerSaveDisable", POWER_SAVE_DISABLE },
{ "OweDisable", OWE_DISABLE },
{ "MulticastRxDisable", MULTICAST_RX_DISABLE },
{ "SaeDisable", SAE_DISABLE },
}; };
struct wiphy { struct wiphy {
@ -198,6 +204,9 @@ uint16_t wiphy_get_supported_ciphers(struct wiphy *wiphy, uint16_t mask)
static bool wiphy_can_connect_sae(struct wiphy *wiphy) static bool wiphy_can_connect_sae(struct wiphy *wiphy)
{ {
if (wiphy->driver_flags & SAE_DISABLE)
return false;
/* /*
* WPA3 Specification version 3, Section 2.2: * WPA3 Specification version 3, Section 2.2:
* A STA shall not enable WEP and TKIP * A STA shall not enable WEP and TKIP
@ -230,29 +239,22 @@ static bool wiphy_can_connect_sae(struct wiphy *wiphy)
* cards the entire SAE protocol as well as the subsequent 4-way * cards the entire SAE protocol as well as the subsequent 4-way
* handshake are all done in the driver/firmware (fullMAC). * handshake are all done in the driver/firmware (fullMAC).
* *
* 3. TODO: Cards which allow SAE in userspace via CMD_EXTERNAL_AUTH. * 3. Cards which allow SAE in userspace via CMD_EXTERNAL_AUTH.
* These cards do not support AUTH/ASSOC commands but do implement * These cards do not support AUTH/ASSOC commands but do implement
* CMD_EXTERNAL_AUTH which is supposed to allow userspace to * CMD_EXTERNAL_AUTH which is supposed to allow userspace to
* generate Authenticate frames as it would for case (1). As it * generate Authenticate frames as it would for case (1).
* stands today only one driver actually uses CMD_EXTERNAL_AUTH and
* for now IWD will not allow connections to SAE networks using this
* mechanism.
*/ */
if (wiphy_has_feature(wiphy, NL80211_FEATURE_SAE)) { if (wiphy_has_feature(wiphy, NL80211_FEATURE_SAE)) {
/* Case (1) */ /* Case (1) */
if (wiphy->support_cmds_auth_assoc) if (wiphy->support_cmds_auth_assoc)
return true; return true;
/* /* Case 3 */
* Case (3) iwd_notice(IWD_NOTICE_CONNECT_INFO,
* "FullMAC driver: %s using SAE. Expect EXTERNAL_AUTH",
* TODO: No support for CMD_EXTERNAL_AUTH yet.
*/
l_warn("SAE unsupported: %s needs CMD_EXTERNAL_AUTH for SAE",
wiphy->driver_str); wiphy->driver_str);
return false; return true;
} }
/* Case (2) */ /* Case (2) */
@ -349,7 +351,8 @@ wpa2_personal:
if (info->akm_suites & IE_RSN_AKM_SUITE_PSK) if (info->akm_suites & IE_RSN_AKM_SUITE_PSK)
return IE_RSN_AKM_SUITE_PSK; return IE_RSN_AKM_SUITE_PSK;
} else if (security == SECURITY_NONE) { } else if (security == SECURITY_NONE) {
if (info->akm_suites & IE_RSN_AKM_SUITE_OWE) if (info->akm_suites & IE_RSN_AKM_SUITE_OWE &&
!wiphy_owe_disabled(wiphy))
return IE_RSN_AKM_SUITE_OWE; return IE_RSN_AKM_SUITE_OWE;
} }
@ -647,7 +650,7 @@ bool wiphy_rrm_capable(struct wiphy *wiphy)
return false; return false;
} }
bool wiphy_has_ext_feature(struct wiphy *wiphy, uint32_t feature) bool wiphy_has_ext_feature(const struct wiphy *wiphy, uint32_t feature)
{ {
return feature < sizeof(wiphy->ext_features) * 8 && return feature < sizeof(wiphy->ext_features) * 8 &&
test_bit(wiphy->ext_features, feature); test_bit(wiphy->ext_features, feature);
@ -726,6 +729,14 @@ bool wiphy_power_save_disabled(struct wiphy *wiphy)
return false; return false;
} }
bool wiphy_owe_disabled(struct wiphy *wiphy)
{
if (wiphy->driver_flags & OWE_DISABLE)
return true;
return false;
}
const uint8_t *wiphy_get_extended_capabilities(struct wiphy *wiphy, const uint8_t *wiphy_get_extended_capabilities(struct wiphy *wiphy,
uint32_t iftype) uint32_t iftype)
{ {
@ -945,6 +956,13 @@ bool wiphy_supports_cmd_offchannel(const struct wiphy *wiphy)
return wiphy->supports_cmd_offchannel; return wiphy->supports_cmd_offchannel;
} }
bool wiphy_supports_multicast_rx(const struct wiphy *wiphy)
{
return wiphy_has_ext_feature(wiphy,
NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS) &&
!(wiphy->driver_flags & MULTICAST_RX_DISABLE);
}
const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy, const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy,
enum band_freq band, enum band_freq band,
size_t *size) size_t *size)
@ -1355,6 +1373,15 @@ static void wiphy_print_basic_info(struct wiphy *wiphy)
if (wiphy->driver_flags & POWER_SAVE_DISABLE) if (wiphy->driver_flags & POWER_SAVE_DISABLE)
flags = l_strv_append(flags, "PowerSaveDisable"); flags = l_strv_append(flags, "PowerSaveDisable");
if (wiphy->driver_flags & OWE_DISABLE)
flags = l_strv_append(flags, "OweDisable");
if (wiphy->driver_flags & MULTICAST_RX_DISABLE)
flags = l_strv_append(flags, "MulticastRxDisable");
if (wiphy->driver_flags & SAE_DISABLE)
flags = l_strv_append(flags, "SaeDisable");
joined = l_strjoinv(flags, ' '); joined = l_strjoinv(flags, ' ');
l_info("\tDriver Flags: %s", joined); l_info("\tDriver Flags: %s", joined);
@ -1692,8 +1719,7 @@ static void parse_supported_bands(struct wiphy *wiphy,
case NL80211_BAND_ATTR_FREQS: case NL80211_BAND_ATTR_FREQS:
nl80211_parse_supported_frequencies(&attr, nl80211_parse_supported_frequencies(&attr,
wiphy->supported_freqs, wiphy->supported_freqs,
band->freq_attrs, band);
band->freqs_len);
break; break;
case NL80211_BAND_ATTR_RATES: case NL80211_BAND_ATTR_RATES:
@ -2227,9 +2253,7 @@ static void wiphy_dump_callback(struct l_genl_msg *msg,
* theory no new frequencies should be added so there * theory no new frequencies should be added so there
* should never be any stale values. * should never be any stale values.
*/ */
nl80211_parse_supported_frequencies(&attr, NULL, nl80211_parse_supported_frequencies(&attr, NULL, band);
band->freq_attrs,
band->freqs_len);
} }
} }
} }
@ -2282,7 +2306,7 @@ static void wiphy_dump_after_regdom(struct wiphy *wiphy)
} }
/* /*
* Another update while dumping wiphy. This next dump should supercede * Another update while dumping wiphy. This next dump should supersede
* the first and not result in a DONE event until this new dump is * the first and not result in a DONE event until this new dump is
* finished. This is because the disabled frequencies are in an unknown * finished. This is because the disabled frequencies are in an unknown
* state and could cause incorrect behavior by any watchers. * state and could cause incorrect behavior by any watchers.

View File

@ -119,7 +119,7 @@ bool wiphy_can_randomize_mac_addr(struct wiphy *wiphy);
bool wiphy_rrm_capable(struct wiphy *wiphy); bool wiphy_rrm_capable(struct wiphy *wiphy);
bool wiphy_supports_ext_key_id(struct wiphy *wiphy); bool wiphy_supports_ext_key_id(struct wiphy *wiphy);
bool wiphy_has_feature(struct wiphy *wiphy, uint32_t feature); bool wiphy_has_feature(struct wiphy *wiphy, uint32_t feature);
bool wiphy_has_ext_feature(struct wiphy *wiphy, uint32_t feature); bool wiphy_has_ext_feature(const struct wiphy *wiphy, uint32_t feature);
uint8_t wiphy_get_max_num_ssids_per_scan(struct wiphy *wiphy); uint8_t wiphy_get_max_num_ssids_per_scan(struct wiphy *wiphy);
uint16_t wiphy_get_max_scan_ie_len(struct wiphy *wiphy); uint16_t wiphy_get_max_scan_ie_len(struct wiphy *wiphy);
uint32_t wiphy_get_max_roc_duration(struct wiphy *wiphy); uint32_t wiphy_get_max_roc_duration(struct wiphy *wiphy);
@ -134,6 +134,7 @@ const char *wiphy_get_name(struct wiphy *wiphy);
bool wiphy_uses_default_if(struct wiphy *wiphy); bool wiphy_uses_default_if(struct wiphy *wiphy);
bool wiphy_control_port_enabled(struct wiphy *wiphy); bool wiphy_control_port_enabled(struct wiphy *wiphy);
bool wiphy_power_save_disabled(struct wiphy *wiphy); bool wiphy_power_save_disabled(struct wiphy *wiphy);
bool wiphy_owe_disabled(struct wiphy *wiphy);
const uint8_t *wiphy_get_extended_capabilities(struct wiphy *wiphy, const uint8_t *wiphy_get_extended_capabilities(struct wiphy *wiphy,
uint32_t iftype); uint32_t iftype);
const uint8_t *wiphy_get_rm_enabled_capabilities(struct wiphy *wiphy); const uint8_t *wiphy_get_rm_enabled_capabilities(struct wiphy *wiphy);
@ -142,6 +143,7 @@ void wiphy_get_reg_domain_country(struct wiphy *wiphy, char *out);
bool wiphy_country_is_unknown(struct wiphy *wiphy); bool wiphy_country_is_unknown(struct wiphy *wiphy);
bool wiphy_supports_uapsd(const struct wiphy *wiphy); bool wiphy_supports_uapsd(const struct wiphy *wiphy);
bool wiphy_supports_cmd_offchannel(const struct wiphy *wiphy); bool wiphy_supports_cmd_offchannel(const struct wiphy *wiphy);
bool wiphy_supports_multicast_rx(const struct wiphy *wiphy);
const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy, const uint8_t *wiphy_get_ht_capabilities(const struct wiphy *wiphy,
enum band_freq band, enum band_freq band,

View File

@ -393,7 +393,7 @@ static int wsc_enrollee_connect(struct wsc_enrollee *wsce, struct scan_bss *bss,
return 0; return 0;
error: error:
handshake_state_free(hs); handshake_state_unref(hs);
return r; return r;
} }

View File

@ -722,7 +722,7 @@ int wsc_parse_attrs(const unsigned char *pdu, unsigned int len,
} }
} }
if (e2 == NULL) { if (!e2) {
if (!ext_iter) if (!ext_iter)
break; break;

View File

@ -75,7 +75,7 @@ static bool secret_from_file(const char *file)
fd = open(file, O_RDONLY, 0); fd = open(file, O_RDONLY, 0);
if (fd < 0) { if (fd < 0) {
printf("Cant open %s (%d)\n", file, fd); printf("Can't open %s (%d)\n", file, fd);
return false; return false;
} }

View File

@ -119,7 +119,7 @@ class VirtualRadio(Radio):
''' '''
A subclass of 'Radio' specific to mac80211_hwsim radios. A subclass of 'Radio' specific to mac80211_hwsim radios.
TODO: Using D-Bus to create and destroy radios is more desireable TODO: Using D-Bus to create and destroy radios is more desirable
than the command line. than the command line.
''' '''
@ -400,7 +400,7 @@ class TestContext(Namespace):
# just given to hostapd/IWD as they appear during # just given to hostapd/IWD as they appear during
# discovery. # discovery.
# #
# TODO: It may be desireable to map PCI/USB adapters to # TODO: It may be desirable to map PCI/USB adapters to
# specific radX radios specified in the config but # specific radX radios specified in the config but
# there are really 2 separate use cases here. # there are really 2 separate use cases here.
# 1. You want to test a *specific* radio with IWD # 1. You want to test a *specific* radio with IWD
@ -482,7 +482,7 @@ class TestContext(Namespace):
def start_ofono(self): def start_ofono(self):
sim_keys = self.hw_config['SETUP'].get('sim_keys', None) sim_keys = self.hw_config['SETUP'].get('sim_keys', None)
if not sim_keys: if not sim_keys:
print("Ofono not requred") print("Ofono not required")
return return
elif sim_keys != 'ofono': elif sim_keys != 'ofono':
os.environ['IWD_SIM_KEYS'] = sim_keys os.environ['IWD_SIM_KEYS'] = sim_keys
@ -701,7 +701,7 @@ def start_test(ctx, subtests, rqueue):
# test results are queued and printed at the very end so its # test results are queued and printed at the very end so its
# difficult to know *where* a test failed (python gives a stack # difficult to know *where* a test failed (python gives a stack
# trace but printing the exception/failure immediately shows # trace but printing the exception/failure immediately shows
# where in the debug logs something failed). Moreso if there are # where in the debug logs something failed). More so if there are
# several test functions inside a single python file they run # several test functions inside a single python file they run
# as a single test and it is difficult (again) to know where # as a single test and it is difficult (again) to know where
# something failed. # something failed.
@ -731,7 +731,7 @@ def start_test(ctx, subtests, rqueue):
func, file = str(t).split(' ') func, file = str(t).split(' ')
# #
# TODO: There may be a better way of doing this # TODO: There may be a better way of doing this
# but strigifying the test class gives us a string: # but stringifying the test class gives us a string:
# <function> (<file>.<class>) # <function> (<file>.<class>)
# #
file = file.strip('()').split('.')[0] + '.py' file = file.strip('()').split('.')[0] + '.py'
@ -981,7 +981,7 @@ def run_auto_tests(ctx, args):
p = multiprocessing.Process(target=start_test, args=(ctx, subtests, rqueue)) p = multiprocessing.Process(target=start_test, args=(ctx, subtests, rqueue))
p.start() p.start()
# Rather than time each subtest we just time the total but # Rather than time each subtest we just time the total but
# mutiply the default time by the number of tests being run. # multiply the default time by the number of tests being run.
p.join(int(args.timeout) * len(subtests)) p.join(int(args.timeout) * len(subtests))
if p.is_alive(): if p.is_alive():

View File

@ -133,7 +133,7 @@ class Process(subprocess.Popen):
f.write(stamp + ': ') f.write(stamp + ': ')
# Write out a separator so multiple process calls per # Write out a separator so multiple process calls per
# test are easer to read. # test are easier to read.
if instance.hup: if instance.hup:
f.write("Terminated: {}\n\n".format(instance.args)) f.write("Terminated: {}\n\n".format(instance.args))

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