driver/wifi: rework CTRL interface manager

This commit streamlines the interaction between the Wifi::Manager
and the wpa_supplicant's CTRL interface.

As user-facing changes it alters some default settings and introduces
new features:

* Every configured network now needs to explicitly have its
  'auto_connect' (to be considered an option for joining) attribute
  set to 'true' whereas this was previously the default value if the
  attribute was not set at all.

* The 'log_level' attribute is added and configures the supplicant's
  verbosity. Valid values correspond to levels used by the supplicant
  and are as follows 'excessive', 'msgdump', 'debug', 'info', 'warning'
  and 'error'. The default value is 'error' and configures the least
  amount of verbosity.

* The 'bgscan' attribute may be used to configure the way the
  supplicant performs background-scanning to steer or rather optimize
  roaming decision within the same network. The default value is set
  to 'simple:30:-70:600'. It can be disabled by specifying an empty
  value, e.g. 'bgscan=""'.

* The 'verbose_state' attribute was removed alltogether and similar
  functionality is now coverted by 'verbose' attribute.

Implementation-wise the internals changed significantly and are
outlined in the following paragraphs.

Formerly the interaction between the manager and the supplicant
was handled in an apparent way where the internal state of each
interaction was in plain sight. This made the flow cumbersome to
follow and therefor each interaction is now confined to its own
'Action' object that encapsulates the ping-pong of commands and
responses between the manager and the supplicant. All actions are
processed in an sequential way and thus there is no longer any
need to defer pending actions depending on the interal state of
the current interaction. Configuration changes as well as events
issued by the supplicant where new actions can be created are
handled in this fashion. Of note are both signal-handlers,
'_handle_cmds' and '_handle_events' respectively.

The state report, which provides the information about the current
state of connectivity to a given wireless network, was dealt with
in the same vein and its handling was spread across the manager
implementation. Again, to make it easier to follow, the generation
of the state report is now purely driven by the 'Join_state' object.
This object encapsulates the state of connectivity and is normally
updated by events issued from the supplicant (see '_handle_events').
It is also incorporated when handling command responses (see
'_handle_cmds').

Handling of timed-actions, like scan and signal quality
update requests, was done by setting a timeout at the Timer session
directly and thus only one timed-action could be pending at any time.
This excluded dealing with timed-actions like connected-scanning
and signal quality polling concurrently. This was changed and now
a One_shot_timeout is used to programm each concurrent timed-action.

For implementing the communication channel for the CTRL interface the
manager and supplicant use a shared memory buffer, the Msg_buffer.
Since the CTRL interface for Genode was implemented using C, some
shenanigans were performed to access the memory buffer. Now the
CTRL interface implementation uses C++ and only exports the functions
required by the supplicant as C. This simplifies the usage of the
Msg_buffer and allows for removing the global functions needed for
synchronizing the Msg_buffer access as those are now part of the
object itself via the 'Notify_interface'.

Fixes #5341.
This commit is contained in:
Josef Söntgen 2024-09-03 16:06:02 +02:00 committed by Christian Helmuth
parent 6b474e059c
commit d4e4b8bf60
23 changed files with 2633 additions and 2276 deletions

View File

@ -14,33 +14,90 @@
#ifndef _WIFI__CTRL_H_ #ifndef _WIFI__CTRL_H_
#define _WIFI__CTRL_H_ #define _WIFI__CTRL_H_
#ifdef __cplusplus #include <util/string.h>
extern "C" {
#endif /* __cplusplus */
#define WPA_CTRL_FD 51 namespace Wifi {
struct Msg_buffer /*
* FD used to poll CTRL state from the supplicant.
*/
enum { CTRL_FD = 51, };
struct Msg_buffer;
struct Notify_interface : Genode::Interface
{
virtual void submit_response() = 0;
virtual void submit_event() = 0;
virtual void block_for_processing() = 0;
};
void ctrl_init(Msg_buffer &);
} /* namespace Wifi */
struct Wifi::Msg_buffer
{ {
unsigned char recv[4096*8]; char send[4096];
unsigned char send[4096];
unsigned recv_id;
unsigned send_id; unsigned send_id;
unsigned char event[1024];
char recv[4096*8];
unsigned recv_id;
unsigned last_recv_id;
char event[1024];
unsigned event_id; unsigned event_id;
} __attribute__((packed)); unsigned last_event_id;
Notify_interface &_notify;
Msg_buffer(Notify_interface &notify)
: _notify { notify } { }
/*
* Member functions below are called by the
* CTRL interface.
*/
void notify_response() const {
_notify.submit_response(); }
void notify_event() const {
_notify.submit_event(); }
void block_for_processing() {
_notify.block_for_processing(); }
/*
* Member functions below are called by the
* Manager.
*/
void with_new_reply(auto const &fn)
{
char const *msg = reinterpret_cast<char const*>(recv);
/* return early */
if (last_recv_id == recv_id)
return;
last_recv_id = recv_id;
fn(msg);
}
void with_new_event(auto const &fn)
{
char const *msg = reinterpret_cast<char const*>(event);
if (last_event_id == event_id)
return;
last_event_id = event_id;
fn(msg);
}
};
void wpa_ctrl_set_fd(void); void wpa_ctrl_set_fd(void);
void *wifi_get_buffer(void);
void wifi_notify_cmd_result(void);
void wifi_block_for_processing(void);
void wifi_notify_event(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _WIFI__CTRL_H_ */ #endif /* _WIFI__CTRL_H_ */

View File

@ -36,12 +36,20 @@ namespace Wifi {
bool rfkill_blocked(); bool rfkill_blocked();
/** /**
* Set RFKILL state from the frontend * Set RFKILL state from the manager
* *
* May be only called from an EP context. * May be only called from an EP context.
*/ */
void set_rfkill(bool); void set_rfkill(bool);
/**
* Trigger RFKILL notification signal
*
* Used by the supplicants RFKILL driver to notify
* the management-layer.
*/
void rfkill_notify();
} /* namespace Wifi */ } /* namespace Wifi */
#endif /* _WIFI__RFKILL_H_ */ #endif /* _WIFI__RFKILL_H_ */

