3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-12-22 13:02:44 +01:00
Commit Graph

7029 Commits

Author SHA1 Message Date
James Prestwood
6a60cb5a32 dpp: use the config's SSID to process scan results
The scan result handling was fragile because it assumed the kernel
would only give results matching the requested SSID. This isn't
something we should assume so instead keep the configuration object
around until after the scan and use the target SSID to lookup the
network.
2023-11-17 09:46:06 -06:00
James Prestwood
fa14ac125e dpp: use the new config->ssid member
This is now a NULL terminated string so it can be used directly.
2023-11-17 09:44:42 -06:00
James Prestwood
00ffb056e8 dpp-util: store SSID as string, not raw buffer
Nearly every use of the ssid member first has to memcpy it to a
buffer and NULL terminate. Instead just store the ssid as a
string when creating/parsing from JSON.
2023-11-17 09:44:36 -06:00
James Prestwood
aa116ba522 dpp: check that DPP is running in station watch
This was causing unneeded WARNING prints because the DPP state
was never checked. Fix this and bail out if DPP isn't running.
2023-11-16 09:47:41 -06:00
James Prestwood
3c02f387cb dpp: scan to pick up extra frequencies when enrolling
The DPP-PKEX spec provides a very limited list of frequencies used
to discover configurators, only 3 on 2.4 and 5GHz bands. Since
configurators (at least in IWD's implementation) are only allowed
on the current operating frequency its very unlikely an enrollee
will find a configurator on these frequencies out of the entire
spectrum.

The spec does mention that the 3 default frequencies should be used
"In lieu of specific channel information obtained in a manner outside
the scope of this specification, ...". This allows the implementation
some flexibility in using a broader range of frequencies.

To increase the chances of finding a configurator shared code
enrollees will first issue a scan to determine what access points are
around, then iterate these frequencies. This is especially helpful
when the configurators are IWD-based since we know that they'll be
on the same channels as the APs in the area.
2023-11-16 09:14:32 -06:00
James Prestwood
c8a86edffe dpp: fix fragile scan/connecting logic
The post-DPP connection was never done quite right due to station's
state being unknown. The state is now tracked in DPP by a previous
patch but the scan path in DPP is still wrong.

It relies on station autoconnect logic which has the potential to
connect to a different network than what was configured with DPP.
Its unlikely but still could happen in theory. In addition the scan
was not selectively filtering results by the SSID that DPP
configured.

This fixes the above problems by first filtering the scan by the
SSID. Then setting the scan results into station without triggering
autoconnect. And finally using network_autoconnect() directly
instead of relying on station to choose the SSID.
2023-11-16 09:10:39 -06:00
James Prestwood
e2f28312e2 dpp: add station watch to DPP
DPP (both DPP and PKEX) run the risk of odd behavior if station
decides to change state. DPP is completely unaware of this and
best case would just result in a protocol failure, worst case
duplicate calls to __station_connect_network.

Add a station watch and stop DPP if station changes state during
the protocol.
2023-11-16 09:07:22 -06:00
James Prestwood
485f9f56bf dpp: remove duplicate connected network check 2023-11-16 09:07:10 -06:00
Denis Kenzior
30c6a10f28 netdev: Separate connect_failed and disconnected paths
Commit c59669a366 ("netdev: disambiguate between disconnection types")
introduced different paths for different types of disconnection
notifications from netdev.  Formalize this further by having
netdev_connect_failed only invoke connect_cb.

Disconnections that could be triggered outside of connection
related events are now handled on a different code path.  For this
purpose, netdev_disconnected() is introduced.
2023-11-14 17:40:56 -06:00
Denis Kenzior
a14d78596d netdev: Simplify netdev_auth_cb error logic 2023-11-14 17:29:59 -06:00
Denis Kenzior
972d277363 netdev: Remove improper use of netdev_connect_failed
When a roam event is received, iwd generates a firmware scan request and
notifies its event filter of the ROAMING condition.  In cases where the
firmware scan could not be started successfully, netdev_connect_failed
is invoked.  This is not a correct use of netev_connect_failed since it
doesn't actually disconnect the underlying netdev and the reflected
state becomes de-synchronized from the underlying kernel device.

The firmware scan request could currently fail for two reasons:
  1. nl80211 genl socket is in a bad state, or
  2. the scan context does not exist

Since both reasons are highly unlikely, simply use L_WARN instead.

The other two cases where netdev_connect_failed is used could only occur
if the kernel message is invalid.  The message is ignored in that case
and a warning is printed.

The situation described above also exists in netdev_get_fw_scan_cb. If
the scan could not be completed successfully, there's not much iwd can
do to recover.  Have iwd remain in roaming state and print an error.
2023-11-14 17:27:34 -06:00
Denis Kenzior
c59669a366 netdev: disambiguate between disconnection types
There are generally three scenarios where iwd generates a disconnection
command to the kernel:
  1. Error conditions stemming from a connection related event.  For
     example if SAE/FT/FILS authentication fails during Authenticate or
     Associate steps and the kernel doesn't disconnect properly.
  2. Deauthentication after the connection has been established and not
     related to a connection attempt in progress.  For example, SA Query
     processing that triggers an disconnect.
  3. Disconnects that are triggered due to a handshake failure or if
     setting keys resulting from the handshake fails.  These disconnects
     can be triggered as a result of a pending connection or when a
     connection has been established (e.g. due to rekeying).

Distinguish between 1 and 2/3 by having the disconnect procedure take
different paths.  For now there are no functional changes since all
paths end up in netdev_connect_failed(), but this will change in the
future.
2023-11-14 14:55:06 -06:00
Denis Kenzior
28798990d2 netdev: Move CMD_REKEY_OFFLOAD builder to nl80211util 2023-11-14 10:09:07 -06:00
Denis Kenzior
05c1d34c6e netdev: Move CMD_NEW_KEY RX-only builder to nl80211util 2023-11-14 10:03:58 -06:00
Denis Kenzior
708a8feaba netdev: Move pairwise NEW_KEY builder to nl80211util 2023-11-14 09:57:36 -06:00
Denis Kenzior
1aa83722a0 netdev: Move CMD_DEL_STATION builder to nl80211util
While here, also get rid of netdev_del_station.  The only user of this
function was in ap.c and it could easily be replaced by invoking the new
nl80211_build_del_station function.  The callback used by
netdev_build_del_station only printed an error and didn't do anything
useful.  Get rid of it for now.
2023-11-14 09:49:39 -06:00
Denis Kenzior
904373eee7 netdev: Move CMD_DEAUTHENTICATE builder to nl80211util 2023-11-14 09:26:49 -06:00
Denis Kenzior
7498eaae62 netdev: Move CMD_DISCONNECT builder to nl80211util 2023-11-14 09:21:58 -06:00
Denis Kenzior
d12d8bec85 netdev: Don't unnecessarily call netdev_connect_failed
netdev_begin_connection() already invokes netdev_connect_failed on
error.  Remove any calls to netdev_connect_failed in callers of
netdev_begin_connection().

Fixes: 4165d9414f ("netdev: use wiphy radio work queue for connections")
2023-11-13 23:11:12 -06:00
Denis Kenzior
afc8f53fd3 netdev: Use CMD_DISCONNECT if OCI fails
If netdev_get_oci fails, a goto deauth is invoked in order to terminate
the current connection and return an error to the caller.  Unfortunately
the deauth label builds CMD_DEAUTHENTICATE in order to terminate the
connection.  This was fine because it used to handle authentication
protocols that ran over CMD_AUTHENTICATE and CMD_ASSOCIATE.  However,
OCI can also be used on FullMAC hardware that does not support them.
Use CMD_DISCONNECT instead which works everywhere.

Fixes: 06482b8116 ("netdev: Obtain operating channel info")
2023-11-13 21:29:08 -06:00
Denis Kenzior
e1c2706674 netdev: sa_query: Fix reason code handling
The reason code field was being obtained as a uint8_t value, while it is
actually a uint16_t in little-endian byte order.

Fixes: f3cc96499c ("netdev: added support for SA Query")
2023-11-13 17:14:34 -06:00
Denis Kenzior
bef70275f7 netdev: Fix obtaining reason code from deauth frames
The reason code from deauthentication frame was being obtained as a
uint8_t instead of a uint16_t.  The value was only ever used in an
informational statement.  Since the value was in little endian, only the
first 8 bits of the reason code were obtained.  Fix that.

Fixes: 2bebb4bdc7 ("netdev: Handle deauth frames prior to association")
2023-11-13 16:43:39 -06:00
James Prestwood
dbce8f9ff9 auto-t: get DPP PKEX test running reliably
Several tests do not pass due to some additional changes that have
not been merged. Remove these cases and add some hardening after
discovering some unfortunate wpa_supplicant behavior.

 - Disable p2p in wpa_supplicant. With p2p enabled an extra device
   is created which starts receiving DPP frames and printing
   confusing messages.
 - Remove extra asserts which don't make sense currently. These
   will be added back later as future additions to PKEX are
   upstreamed.
 - Work around wpa_supplicant retransmit limitation. This is
   described in detail in the comment in pkex_test.py
2023-11-13 09:47:13 -06:00
James Prestwood
13952ff350 auto-t: add stop APIs and fix some issues wpas.py
- wait_for_event was returning a list in certain cases, not the
   event itself
 - The configurator ID was not being printed (',' instead of '%')
 - The DPP ID was not being properly waited for with PKEX
2023-11-13 09:46:58 -06:00
James Prestwood
2be49a93ba auto-t: make test timeout configurable
With the addition of DPP PKEX autotests some of the timeouts are
quite long and hit test-runners maximum timeouts. For UML we should
allow this since time-travel lets us skip idle waits. Move the test
timeout out of a global define and into the argument list so QEMU
and UML can define it differently.
2023-11-13 09:46:34 -06:00
James Prestwood
3b6d279184 client: add client commands for shared code configuration
The StartConfigurator() call was left out since there would be no
functional difference to the user in iwctl. Its expected that
human users of the shared code API provide the code/id ahead of
time, i.e. use ConfigureEnrollee/StartEnrollee.
2023-11-11 10:27:27 -06:00
James Prestwood
6e2dacb0ec client: Add shared code DBus interface 2023-11-11 10:27:10 -06:00
Finn Behrens
8d2e35b2d4 client: display_completion_matches add 0-byte check
Check that enough space for newline and 0-byte is left in line.
This fixes a buffer overflow on specific completion results.

Reported-By: Leona Maroni <dev@leona.is>
2023-11-11 10:24:01 -06:00
James Prestwood
2f4c09def0 dpp: fix removed dpp_reset in Stop()
It seems in my patch reordering both stop methods lost the actual
call to dpp_reset().
2023-11-09 20:15:56 -06:00
James Prestwood
29778733e5 auto-t: add DPP PKEX tests 2023-11-09 10:35:21 -06:00
James Prestwood
6f7384d5c7 auto-t: add APIs for PKEX
Also added some sanity checks to the existing DPP APIs to make
sure started/role gets set correctly.
2023-11-09 10:35:11 -06:00
James Prestwood
809ddcebbd auto-t: add utils for wpa_supplicant PKEX 2023-11-09 10:35:00 -06:00
James Prestwood
2ca9a55fd5 dpp: Add StartConfigurator, PKEX agent support
Adds a configurator variant to be used along side an agent. When
called the configurator will start and wait for an initial PKEX
exchange message from an enrollee at which point it will request
the code from an agent. This provides more flexibility for
configurators that are capable of configuring multiple enrollees
with different identifiers/codes.

Note that the timing requirements per the DPP spec still apply
so this is not meant to be used with a human configurator but
within an automated agent which does a quick lookup of potential
identifiers/codes and can reply within the 200ms window.
2023-11-09 10:34:46 -06:00
James Prestwood
cf378e562e dpp: initial version of PKEX configurator support
The PKEX configurator role is currently limited to being a responder.
When started the configurator will listen on its current operating
channel for a PKEX exchange request. Once received it and the
encrypted key is properly decrypted it treats this peer as the
enrollee and won't allow configurations from other peers unless
PKEX is restarted. The configurator will encrypt and send its
encrypted ephemeral key in the PKEX exchange response. The enrollee
then sends its encrypted bootstrapping key (as commit-reveal request)
then the same for the configurator (as commit-reveal response).

After this, PKEX authentication begins. The enrollee is expected to
send the authenticate request, since its the initiator.
2023-11-09 10:26:59 -06:00
James Prestwood
a7d35a27a3 dpp: initial version of PKEX enrollee support
This is the initial support for PKEX enrollees acting as the
initiator. A PKEX initiator starts the protocol by broadcasting
the PKEX exchange request. This request contains a key encrypted
with the pre-shared PKEX code. If accepted the peer sends back
the exchange response with its own encrypted key. The enrollee
decrypts this and performs some crypto/hashing in order to establish
an ephemeral key used to encrypt its own boostrapping key. The
boostrapping key is encrypted and sent to the peer in the PKEX
commit-reveal request. The peer then does the same thing, encrypting
its own bootstrapping key and sending to the initiator as the
PKEX commit-reveal response.

After this, both peers have exchanged their boostrapping keys
securely and can begin DPP authentication, then configuration.

For now the enrollee will only iterate the default channel list
from the Easy Connect spec. Future upates will need to include some
way of discovering non-default channel configurators, but the
protocol needs to be ironed out first.
2023-11-09 10:23:01 -06:00
James Prestwood
9dbfac2756 doc: document Stop() correctly for both DPP interfaces
Stop() was separated more clearly between the two interfaces and will
now return NotFound if called on an interface that isn't currently
running.
2023-11-09 10:21:14 -06:00
James Prestwood
c193d36499 auto-t: fix testDPP after Stop() change
Stop() will now return NotFound if DPP is not running. This causes
the DPP test to fail since it calls this regardless if the protocol
already stopped. Ignore this exception since tests end in various
states, some stopped and some not.
2023-11-09 10:20:51 -06:00
James Prestwood
f9833665b7 dpp: introduce dpp_interface type, prep for PKEX
PKEX and DPP will share the same state machine since the DPP protocol
follows PKEX. This does pose an issue with the DBus interfaces
because we don't want DPP initiated by the SharedCode interface to
start setting properties on the DeviceProvisioning interface.

To handle this a dpp_interface enum is being introduced which binds
the dpp_sm object to a particular interface, for the life of the
protocol run. Once the protocol finishes the dpp_sm can be unbound
allowing either interface to use it again later.
2023-11-09 10:05:13 -06:00
James Prestwood
c0a356711d dpp-util: fix typo, 'REQUST' 2023-11-09 10:05:07 -06:00
Denis Kenzior
653122498a treewide: Fix compilation due to missing rtnetlink.h 2023-11-09 09:27:00 -06:00
Ronan Pigott
c574c80e27 tree-wide: correct the spelling Ghz -> GHz
This mispelling was present in the configuration, so I retained parsing
of the legacy BandModifier*Ghz options for compatibility. Without this
change anyone spelling GHz correctly in their configs would be very
confused.
2023-11-07 21:11:50 -06:00
James Prestwood
20f13184f2 doc: PKEX support for DPP
PKEX is part of the WFA EasyConnect specification and is
an additional boostrapping method (like QR codes) for
exchanging public keys between a configurator and enrollee.

PKEX operates over wifi and requires a key/code be exchanged
prior to the protocol. The key is used to encrypt the exchange
of the boostrapping information, then DPP authentication is
started immediately aftewards.

This can be useful for devices which don't have the ability to
scan a QR code, or even as a more convenient way to share
wireless credentials if the PSK is very secure (i.e. not a
human readable string).

PKEX would be used via the three DBus APIs on a new interface
SharedCodeDeviceProvisioning.

ConfigureEnrollee(a{sv}) will start a configurator with a
static shared code (optionally identifier) passed in as the
argument to this method.

StartEnrollee(a{sv}) will start a PKEX enrollee using a static
shared code (optionally identifier) passed as the argument to
the method.

StartConfigurator(o) will start a PKEX configurator and use the
agent specified by the path argument. The configurator will query
the agent for a specific code when an enrollee sends the initial
exchange message.

After the PKEX protocol is finished, DPP bootstrapping keys have
been exchanged and DPP Authentication will start, followed by
configuration.
2023-11-07 20:24:38 -06:00
James Prestwood
8864329928 netdev: handle/send beacon loss event 2023-11-07 12:15:05 -06:00
James Prestwood
e57cc5d4c6 station: start roam on beacon loss event
Beacon loss handling was removed in the past because it was
determined that this even always resulted in a disconnect. This
was short sighted and not always true. The default kernel behavior
waits for 7 lost beacons before emitting this event, then sends
either a few nullfuncs or probe requests to the BSS to determine
if its really gone. If these come back successfully the connection
will remain alive. This can give IWD some time to roam in some
cases so we should be handling this event.

Since beacon loss indicates a very poor connection the roam scan
is delayed by a few seconds in order to give the kernel a chance
to send the nullfuncs/probes or receive more beacons. This may
result in a disconnect, but it would have happened anyways.
Attempting a roam mainly handles the case when the connection can
be maintained after beacon loss, but is still poor.
2023-11-07 12:15:05 -06:00
James Prestwood
9107378efe station: provide new state in __station_connect_network
This is being done to allow the DPP module to work correctly. DPP
currently uses __station_connect_network incorrectly since it
does not (and cannot) change the state after calling. The only
way to connect with a state change is via station_connect_network
which requires a DBus method that triggered the connection; DPP
does not have this due to its potentially long run time.

To support DPP there are a few options:
 1. Pass a state into __station_connect_network (this patch)
 2. Support a NULL DBus message in station_connect_network. This
    would require several NULL checks and adding all that to only
    support DPP just didn't feel right.
 3. A 3rd connect API in station which wraps
    __station_connect_network and changes the state. And again, an
    entirely new API for only DPP felt wrong (I guess we did this
    for network_autoconnect though...)

Its about 50/50 between call sites that changed state after calling
and those that do not. Changing the state inside
__station_connect_network felt useful enough to cover the cases that
could benefit and the remaining cases could handle it easily enough:
 - network_autoconnect(), and the state is changed by station after
   calling so it more or less follows the same pattern just routes
   through network. This will now pass the CONNECTING_AUTO state
   from within network vs station.
 - The disconnect/reconnect path. Here the state is changed to
   ROAMING prior in order to avoid multiple state changes. Knowing
   this the same ROAMING state can be passed which won't trigger a
   state change.
 - Retrying after a failed BSS. The state changes on the first call
   then remains the same for each connection attempt. To support this
   the current station->state is passed to avoid a state change.
2023-11-02 20:40:07 -05:00
James Prestwood
5a78ebe895 dbus: add net.connman.iwd.SharedCodeAgent DBus interface 2023-11-02 20:31:05 -05:00
James Prestwood
c398672200 dpp: allow enrollee to be authentication initiator
Until now IWD only supported enrollees as responders (configurators
could do both). For PKEX it makes sense for the enrollee to be the
initiator because configurators in the area are already on their
operating channel and going off is inefficient. For PKEX, whoever
initiates also initiates authentication so for this reason the
authentication path is being opened up to allow enrollees to
initiate.
2023-11-02 20:30:18 -05:00
James Prestwood
b8bfbc141d dpp: fix config request header check
The check for the header was incorrect according to the spec.
Table 58 indicates that the "Query Response Info" should be set
to 0x00 for the configuration request. The frame handler was
expecting 0x7f which is the value for the config response frame.

Unfortunately wpa_supplicant also gets this wrong and uses 0x7f
in all cases which is likely why this value was set incorrectly
in IWD. The issue is that IWD's config request is correct which
means IWD<->IWD configuration is broken. (and wpa_supplicant as
a configurator likely doesn't validate the config request).

Fix this by checking both 0x7f and 0x00 to handle both
supplicants.
2023-11-02 20:28:06 -05:00
James Prestwood
a943a81f87 dpp: remove scan_periodic_stop calls
Stopping periodic scans and not restarting them prevents autoconnect
from working again if DPP (or the post-DPP connect) fails. Since
the DPP offchannel work is at a higher priority than scanning (and
since new offchannels are queue'd before canceling) there is no risk
of a scan happening during DPP so its safe to leave periodic scans
running.
2023-11-02 20:27:59 -05:00
James Prestwood
320041eaf2 station: rate limit packet loss roam scans
The packet loss handler puts a higher priority on roaming compared
to the low signal roam path. This is generally beneficial since this
event usually indicates some problem with the BSS and generally is
an indicator that a disconnect will follow sometime soon.

But by immediately issuing a scan we run the risk of causing many
successive scans if more packet loss events arrive following
the roam scans (and if no candidates are found). Logs provided
further.

To help with this handle the first event with priority and
immediately issue a roam scan. If another event comes in within a
certain timeframe (2 seconds) don't immediately scan, but instead
rearm the roam timer instead of issuing a scan. This also handles
the case of a low signal roam scan followed by a packet loss
event. Delaying the roam will at least provide some time for packets
to get out in between roam scans.

Logs were snipped to be less verbose, but this cycled happened
5 times prior. In total 7 scans were issued in 5 seconds which may
very well have been the reason for the local disconnect:

Oct 27 16:23:46 src/station.c:station_roam_failed() 9
Oct 27 16:23:46 src/wiphy.c:wiphy_radio_work_done() Work item 29 done
Oct 27 16:23:47 src/netdev.c:netdev_mlme_notify() MLME notification Notify CQM(64)
Oct 27 16:23:47 src/station.c:station_packets_lost() Packets lost event: 10
Oct 27 16:23:47 src/station.c:station_roam_scan() ifindex: 9
Oct 27 16:23:47 src/wiphy.c:wiphy_radio_work_insert() Inserting work item 30
Oct 27 16:23:47 src/wiphy.c:wiphy_radio_work_next() Starting work item 30
Oct 27 16:23:47 src/station.c:station_start_roam() Using cached neighbor report for roam
Oct 27 16:23:47 src/scan.c:scan_notify() Scan notification Trigger Scan(33)
Oct 27 16:23:47 src/scan.c:scan_request_triggered() Active scan triggered for wdev a
Oct 27 16:23:47 src/scan.c:scan_notify() Scan notification New Scan Results(34)
Oct 27 16:23:47 src/netdev.c:netdev_link_notify() event 16 on ifindex 9
... scan results ...
Oct 27 16:23:47 src/station.c:station_roam_failed() 9
Oct 27 16:23:47 src/wiphy.c:wiphy_radio_work_done() Work item 30 done
Oct 27 16:23:47 src/netdev.c:netdev_mlme_notify() MLME notification Notify CQM(64)
Oct 27 16:23:47 src/station.c:station_packets_lost() Packets lost event: 10
Oct 27 16:23:47 src/station.c:station_roam_scan() ifindex: 9
Oct 27 16:23:47 src/wiphy.c:wiphy_radio_work_insert() Inserting work item 31
Oct 27 16:23:47 src/wiphy.c:wiphy_radio_work_next() Starting work item 31
Oct 27 16:23:47 src/station.c:station_start_roam() Using cached neighbor report for roam
Oct 27 16:23:47 src/scan.c:scan_notify() Scan notification Trigger Scan(33)
Oct 27 16:23:47 src/scan.c:scan_request_triggered() Active scan triggered for wdev a
Oct 27 16:23:48 src/scan.c:scan_notify() Scan notification New Scan Results(34)
Oct 27 16:23:48 src/netdev.c:netdev_link_notify() event 16 on ifindex 9
... scan results ...
Oct 27 16:23:48 src/station.c:station_roam_failed() 9
Oct 27 16:23:48 src/wiphy.c:wiphy_radio_work_done() Work item 31 done
Oct 27 16:23:48 src/netdev.c:netdev_mlme_notify() MLME notification Notify CQM(64)
Oct 27 16:23:48 src/station.c:station_packets_lost() Packets lost event: 10
Oct 27 16:23:48 src/station.c:station_roam_scan() ifindex: 9
Oct 27 16:23:48 src/wiphy.c:wiphy_radio_work_insert() Inserting work item 32
Oct 27 16:23:48 src/wiphy.c:wiphy_radio_work_next() Starting work item 32
Oct 27 16:23:48 src/station.c:station_start_roam() Using cached neighbor report for roam
Oct 27 16:23:48 src/scan.c:scan_notify() Scan notification Trigger Scan(33)
Oct 27 16:23:48 src/scan.c:scan_request_triggered() Active scan triggered for wdev a
Oct 27 16:23:49 src/netdev.c:netdev_link_notify() event 16 on ifindex 9
Oct 27 16:23:49 src/netdev.c:netdev_mlme_notify() MLME notification Del Station(20)
Oct 27 16:23:49 src/netdev.c:netdev_mlme_notify() MLME notification Deauthenticate(39)
Oct 27 16:23:49 src/netdev.c:netdev_deauthenticate_event()
Oct 27 16:23:49 src/netdev.c:netdev_mlme_notify() MLME notification Disconnect(48)
Oct 27 16:23:49 src/netdev.c:netdev_disconnect_event()
Oct 27 16:23:49 Received Deauthentication event, reason: 4, from_ap: false
2023-10-30 09:43:12 -05:00