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_
#define _WIFI__CTRL_H_
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <util/string.h>
#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
{
unsigned char recv[4096*8];
unsigned char send[4096];
unsigned recv_id;
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
{
char send[4096];
unsigned send_id;
unsigned char event[1024];
char recv[4096*8];
unsigned recv_id;
unsigned last_recv_id;
char event[1024];
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 *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_ */

View File

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

View File

@ -10,7 +10,8 @@ CC_OPT += -Wno-unused-function
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

View File

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

View File

@ -93,7 +93,7 @@ The nl80211 driver patch contains the following changes:
goto err;
- 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) {
wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
strerror(errno));

View File

@ -1 +1 @@
aa7af61abdac82e5a783d8371be9cb8d1a41fceb
15974e67af9112d919f1625a5c2ed2d996cdbdfb

View File

@ -133,8 +133,8 @@ install_config {
<config>
<rom name="wifi_config">
<inline description="CONNECT">
<wifi_config connected_scan_interval="0" scan_interval="5" rfkill="no" verbose="no" verbose_state="no">
<network ssid="} [wifi_ssid] {" protection="WPA2" passphrase="} [wifi_psk] {"/>
<wifi_config>
<network ssid="} [wifi_ssid] {" protection="WPA2" passphrase="} [wifi_psk] {" auto_connect="true"/>
</wifi_config>
</inline>
<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
libnl and wpa_supplicant to Genode. Depending on the used platform it
The wifi component consists of a port of the Linux mac802.11 stack as well
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
PC platform it contains the ath9k, iwlwifi and rtlwifi drivers for PCI(e)
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
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">
! <resource name="RAM" quantum="32M"/>
! <provides><service name="Nic"/></provides>
! <config>
! <libc stdout="/dev/null" stderr="/dev/null" rtc="/dev/rtc"/>
! <libc stdout="/dev/log" stderr="/dev/null" rtc="/dev/rtc"/>
! <vfs>
! <dir name="dev"> <log/> <null/> <rtc/>
! <jitterentropy name="random"/>
@ -38,14 +42,14 @@ can be used:
! </route>
!</start>
On other platforms the wifi library will be different. The following
snippet illustrates the use of the driver on the PinePhone:
On other platforms the wifi library will be different. So, the
following snippet illustrates the use of the driver on the PinePhone.
!<start name="wifi" caps="250">
! <resource name="RAM" quantum="32M"/>
! <provides><service name="Nic"/></provides>
! <config>
! <libc stdout="/dev/null" stderr="/dev/null" rtc="/dev/rtc"/>
! <libc stdout="/dev/log" stderr="/dev/null" rtc="/dev/rtc"/>
! <vfs>
! <dir name="dev"> <log/> <null/> <rtc/>
! <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
contained in an '.tar' archive that is request as a ROM module.
The driver will request access to the ROM module 'wifi_config' to
connect to a network:
The driver will request access to the 'wifi_config' ROM module that
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">
! <network ssid="Foobar" protection="WPA2" passphrase="allyourbase"/>
!</wifi_config>
* :scan_interval: sets the time interval in seconds in which scan
operations are requested and is used when not already connected
to a network. The default is 5 seconds.
To temporarily prevent any radio activity, the 'rfkill' attribute
can be set to 'true'.
* :update_quality_interval: sets the time interval in which the current
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,
either 'WPA', 'WPA2' or 'WPA3' as well as the the passphrase have to be
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.
* :rfkill: allows to temporarily prevent any radio activity. The
default is 'false'.
The configuration may contain more than one network. In This case the driver
will try to select the best one it gets a response from. To prevent it
from automatically joining the network the 'auto_connect' attribute must be
set to 'false'; the default value is 'true'. If the 'explicit_scan' attribute
is set, the driver will pro-actively scan for a hidden network with the given
SSID:
* :bgscan: is an expert option that configures the way the
supplicant performs background scanning to steer or rather optimize
roaming decisions within the same network (SSID). The syntax of the
option string corresponds to the original WPA-supplicant 'bgscan' option.
The default value is set to 'simple:30:-70:600'. This functionality can
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">
! <network ssid="Zero" protection="WPA2" passphrase="allyourbase"/>
* :log_level: allows for steering the verbosity of the supplicant
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"
! explicit_scan="true" auto_connect="false"/>
! explicit_scan="true"/>
!</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
every so often update the current signal quality of the established
connection to the accesspoint. Note that this option is only useable when
the 'connected_scan_interval' is set to '0' as both options are mutually
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:
The wifi driver uses two distinct reports, 'state' and 'accesspoints',
to communicate its state of connectivity and information about the wireless
access points in the vicinity to other components.
This exemplary 'accesspoints' report shows its general structure.
!<accesspoints>
! <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"/>
!</accesspoints>
Each accesspoint node has attributes that contain the SSID and the BSSID
of the accesspoint as well as the link quality (signal strength). These
attributes are mandatory. If the network is protected, the node will also
have an attribute describing the type of protection in addition.
The '<accesspoints>' node can contain a fluctuating number of '<accesspoint>'
nodes that describe an access point with the following attributes.
The second report provides information about the state of the connection
to the currently connected accesspoint:
* :ssid: specifies the name of the network the access point advertises.
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>
! <accesspoint ssid="foobar" bssid="01:02:03:04:05:06" quality="70" freq="2418" state="connected"/>
!</state>
Valid state values are 'connected', 'disconnected', 'connecting'. Depending
on the state, there are additional attributes that can be checked. In case
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').
The '<state>' node encompasses one '<accesspoint>' node that has the
following additional attributes beside the ones already discussed.
By subscribing to both reports and providing the required 'wifi_config' ROM
module, a component is able control the wireless driver.
* :state: specifies the actual state of connectivity. Valid values
are 'connected', 'connecting' and 'disconnected'.
Currently only WPA/WPA2/WPA3 protection using a passphrase is supported and
the SSID is copied verbatim. At the moment, there is no way to express or
escape non alphanumeric characters.
* :auth_failure: is an optional attribute and set to 'true' in case
the PSK was wrong
* :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
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
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
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
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
@ -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
used:
used.
!LIBS := base pc_wifi

File diff suppressed because it is too large Load Diff

View File

@ -28,68 +28,12 @@
/* local includes */
#include "util.h"
#include "wpa.h"
#include "frontend.h"
#include "manager.h"
#include "access_firmware.h"
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 */
extern void wifi_init(Genode::Env&, Genode::Blockade&);
extern void wifi_set_rfkill_sigh(Genode::Signal_context_capability);
@ -102,7 +46,7 @@ struct Main
Env &env;
Constructible<Wpa_thread> _wpa;
Constructible<Wifi::Frontend> _frontend;
Constructible<Wifi::Manager> _manager;
struct Request_handler : Wifi::Firmware_request_handler
{
@ -173,28 +117,17 @@ struct Main
/* prepare Lx_kit::Env */
wifi_init(env, _wpa_startup_blockade);
_frontend.construct(env, _wifi_msg_buffer);
_wifi_frontend = &*_frontend;
_manager.construct(env);
Wifi::rfkill_establish_handler(*_wifi_frontend);
Wifi::rfkill_establish_handler(*_manager);
Wifi::firmware_establish_handler(_request_handler);
Wifi::ctrl_init(_manager->msg_buffer());
_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)
{
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
* \date 2018-07-23
*/
@ -59,9 +59,9 @@ namespace Util {
}
}
/**********************************
** Front end specific utilities **
**********************************/
/********************************
** Manager-specific utilities **
********************************/
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);
if (rfkilled != _rfkill_state.blocked)
if (rfkilled != _rfkill_state.blocked) {
rfkill_switch_all(RFKILL_TYPE_WLAN, !!_rfkill_state.blocked);
_rfkill_state.rfkilled = rfkilled;
_rfkill_state.rfkilled = !!_rfkill_state.blocked;
}
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_kit::env().scheduler.execute();
}
void Wifi::rfkill_notify()
{
if (_wlan_ptr->rfkill_helper.constructed())
_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 {
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
* \date 2014-12-08
*/
@ -45,8 +45,7 @@ int wpa_main(void)
memset(&params, 0, sizeof(params));
// TODO use CTRL interface for setting debug level
params.wpa_debug_level = 1 ? MSG_DEBUG : MSG_INFO;
params.wpa_debug_level = MSG_ERROR;
params.ctrl_interface = "GENODE";
global = wpa_supplicant_init(&params);

View File

@ -7,7 +7,8 @@
/* needed by wifi driver */
wpa_main;
wpa_reporter_init;
wpa_ctrl_set_fd;
*wpa_ctrl_set_fd*;
*ctrl_init*;
/* needed by wpa_driver_nl80211 */
__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=""/>
</wifi>

View File

@ -141,7 +141,6 @@ struct Sculpt::Network : Noncopyable
_wlan_config.generate([&] (Xml_generator &xml) {
xml.attribute("connected_scan_interval", 0U);
xml.attribute("scan_interval", 10U);
xml.attribute("update_quality_interval", 30U);
@ -172,7 +171,6 @@ struct Sculpt::Network : Noncopyable
_wlan_config.generate([&] (Xml_generator &xml) {
xml.attribute("connected_scan_interval", 0U);
xml.attribute("scan_interval", 10U);
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.
# The syntax for the networks parameter is
#
# { ssid protection passphrase explicit_scan }
# { ssid protection passphrase auto_connect explicit_scan }
#
# Example dynamic_rom config:
#
# {<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 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>}
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_state
set config "<wifi_config"
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 " update_quality_interval=\"$update_quality_interval\""
append config " rfkill=\"$rfkill\""
append config ">\n"
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 " protection=\"[lindex $n 1]\""
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 "</wifi_config>\n"
@ -236,19 +235,19 @@ append config {
<config verbose="yes">
<rom name="wifi_config">
<inline description="disconnect">
} [wifi_config 30 5 no {}] {
} [wifi_config 5 10 no {}] {
</inline>
<sleep milliseconds="15000"/>
<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>
<sleep milliseconds="60000"/>
<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>
<sleep milliseconds="30000"/>
<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>
<sleep milliseconds="30000"/>
</rom>
@ -327,7 +326,7 @@ install_config $config
# boot-module assembly
#
if {$debug_driver} {
exec rm bin/wifi.lib.so
catch {exec rm bin/wifi.lib.so}
exec echo dummy > bin/wifi.lib.so
}

View File

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