View File

@ -10,7 +10,8 @@ CC_OPT += -Wno-unused-function
CC_CXX_OPT += -fpermissive CC_CXX_OPT += -fpermissive
SRC_C += main.c ctrl_iface_genode.c SRC_C += main.c
SRC_CC += ctrl_iface_genode.cc
INC_DIR += $(REP_DIR)/include INC_DIR += $(REP_DIR)/include

View File

@ -22,6 +22,7 @@ wifi_ifname T
_ZN4Wifi20firmware_get_requestEv T _ZN4Wifi20firmware_get_requestEv T
_ZN4Wifi26firmware_establish_handlerERNS_24Firmware_request_handlerE T _ZN4Wifi26firmware_establish_handlerERNS_24Firmware_request_handlerE T
_ZN4Wifi10set_rfkillEb T _ZN4Wifi10set_rfkillEb T
_ZN4Wifi13rfkill_notifyEv T
_ZN4Wifi14rfkill_blockedEv T _ZN4Wifi14rfkill_blockedEv T
_ZN4Wifi24rfkill_establish_handlerERNS_27Rfkill_notification_handlerE T _ZN4Wifi24rfkill_establish_handlerERNS_27Rfkill_notification_handlerE T
convert_errno_from_linux T convert_errno_from_linux T

View File

@ -93,7 +93,7 @@ The nl80211 driver patch contains the following changes:
goto err; goto err;
- global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); - global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ global->ioctl_sock = 42; + global->ioctl_sock = 12345; /* arbitrarily chosen number b/c it won't be used anyway */
if (global->ioctl_sock < 0) { if (global->ioctl_sock < 0) {
wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s", wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
strerror(errno)); strerror(errno));

View File

@ -1 +1 @@
aa7af61abdac82e5a783d8371be9cb8d1a41fceb 15974e67af9112d919f1625a5c2ed2d996cdbdfb

View File

@ -133,8 +133,8 @@ install_config {
<config> <config>
<rom name="wifi_config"> <rom name="wifi_config">
<inline description="CONNECT"> <inline description="CONNECT">
<wifi_config connected_scan_interval="0" scan_interval="5" rfkill="no" verbose="no" verbose_state="no"> <wifi_config>
<network ssid="} [wifi_ssid] {" protection="WPA2" passphrase="} [wifi_psk] {"/> <network ssid="} [wifi_ssid] {" protection="WPA2" passphrase="} [wifi_psk] {" auto_connect="true"/>
</wifi_config> </wifi_config>
</inline> </inline>
<sleep milliseconds="600000"/> <!-- 10 minutes --> <sleep milliseconds="600000"/> <!-- 10 minutes -->

View File

@ -1,5 +1,5 @@
The wifi component is a port of the Linux mac802.11 stack as well as The wifi component consists of a port of the Linux mac802.11 stack as well
libnl and wpa_supplicant to Genode. Depending on the used platform it as libnl and wpa_supplicant to Genode. Depending on the used platform it
features a selection of drivers for wireless devices. For example on the features a selection of drivers for wireless devices. For example on the
PC platform it contains the ath9k, iwlwifi and rtlwifi drivers for PCI(e) PC platform it contains the ath9k, iwlwifi and rtlwifi drivers for PCI(e)
devices. devices.
@ -10,14 +10,18 @@ The 'wifi' binary is the generic management part that includes the Wifi
configuration interface and the 'wpa_supplicant'. A suitable driver library configuration interface and the 'wpa_supplicant'. A suitable driver library
is loaded at run-time (see section [Debugging]). is loaded at run-time (see section [Debugging]).
To start the component on the PC platform the following configuration snippet
can be used: Configuration
~~~~~~~~~~~~~
This configuration snippet shows how to start the component on the PC
platform.
!<start name="wifi" caps="250"> !<start name="wifi" caps="250">
! <resource name="RAM" quantum="32M"/> ! <resource name="RAM" quantum="32M"/>
! <provides><service name="Nic"/></provides> ! <provides><service name="Nic"/></provides>
! <config> ! <config>
! <libc stdout="/dev/null" stderr="/dev/null" rtc="/dev/rtc"/> ! <libc stdout="/dev/log" stderr="/dev/null" rtc="/dev/rtc"/>
! <vfs> ! <vfs>
! <dir name="dev"> <log/> <null/> <rtc/> ! <dir name="dev"> <log/> <null/> <rtc/>
! <jitterentropy name="random"/> ! <jitterentropy name="random"/>
@ -38,14 +42,14 @@ can be used:
! </route> ! </route>
!</start> !</start>
On other platforms the wifi library will be different. The following On other platforms the wifi library will be different. So, the
snippet illustrates the use of the driver on the PinePhone: following snippet illustrates the use of the driver on the PinePhone.
!<start name="wifi" caps="250"> !<start name="wifi" caps="250">
! <resource name="RAM" quantum="32M"/> ! <resource name="RAM" quantum="32M"/>
! <provides><service name="Nic"/></provides> ! <provides><service name="Nic"/></provides>
! <config> ! <config>
! <libc stdout="/dev/null" stderr="/dev/null" rtc="/dev/rtc"/> ! <libc stdout="/dev/log" stderr="/dev/null" rtc="/dev/rtc"/>
! <vfs> ! <vfs>
! <dir name="dev"> <log/> <null/> <rtc/> ! <dir name="dev"> <log/> <null/> <rtc/>
! <jitterentropy name="random"/> ! <jitterentropy name="random"/>
@ -79,55 +83,95 @@ directory in the driver's local VFS. It is up to the configuration
how those files are made available. In these examples they are how those files are made available. In these examples they are
contained in an '.tar' archive that is request as a ROM module. contained in an '.tar' archive that is request as a ROM module.
The driver will request access to the ROM module 'wifi_config' to The driver will request access to the 'wifi_config' ROM module that
connect to a network: contains its actual configuration in the '<wifi_config>' node. This
node features the following attributes.
!<wifi_config connected_scan_interval="30" scan_interval="10" rfkill="no"> * :scan_interval: sets the time interval in seconds in which scan
! <network ssid="Foobar" protection="WPA2" passphrase="allyourbase"/> operations are requested and is used when not already connected
!</wifi_config> to a network. The default is 5 seconds.
To temporarily prevent any radio activity, the 'rfkill' attribute * :update_quality_interval: sets the time interval in which the current
can be set to 'true'. signal quality of the connected access point is updated (RSSI polling).
The default value is 30 seconds.
If the network is protected by, e.g., WPA/WPA2/WPA3, the protection type, * :rfkill: allows to temporarily prevent any radio activity. The
either 'WPA', 'WPA2' or 'WPA3' as well as the the passphrase have to be default is 'false'.
specified.
The 'bssid' attribute can be used to select a specifc accesspoint within a
network. Of all attributes only the 'ssid' attribute is mandatory, all others
are optional and should only be used when needed.
The configuration may contain more than one network. In This case the driver * :bgscan: is an expert option that configures the way the
will try to select the best one it gets a response from. To prevent it supplicant performs background scanning to steer or rather optimize
from automatically joining the network the 'auto_connect' attribute must be roaming decisions within the same network (SSID). The syntax of the
set to 'false'; the default value is 'true'. If the 'explicit_scan' attribute option string corresponds to the original WPA-supplicant 'bgscan' option.
is set, the driver will pro-actively scan for a hidden network with the given The default value is set to 'simple:30:-70:600'. This functionality can
SSID: be disabled by specifying an empty value, e.g. 'bgscan=""'. If bgscan is
disabled the 'accesspoints' report will not be updated while the
supplicant is connected to a network.
!<wifi_config connected_scan_interval="30" scan_interval="10"> * :log_level: allows for steering the verbosity of the supplicant
! <network ssid="Zero" protection="WPA2" passphrase="allyourbase"/> and may assist while diagnosing problems with the driver.
Valid values correspond to levels used by the supplicant
and are as follows 'excessive', 'msgdump', 'debug', 'info',
'warning' and 'error'. The default value is 'error' and configures
the least amount of verbosity.
* :verbose: allows for logging of diagnostic messages generated
by the managing portion of the driver. The default is 'false'.
Besides those attributes the '<wifi_config>' node can host one or
more '<network>' nodes. Such a node describes the parameters of
a network and has the following attributes.
* :ssid: sets the name of the network.
Note: the SSID is copied verbatim and at the moment, there is no way
to express or escape non alphanumeric characters.
* :bssid: can be used to select a specific access point within a
network.
* :protection: specifies the used protection mechanism of the
network. Valid values are 'WPA', 'WPA2', 'WPA3' and 'NONE'.
The last one is used in case the network uses other means of
protection and access is open.
Note: currently only personal WPA protection using a pre-shared-key
(PSK) is supported.
* :passphrase: sets the PSK that is required should the
network be protected.
* :auto_connect: allows for joining a network automatically. The
default value is 'true'. When more than one network is configured
with enabled 'auto_connect' the driver will try to select the best
one it gets a response from.
Note: If configured auto-connect networks overlap in locality, the
driver might switch dynamically between the networks.
* :explicit_scan: must be set to explicitly scan for hidden networks.
The default is 'false' and it should only be set for networks
that are hidden indeed.
Of all attributes solely the 'ssid' attribute is mandatory and all
others are optional. They should be used when needed only.
The following exemplary snippet showcases a config for two networks where
the first one should be automatically considered for joining and uses 'WPA2'
while the second one is hidden but should show up in the scan results and
uses 'WPA'.
!<wifi_config scan_interval="10" update_quality_interval="30">
! <network ssid="Zero" protection="WPA2" passphrase="allyourbase"
! auto_connect="true"/>
! <network ssid="Skynet" protection="WPA" passphrase="12345678" ! <network ssid="Skynet" protection="WPA" passphrase="12345678"
! explicit_scan="true" auto_connect="false"/> ! explicit_scan="true"/>
!</wifi_config> !</wifi_config>
By default, the driver scans for available networks only when not
connected. This can be changed with the 'connected_scan_interval'
attribute, which specifies the interval for connected scans in
seconds and directly influences any roaming decision, i.e., select
a better fit accesspoint for the configured network.
In addition, by specifing 'update_quality_interval', the driver will The wifi driver uses two distinct reports, 'state' and 'accesspoints',
every so often update the current signal quality of the established to communicate its state of connectivity and information about the wireless
connection to the accesspoint. Note that this option is only useable when access points in the vicinity to other components.
the 'connected_scan_interval' is set to '0' as both options are mutually This exemplary 'accesspoints' report shows its general structure.
exclusive.
Also, the driver can be switched to verbose logging during runtime
by setting the 'verbose' or 'verbose_state' attribute to 'true'.
The wifi driver creates two distinct reports to communicate its state and
information about the wireless infrastructure to other components. The
first one is a list of all available accesspoints. The following examplary
report shows its general structure:
!<accesspoints> !<accesspoints>
! <accesspoint ssid="skynet" bssid="00:01:02:03:04:05" quality="40"/> ! <accesspoint ssid="skynet" bssid="00:01:02:03:04:05" quality="40"/>
@ -135,32 +179,50 @@ report shows its general structure:
! <accesspoint ssid="foobar" bssid="01:02:03:04:05:07" quality="10" protection="WPA2"/> ! <accesspoint ssid="foobar" bssid="01:02:03:04:05:07" quality="10" protection="WPA2"/>
!</accesspoints> !</accesspoints>
Each accesspoint node has attributes that contain the SSID and the BSSID The '<accesspoints>' node can contain a fluctuating number of '<accesspoint>'
of the accesspoint as well as the link quality (signal strength). These nodes that describe an access point with the following attributes.
attributes are mandatory. If the network is protected, the node will also
have an attribute describing the type of protection in addition.
The second report provides information about the state of the connection * :ssid: specifies the name of the network the access point advertises.
to the currently connected accesspoint: Empty SSIDs are not reported.
* :bssid: specifies the physical address of the access point.
* :freq: specifies the frequency used by the access point.
* :quality: specifies the approximated link quality (calculated from the
RSSI value).
* :protection: specifies which kind of protection is employed by the access
point.
Note: when a mixed protection is used by the network, like WPA2-PSK and
WPA3-PSK mixed-mode, only the strongest protection (WPA3-PSK) is
advertised.
The 'state' report provides information about the state of the connectivity
and looks as follows.
!<state> !<state>
! <accesspoint ssid="foobar" bssid="01:02:03:04:05:06" quality="70" freq="2418" state="connected"/> ! <accesspoint ssid="foobar" bssid="01:02:03:04:05:06" quality="70" freq="2418" state="connected"/>
!</state> !</state>
Valid state values are 'connected', 'disconnected', 'connecting'. Depending The '<state>' node encompasses one '<accesspoint>' node that has the
on the state, there are additional attributes that can be checked. In case following additional attributes beside the ones already discussed.
of an authentication error, e.g. the passphrase is wrong, the 'auth_failure'
attribute will be set to 'true'. The 'rfkilled' attribute is set to 'true'
if a disconnect was triggered by disabling the radio activity via setting
the 'rfkill' attribute. It can also contain the optional 'quality' attribute
to denote the current signal quality (see 'update_quality_interval').
By subscribing to both reports and providing the required 'wifi_config' ROM * :state: specifies the actual state of connectivity. Valid values
module, a component is able control the wireless driver. are 'connected', 'connecting' and 'disconnected'.
Currently only WPA/WPA2/WPA3 protection using a passphrase is supported and * :auth_failure: is an optional attribute and set to 'true' in case
the SSID is copied verbatim. At the moment, there is no way to express or the PSK was wrong
escape non alphanumeric characters.
* :rfkilled: is an optional attribute and set to 'true' whenever
radio activity was temporarily disabled.
* :not_found: is an optional attribute and is only set when a single
auto-connect network was configured but could not be found.
By subscribing to both reports and providing the required 'wifi_config'
ROM module, a component is able control the wireless driver.
The driver optionally reports the following information under the The driver optionally reports the following information under the
label "devices" if requested in the config as depicted. label "devices" if requested in the config as depicted.
@ -177,7 +239,7 @@ As mentioned in the introduction the 'wifi' component is special in the
regard that the actual driver is provided as a shared-object to better isolate regard that the actual driver is provided as a shared-object to better isolate
it from the the driver binary that is a Libc::Component managing the it from the the driver binary that is a Libc::Component managing the
'wpa_supplicant'. Since this code and in return the binary is the same for each 'wpa_supplicant'. Since this code and in return the binary is the same for each
platform it is linked against an artifical 'wifi' library that only exists as platform it is linked against an artificial 'wifi' library that only exists as
an ABI stub created via 'lib/symbols/wifi'. In case the driver is integrated an ABI stub created via 'lib/symbols/wifi'. In case the driver is integrated
via depot archives this is, besides setting the proper ROM routes, of no via depot archives this is, besides setting the proper ROM routes, of no
concern. However, when the driver is built without the depot, the boot image concern. However, when the driver is built without the depot, the boot image
@ -198,7 +260,7 @@ rather then stub ABI library. This is achieved by adapting the driver's
![…] ![…]
'LIBS' must be changed as follows in case the PC wifi driver library is 'LIBS' must be changed as follows in case the PC wifi driver library is
used: used.
!LIBS := base pc_wifi !LIBS := base pc_wifi

File diff suppressed because it is too large Load Diff

View File

@ -28,68 +28,12 @@
/* local includes */ /* local includes */
#include "util.h" #include "util.h"
#include "wpa.h" #include "wpa.h"
#include "frontend.h" #include "manager.h"
#include "access_firmware.h" #include "access_firmware.h"
using namespace Genode; using namespace Genode;
static Msg_buffer _wifi_msg_buffer;
static Wifi::Frontend *_wifi_frontend = nullptr;
/**
* Notify front end about command processing
*
* Called by the CTRL interface after wpa_supplicant has processed
* the command.
*/
void wifi_block_for_processing(void)
{
if (!_wifi_frontend) {
warning("frontend not available, dropping notification");
return;
}
/*
* Next time we block as long as the front end has not finished
* handling our previous request
*/
_wifi_frontend->block_for_processing();
/* XXX hack to trick poll() into returning faster */
wpa_ctrl_set_fd();
}
void wifi_notify_cmd_result(void)
{
if (!_wifi_frontend) {
warning("frontend not available, dropping notification");
return;
}
Signal_transmitter(_wifi_frontend->result_sigh()).submit();
}
/**
* Notify front end about triggered event
*
* Called by the CTRL interface whenever wpa_supplicant has triggered
* a event.
*/
void wifi_notify_event(void)
{
if (!_wifi_frontend) {
Genode::warning("frontend not available, dropping notification");
return;
}
Signal_transmitter(_wifi_frontend->event_sigh()).submit();
}
/* exported by wifi.lib.so */ /* exported by wifi.lib.so */
extern void wifi_init(Genode::Env&, Genode::Blockade&); extern void wifi_init(Genode::Env&, Genode::Blockade&);
extern void wifi_set_rfkill_sigh(Genode::Signal_context_capability); extern void wifi_set_rfkill_sigh(Genode::Signal_context_capability);
@ -101,8 +45,8 @@ struct Main
{ {
Env &env; Env &env;
Constructible<Wpa_thread> _wpa; Constructible<Wpa_thread> _wpa;
Constructible<Wifi::Frontend> _frontend; Constructible<Wifi::Manager> _manager;
struct Request_handler : Wifi::Firmware_request_handler struct Request_handler : Wifi::Firmware_request_handler
{ {
@ -173,28 +117,17 @@ struct Main
/* prepare Lx_kit::Env */ /* prepare Lx_kit::Env */
wifi_init(env, _wpa_startup_blockade); wifi_init(env, _wpa_startup_blockade);
_frontend.construct(env, _wifi_msg_buffer); _manager.construct(env);
_wifi_frontend = &*_frontend;
Wifi::rfkill_establish_handler(*_wifi_frontend); Wifi::rfkill_establish_handler(*_manager);
Wifi::firmware_establish_handler(_request_handler); Wifi::firmware_establish_handler(_request_handler);
Wifi::ctrl_init(_manager->msg_buffer());
_wpa.construct(env, _wpa_startup_blockade); _wpa.construct(env, _wpa_startup_blockade);
} }
}; };
/**
* Return shared-memory message buffer
*
* It is used by the wpa_supplicant CTRL interface.
*/
void *wifi_get_buffer(void)
{
return &_wifi_msg_buffer;
}
void Libc::Component::construct(Libc::Env &env) void Libc::Component::construct(Libc::Env &env)
{ {
static Main server(env); static Main server(env);

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/* /*
* \brief Wifi front end utilities * \brief Wifi manager utilities
* \author Josef Soentgen * \author Josef Soentgen
* \date 2018-07-23 * \date 2018-07-23
*/ */
@ -59,9 +59,9 @@ namespace Util {
} }
} }
/********************************** /********************************
** Front end specific utilities ** ** Manager-specific utilities **
**********************************/ ********************************/
inline unsigned approximate_quality(char const *str) inline unsigned approximate_quality(char const *str)
{ {

View File

@ -460,10 +460,10 @@ static int rfkill_task_function(void *arg)
bool rfkilled = !!rfkill_get_global_sw_state(RFKILL_TYPE_WLAN); bool rfkilled = !!rfkill_get_global_sw_state(RFKILL_TYPE_WLAN);
if (rfkilled != _rfkill_state.blocked) if (rfkilled != _rfkill_state.blocked) {
rfkill_switch_all(RFKILL_TYPE_WLAN, !!_rfkill_state.blocked); rfkill_switch_all(RFKILL_TYPE_WLAN, !!_rfkill_state.blocked);
_rfkill_state.rfkilled = !!_rfkill_state.blocked;
_rfkill_state.rfkilled = rfkilled; }
lx_emul_task_schedule(true); lx_emul_task_schedule(true);
} }

View File

@ -342,7 +342,11 @@ void Wifi::set_rfkill(bool blocked)
*/ */
lx_emul_task_unblock(uplink_task_struct_ptr); lx_emul_task_unblock(uplink_task_struct_ptr);
Lx_kit::env().scheduler.execute(); Lx_kit::env().scheduler.execute();
}
void Wifi::rfkill_notify()
{
if (_wlan_ptr->rfkill_helper.constructed()) if (_wlan_ptr->rfkill_helper.constructed())
_wlan_ptr->rfkill_helper->submit_notification(); _wlan_ptr->rfkill_helper->submit_notification();
} }

View File

@ -54,6 +54,8 @@ static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
} else { } else {
rfkill->cfg->unblocked_cb(rfkill->cfg->ctx); rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
} }
Wifi::rfkill_notify();
} }
} }

View File

@ -1,250 +0,0 @@
/*
* \brief WPA Supplicant frontend
* \author Josef Soentgen
* \date 2018-07-18
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/*
* based on:
*
* WPA Supplicant / UNIX domain socket -based control interface
* Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
/* wpa_supplicant includes */
#include "includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/list.h"
#include "common/ctrl_iface_common.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "ctrl_iface.h"
/* rep includes */
#include <wifi/ctrl.h>
typedef unsigned unaligned_unsigned __attribute__ ((aligned (1)));
struct ctrl_iface_priv {
struct wpa_supplicant *wpa_s;
int fd;
int level;
/* TODO replace w/ Msg_buffer */
char *send_buffer;
size_t send_buffer_size;
unaligned_unsigned *send_id;
char *recv_buffer;
size_t recv_buffer_size;
unaligned_unsigned *recv_id;
unsigned last_recv_id;
char *event_buffer;
size_t event_buffer_size;
unaligned_unsigned *event_id;
};
struct ctrl_iface_global_priv {
struct wpa_global *global;
};
extern void nl_set_wpa_ctrl_fd(void);
void wpa_ctrl_set_fd()
{
nl_set_wpa_ctrl_fd();
}
static void send_reply(struct ctrl_iface_priv *priv, char const *txt, size_t len)
{
char *msg = priv->send_buffer;
size_t mlen = priv->send_buffer_size;
if (len >= mlen) {
len = mlen - 1;
}
memcpy(msg, txt, len);
msg[len] = 0;
(*priv->send_id)++;
}
/*
* This function is called by wpa_supplicant whenever it receives a
* command via the CTRL interface, i.e. the front end has sent a new
* message.
*/
static void wpa_supplicant_ctrl_iface_receive(int fd, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_supplicant *wpa_s = eloop_ctx;
struct ctrl_iface_priv *priv = sock_ctx;
char *msg = priv->recv_buffer;
unsigned const recv_id = *priv->recv_id;
char *reply = NULL;
size_t reply_len = 0;
if (msg[0] == 0 || recv_id == priv->last_recv_id) { return; }
priv->last_recv_id = recv_id;
reply = wpa_supplicant_ctrl_iface_process(wpa_s, msg,
&reply_len);
if (reply) {
wifi_block_for_processing();
send_reply(priv, reply, reply_len);
wifi_notify_cmd_result();
os_free(reply);
} else
if (reply_len == 1) {
wifi_block_for_processing();
send_reply(priv, "FAIL", 4);
wifi_notify_cmd_result();
} else
if (reply_len == 2) {
wifi_block_for_processing();
send_reply(priv, "OK", 2);
wifi_notify_cmd_result();
}
}
static void send_event(struct ctrl_iface_priv *priv, char const *txt, size_t len)
{
char *msg = priv->event_buffer;
size_t mlen = priv->event_buffer_size;
if (len >= mlen) {
len = mlen - 1;
}
memcpy(msg, txt, len);
msg[len] = 0;
(*priv->event_id)++;
}
/*
* This function is called by wpa_supplicant whenever it wants to
* forward some message. We filter these messages and forward only
* those, which are of interest to the front end.
*/
static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
enum wpa_msg_type type,
const char *txt, size_t len)
{
/* there is not global support */
if (type == WPA_MSG_ONLY_GLOBAL) { return; }
struct wpa_supplicant *wpa_s = ctx;
if (wpa_s == NULL) { return; }
struct ctrl_iface_priv *priv = wpa_s->ctrl_iface;
if (!priv || level < priv->level) { return; }
/*
* Filter messages and only forward events the front end cares
* about or rather knows how to handle.
*/
int const forward =
strncmp(txt, "CTRL-EVENT-SCAN-RESULTS", 23) == 0
|| strncmp(txt, "CTRL-EVENT-CONNECTED", 20) == 0
|| strncmp(txt, "CTRL-EVENT-DISCONNECTED", 23) == 0
|| strncmp(txt, "CTRL-EVENT-NETWORK-NOT-FOUND", 28) == 0
/* needed to detect connecting state */
|| strncmp(txt, "SME: Trying to authenticate", 27) == 0
;
if (!forward) { return; }
wifi_notify_event();
send_event(priv, txt, len);
}
struct ctrl_iface_priv *
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv;
priv = os_zalloc(sizeof(*priv));
if (priv == NULL) { return NULL; }
if (wpa_s->conf->ctrl_interface == NULL) {
return priv;
}
struct Msg_buffer *msg_buffer = (struct Msg_buffer*)wifi_get_buffer();
priv->recv_buffer = (char *)msg_buffer->send;
priv->recv_buffer_size = sizeof(msg_buffer->send);
priv->send_buffer = (char *)msg_buffer->recv;
priv->send_buffer_size = sizeof(msg_buffer->recv);
priv->send_id = &msg_buffer->recv_id;
priv->recv_id = &msg_buffer->send_id;
priv->event_buffer = (char *)msg_buffer->event;
priv->event_buffer_size = sizeof(msg_buffer->event);
priv->event_id = &msg_buffer->event_id;
priv->level = MSG_INFO;
priv->fd = WPA_CTRL_FD;
eloop_register_read_sock(priv->fd,
wpa_supplicant_ctrl_iface_receive,
wpa_s, priv);
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
return priv;
}
void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
struct ctrl_iface_priv *priv)
{
(void)wpa_s;
os_free(priv);
}
void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) { }
struct ctrl_iface_global_priv *
wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
{
struct ctrl_iface_global_priv *priv;
priv = os_zalloc(sizeof(*priv));
return priv;
}
void wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *p)
{
os_free(p);
}

View File

@ -0,0 +1,240 @@
/*
* \brief Genode-specific WPA supplicant ctrl_iface
* \author Josef Soentgen
* \date 2018-07-18
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/*
* based on:
*
* WPA Supplicant / UNIX domain socket -based control interface
* Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
/* wpa_supplicant includes */
extern "C" {
#include "includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/list.h"
#include "common/ctrl_iface_common.h"
#include "eapol_supp/eapol_supp_sm.h"
#include "config.h"
#include "wpa_supplicant_i.h"
#include "ctrl_iface.h"
}
/* rep includes */
#include <wifi/ctrl.h>
static Wifi::Msg_buffer *_msg_buffer;
void Wifi::ctrl_init(Msg_buffer &buffer)
{
_msg_buffer = &buffer;
}
struct ctrl_iface_priv {
struct wpa_supplicant *wpa_s;
int fd;
int level;
Wifi::Msg_buffer *buffer;
unsigned last_send_id;
};
struct ctrl_iface_global_priv {
struct wpa_global *global;
};
extern "C"
void nl_set_wpa_ctrl_fd(void);
void wpa_ctrl_set_fd()
{
nl_set_wpa_ctrl_fd();
}
static void send_reply(Wifi::Msg_buffer &buffer, char const *txt, size_t len)
{
buffer.block_for_processing();
/* XXX hack to trick poll() into returning faster */
wpa_ctrl_set_fd();
if (len >= sizeof(buffer.recv))
len = sizeof(buffer.recv) - 1;
memcpy(buffer.recv, txt, len);
buffer.recv[len] = 0;
buffer.recv_id++;
buffer.notify_response();
}
/*
* This function is called by wpa_supplicant whenever it receives a
* command via the CTRL interface, i.e. the manager has sent a new
* message.
*/
static void wpa_supplicant_ctrl_iface_receive(int fd, void *eloop_ctx,
void *sock_ctx)
{
struct wpa_supplicant *wpa_s = static_cast<wpa_supplicant*>(eloop_ctx);
struct ctrl_iface_priv *priv = static_cast<ctrl_iface_priv*>(sock_ctx);
Wifi::Msg_buffer &buffer = *priv->buffer;
char *reply = NULL;
size_t reply_len = 0;
if (buffer.send[0] == 0 || buffer.send_id == priv->last_send_id)
return;
priv->last_send_id = buffer.send_id;
reply = wpa_supplicant_ctrl_iface_process(wpa_s,
buffer.send,
&reply_len);
if (reply) {
send_reply(buffer, reply, reply_len);
os_free(reply);
} else
if (reply_len == 1) {
send_reply(buffer, "FAIL", 4);
} else
if (reply_len == 2) {
send_reply(buffer, "OK", 2);
}
}
static void send_event(Wifi::Msg_buffer &buffer, char const *txt, size_t len)
{
buffer.block_for_processing();
/* XXX hack to trick poll() into returning faster */
wpa_ctrl_set_fd();
if (len >= sizeof(buffer.event))
len = sizeof(buffer.event) - 1;
memcpy(buffer.event, txt, len);
buffer.event[len] = 0;
buffer.event_id++;
buffer.notify_event();
}
/*
* This function is called by wpa_supplicant whenever it wants to
* forward some message. We filter these messages and forward only
* those, which are of interest to the Wifi manager.
*/
static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
enum wpa_msg_type type,
const char *txt, size_t len)
{
/* there is not global support */
if (type == WPA_MSG_ONLY_GLOBAL)
return;
struct wpa_supplicant *wpa_s = static_cast<wpa_supplicant*>(ctx);
if (wpa_s == NULL)
return;
struct ctrl_iface_priv *priv = wpa_s->ctrl_iface;
if (!priv || level < priv->level)
return;
/*
* Filter messages and only forward events the manager cares
* about or rather knows how to handle.
*/
bool const forward =
strncmp(txt, "CTRL-EVENT-SCAN-RESULTS", 23) == 0
|| strncmp(txt, "CTRL-EVENT-CONNECTED", 20) == 0
|| strncmp(txt, "CTRL-EVENT-DISCONNECTED", 23) == 0
|| strncmp(txt, "CTRL-EVENT-NETWORK-NOT-FOUND", 28) == 0
/* needed to detect connecting state */
|| strncmp(txt, "SME: Trying to authenticate", 27) == 0;
if (!forward)
return;
Wifi::Msg_buffer &buffer = *priv->buffer;
send_event(buffer, txt, len);
}
extern "C" struct ctrl_iface_priv *
wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
{
struct ctrl_iface_priv *priv = (ctrl_iface_priv*)os_zalloc(sizeof(*priv));
if (priv == NULL) { return NULL; }
if (wpa_s->conf->ctrl_interface == NULL) {
return priv;
}
priv->buffer = _msg_buffer;
priv->level = MSG_INFO;
priv->fd = Wifi::CTRL_FD;
eloop_register_read_sock(priv->fd,
wpa_supplicant_ctrl_iface_receive,
wpa_s, priv);
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
return priv;
}
extern "C"
void wpa_supplicant_ctrl_iface_deinit(struct wpa_supplicant *wpa_s,
struct ctrl_iface_priv *priv)
{
(void)wpa_s;
os_free(priv);
}
extern "C"
void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) { }
extern "C" struct ctrl_iface_global_priv *
wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
{
struct ctrl_iface_global_priv *priv =
(ctrl_iface_global_priv*)os_zalloc(sizeof(*priv));
return priv;
}
extern "C"
void wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *p)
{
os_free(p);
}

View File

@ -1,5 +1,5 @@
/* /*
* \brief WPA Supplicant frontend * \brief Custom WPA Supplicant main routine
* \author Josef Soentgen * \author Josef Soentgen
* \date 2014-12-08 * \date 2014-12-08
*/ */
@ -45,8 +45,7 @@ int wpa_main(void)
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
// TODO use CTRL interface for setting debug level params.wpa_debug_level = MSG_ERROR;
params.wpa_debug_level = 1 ? MSG_DEBUG : MSG_INFO;
params.ctrl_interface = "GENODE"; params.ctrl_interface = "GENODE";
global = wpa_supplicant_init(&params); global = wpa_supplicant_init(&params);

View File

@ -7,7 +7,8 @@
/* needed by wifi driver */ /* needed by wifi driver */
wpa_main; wpa_main;
wpa_reporter_init; wpa_reporter_init;
wpa_ctrl_set_fd; *wpa_ctrl_set_fd*;
*ctrl_init*;
/* needed by wpa_driver_nl80211 */ /* needed by wpa_driver_nl80211 */
__hide_aliasing_typecast; __hide_aliasing_typecast;

View File

@ -1,3 +1,3 @@
<wifi connected_scan_interval="0" scan_interval="5" rfkill="no" verbose="no"> <wifi scan_interval="5" rfkill="no" verbose="no">
<network ssid="" protection="NONE" passphrase=""/> <network ssid="" protection="NONE" passphrase=""/>
</wifi> </wifi>

View File

@ -141,7 +141,6 @@ struct Sculpt::Network : Noncopyable
_wlan_config.generate([&] (Xml_generator &xml) { _wlan_config.generate([&] (Xml_generator &xml) {
xml.attribute("connected_scan_interval", 0U);
xml.attribute("scan_interval", 10U); xml.attribute("scan_interval", 10U);
xml.attribute("update_quality_interval", 30U); xml.attribute("update_quality_interval", 30U);
@ -172,7 +171,6 @@ struct Sculpt::Network : Noncopyable
_wlan_config.generate([&] (Xml_generator &xml) { _wlan_config.generate([&] (Xml_generator &xml) {
xml.attribute("connected_scan_interval", 0U);
xml.attribute("scan_interval", 10U); xml.attribute("scan_interval", 10U);
xml.attribute("update_quality_interval", 30U); xml.attribute("update_quality_interval", 30U);

View File

@ -30,29 +30,27 @@ proc wifi_wpa { } {
# You may script your tests with this function in the dynamic_rom config below. # You may script your tests with this function in the dynamic_rom config below.
# The syntax for the networks parameter is # The syntax for the networks parameter is
# #
# { ssid protection passphrase explicit_scan } # { ssid protection passphrase auto_connect explicit_scan }
# #
# Example dynamic_rom config: # Example dynamic_rom config:
# #
# {<inline description="auto-connect both networks"> # {<inline description="auto-connect both networks">
# } [wifi_config 30 5 no [list "net1 WPA2 net1_psk no" "net2 WPA2 net2_psk no"]] { # } [wifi_config 5 no [list "net1 WPA2 net1_psk yes no" "net2 WPA2 net2_psk yes no"]] {
# </inline> # </inline>
# <inline description="aquto-connect both, but net2 explicitly"> # <inline description="aquto-connect both, but net2 explicitly">
# } [wifi_config 30 5 no [list "net1 WPA2 net1_psk no" "net2 WPA2 net2_psk yes"]] { # } [wifi_config 5 no [list "net1 WPA2 net1_psk yes no" "net2 WPA2 net2_psk yes yes"]] {
# </inline>} # </inline>}
set wifi_verbose false set wifi_verbose false
set wifi_verbose_state false
proc wifi_config { connected_scan_interval scan_interval rfkill networks } { proc wifi_config { scan_interval update_quality_interval rfkill networks } {
global wifi_verbose global wifi_verbose
global wifi_verbose_state global wifi_verbose_state
set config "<wifi_config" set config "<wifi_config"
append config " verbose=\"$wifi_verbose\"" append config " verbose=\"$wifi_verbose\""
append config " verbose_state=\"$wifi_verbose_state\""
append config " connected_scan_interval=\"$connected_scan_interval\""
append config " scan_interval=\"$scan_interval\"" append config " scan_interval=\"$scan_interval\""
append config " update_quality_interval=\"$update_quality_interval\""
append config " rfkill=\"$rfkill\"" append config " rfkill=\"$rfkill\""
append config ">\n" append config ">\n"
foreach n $networks { foreach n $networks {
@ -60,7 +58,8 @@ proc wifi_config { connected_scan_interval scan_interval rfkill networks } {
append config " ssid=\"[lindex $n 0]\"" append config " ssid=\"[lindex $n 0]\""
append config " protection=\"[lindex $n 1]\"" append config " protection=\"[lindex $n 1]\""
append config " passphrase=\"[lindex $n 2]\"" append config " passphrase=\"[lindex $n 2]\""
append config " explicit_scan=\"[lindex $n 3]\"" append config " auto_connect=\"[lindex $n 3]\""
append config " explicit_scan=\"[lindex $n 4]\""
append config "/>\n" append config "/>\n"
} }
append config "</wifi_config>\n" append config "</wifi_config>\n"
@ -236,19 +235,19 @@ append config {
<config verbose="yes"> <config verbose="yes">
<rom name="wifi_config"> <rom name="wifi_config">
<inline description="disconnect"> <inline description="disconnect">
} [wifi_config 30 5 no {}] { } [wifi_config 5 10 no {}] {
</inline> </inline>
<sleep milliseconds="15000"/> <sleep milliseconds="15000"/>
<inline description="connect"> <inline description="connect">
} [wifi_config 30 5 no [list "[wifi_ssid] [wifi_wpa] [wifi_psk] yes"]] { } [wifi_config 5 10 no [list "[wifi_ssid] [wifi_wpa] [wifi_psk] yes no "]] {
</inline> </inline>
<sleep milliseconds="60000"/> <sleep milliseconds="60000"/>
<inline description="rfkill block"> <inline description="rfkill block">
} [wifi_config 30 5 yes [list "[wifi_ssid] [wifi_wpa] [wifi_psk] yes"]] { } [wifi_config 5 10 yes [list "[wifi_ssid] [wifi_wpa] [wifi_psk] yes no "]] {
</inline> </inline>
<sleep milliseconds="30000"/> <sleep milliseconds="30000"/>
<inline description="rfkill unblock"> <inline description="rfkill unblock">
} [wifi_config 30 5 no [list "[wifi_ssid] [wifi_wpa] [wifi_psk] yes"]] { } [wifi_config 5 10 no [list "[wifi_ssid] [wifi_wpa] [wifi_psk] yes no "]] {
</inline> </inline>
<sleep milliseconds="30000"/> <sleep milliseconds="30000"/>
</rom> </rom>
@ -327,7 +326,7 @@ install_config $config
# boot-module assembly # boot-module assembly
# #
if {$debug_driver} { if {$debug_driver} {
exec rm bin/wifi.lib.so catch {exec rm bin/wifi.lib.so}
exec echo dummy > bin/wifi.lib.so exec echo dummy > bin/wifi.lib.so
} }

View File

@ -244,8 +244,8 @@ if { $use_wifi_driver } {
<ram/> <ram/>
<import> <import>
<inline name="wifi_config"> <inline name="wifi_config">
<wifi_config connected_scan_interval="0" scan_interval="10" rfkill="no" verbose="no" verbose_state="no">} <wifi_config>}
append config "<network ssid=\"$wifi_ssid\" protection=\"WPA2\" passphrase=\"$wifi_psk\"/>" append config "<network ssid=\"$wifi_ssid\" protection=\"WPA2\" passphrase=\"$wifi_psk\" auto_connect=\"true\"/>"
append config { </wifi_config> append config { </wifi_config>
</inline> </inline>
</import> </import>