driver/wifi: split hidden network scanning

This commit splits the intent to scan for a hidden network and the
actual configuration of a network itself by introducing
'<explicit_scan>' nodes. Like the '<network>' node these also feature
a 'ssid' attribute and each node is incorporated into the SCAN request
if its SSID is valid.

For more information please consult 'repos/src/driver/wifi/README'.

Issue #5356.
This commit is contained in:
Josef Söntgen 2024-10-07 17:16:18 +02:00
parent 40f2b58098
commit e4d2a4df42
No known key found for this signature in database
GPG Key ID: CFCD8B5D2D93D80E
3 changed files with 112 additions and 35 deletions

View File

@ -55,12 +55,14 @@ proc wifi_config { scan_interval update_quality_interval rfkill networks } {
append config " rfkill=\"$rfkill\""
append config ">\n"
foreach n $networks {
if {[lindex $n 4] == "yes"} {
append config " <explicit_scan ssid=\"[lindex $n 0]\"/>\n"
}
append config " <network"
append config " ssid=\"[lindex $n 0]\""
append config " protection=\"[lindex $n 1]\""
append config " passphrase=\"[lindex $n 2]\""
append config " auto_connect=\"[lindex $n 3]\""
append config " explicit_scan=\"[lindex $n 4]\""
append config "/>\n"
}
append config "</wifi_config>\n"

View File

@ -148,25 +148,28 @@ a network and has the following attributes.
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.
To scan for a hidden network a '<explicit_scan>' node must by added.
It contains the following mandatory attribute.
* :ssid: set the name of the hidden network to scan for
Note: the SSID is copied verbatim and at the moment, there is no way
to express or escape non alphanumeric characters.
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'.
while the second one is hidden but should show up in the scan results.
!<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"/>
! <explicit_scan ssid="Skynet"/>
!</wifi_config>
To join the hidden network a corresponding '<network>' is needed.
The wifi driver uses two distinct reports, 'state' and 'accesspoints',
to communicate its state of connectivity and information about the wireless

View File

@ -165,7 +165,6 @@ struct Accesspoint : Interface
* Internal configuration fields
*/
bool auto_connect { false };
bool explicit_scan { false };
static Accesspoint from_xml(Xml_node const &node)
{
@ -177,7 +176,6 @@ struct Accesspoint : Interface
ap.pass = node.attribute_value("passphrase", Accesspoint::Pass(""));
ap.prot = node.attribute_value("protection", Accesspoint::Prot("NONE"));
ap.auto_connect = node.attribute_value("auto_connect", true);
ap.explicit_scan = node.attribute_value("explicit_scan", false);
return ap;
}
@ -219,8 +217,7 @@ struct Accesspoint : Interface
"protection: ", prot, " "
"id: ", id, " "
"quality: ", quality, " "
"auto_connect: ", auto_connect, " "
"explicit_scan: ", explicit_scan);
"auto_connect: ", auto_connect);
}
bool wpa() const { return prot != "NONE"; }
@ -232,7 +229,6 @@ struct Accesspoint : Interface
bool const update = ((Accesspoint::valid(other.bssid) && other.bssid != bssid)
|| pass != other.pass
|| prot != other.prot
|| explicit_scan != other.explicit_scan
|| auto_connect != other.auto_connect);
if (!update)
return false;
@ -243,7 +239,6 @@ struct Accesspoint : Interface
pass = other.pass;
prot = other.prot;
auto_connect = other.auto_connect;
explicit_scan = other.explicit_scan;
return true;
}
};
@ -276,6 +271,37 @@ struct Network : List_model<Network>::Element
};
struct Explicit_scan : List_model<Explicit_scan>::Element
{
Accesspoint::Ssid _ssid { };
Explicit_scan(Accesspoint::Ssid const &ssid) : _ssid { ssid } { }
virtual ~Explicit_scan() { }
void with_ssid(auto const &fn) {
if (Accesspoint::valid(_ssid))
fn(_ssid);
}
void with_ssid(auto const &fn) const {
if (Accesspoint::valid(_ssid))
fn(_ssid);
}
/**************************
** List_model interface **
**************************/
static bool type_matches(Xml_node const &node) {
return node.has_type("explicit_scan"); }
bool matches(Xml_node const &node) {
return _ssid == node.attribute_value("ssid", Accesspoint::Ssid()); }
};
static void for_each_line(char const *msg, auto const &fn)
{
char line_buffer[1024];
@ -393,7 +419,7 @@ struct Add_network_cmd : Action
enum class State : unsigned {
INIT, ADD_NETWORK, FILL_NETWORK_SSID, FILL_NETWORK_BSSID,
FILL_NETWORK_KEY_MGMT, SET_NETWORK_PMF, FILL_NETWORK_PSK,
SET_SCAN_SSID, ENABLE_NETWORK, COMPLETE
ENABLE_NETWORK, COMPLETE
};
Ctrl_msg_buffer &_msg;
@ -462,12 +488,6 @@ struct Add_network_cmd : Action
_state = State::FILL_NETWORK_PSK;
break;
case State::FILL_NETWORK_PSK:
ctrl_cmd(_msg, Cmd("SET_NETWORK ", _accesspoint.id,
" scan_ssid ",
_accesspoint.explicit_scan ? "1" : "0"));
_state = State::SET_SCAN_SSID;
break;
case State::SET_SCAN_SSID:
if (_accesspoint.auto_connect) {
ctrl_cmd(_msg, Cmd("ENABLE_NETWORK ", _accesspoint.id));
_state = State::ENABLE_NETWORK;
@ -508,7 +528,6 @@ struct Add_network_cmd : Action
case State::FILL_NETWORK_KEY_MGMT: [[fallthrough]];
case State::SET_NETWORK_PMF: [[fallthrough]];
case State::FILL_NETWORK_PSK: [[fallthrough]];
case State::SET_SCAN_SSID: [[fallthrough]];
case State::ENABLE_NETWORK:
if (!cmd_successful(msg)) {
error("ADD_NETWORK(", (unsigned)_state, ") failed: ", msg);
@ -538,7 +557,6 @@ struct Add_network_cmd : Action
case State::FILL_NETWORK_KEY_MGMT: break;
case State::SET_NETWORK_PMF: break;
case State::FILL_NETWORK_PSK: break;
case State::SET_SCAN_SSID: break;
case State::ENABLE_NETWORK: break;
case State::COMPLETE: break;
}
@ -633,8 +651,7 @@ struct Update_network_cmd : Action
{
enum class State : unsigned {
INIT, UPDATE_NETWORK_PSK,
DISABLE_NETWORK, SET_SCAN_SSID,
ENABLE_NETWORK, COMPLETE
DISABLE_NETWORK, ENABLE_NETWORK, COMPLETE
};
Ctrl_msg_buffer &_msg;
Accesspoint _accesspoint;
@ -665,12 +682,6 @@ struct Update_network_cmd : Action
break;
case State::UPDATE_NETWORK_PSK:
ctrl_cmd(_msg, Cmd("DISABLE_NETWORK ", _accesspoint.id));
_state = State::SET_SCAN_SSID;
break;
case State::SET_SCAN_SSID:
ctrl_cmd(_msg, Cmd("SET_NETWORK ", _accesspoint.id,
" scan_ssid ",
_accesspoint.explicit_scan ? "1" : "0"));
_state = State::DISABLE_NETWORK;
break;
case State::DISABLE_NETWORK:
@ -698,7 +709,6 @@ struct Update_network_cmd : Action
case State::INIT: break;
case State::UPDATE_NETWORK_PSK: [[fallthrough]];
case State::ENABLE_NETWORK: [[fallthrough]];
case State::SET_SCAN_SSID: [[fallthrough]];
case State::DISABLE_NETWORK:
if (!cmd_successful(msg)) {
error("UPDATE_NETWORK(", (unsigned)_state, ") failed: ", msg);
@ -729,6 +739,10 @@ struct Scan_cmd : Action
Ctrl_msg_buffer &_msg;
State _state;
/* enough to store 64 hidden networks */
char ssid_buffer[4060] { };
size_t buffer_pos { 0 };
Scan_cmd(Ctrl_msg_buffer &msg)
:
Action { Command::SCAN },
@ -741,11 +755,32 @@ struct Scan_cmd : Action
Genode::print(out, "Scan_cmd[", (unsigned)_state, "]");
}
void append_ssid(Accesspoint::Ssid const &ssid)
{
enum { SSID_ARG_LEN = 6 + 64, /* " ssid " + "a5a5a5a5..." */ };
/* silently ignore SSID */
if (buffer_pos + SSID_ARG_LEN >= sizeof(ssid_buffer))
return;
char ssid_hex[64+1] { };
char const *ssid_ptr = ssid.string();
for (size_t i = 0; i < ssid.length() - 1; i++) {
Util::byte2hex((ssid_hex + i * 2), ssid_ptr[i]);
}
Genode::String<SSID_ARG_LEN + 1> tmp(" ssid ", (char const*)ssid_hex);
size_t const tmp_len = tmp.length() - 1;
Genode::memcpy((ssid_buffer + buffer_pos), tmp.string(), tmp_len);
buffer_pos += tmp_len;
}
void execute() override
{
switch (_state) {
case State::INIT:
ctrl_cmd(_msg, Cmd("SCAN"));
ctrl_cmd(_msg, Cmd("SCAN ", (char const*)ssid_buffer));
_state = State::SCAN;
break;
case State::SCAN:
@ -1252,6 +1287,11 @@ struct Wifi::Manager : Wifi::Rfkill_notification_handler
Heap _network_allocator;
List_model<Network> _network_list { };
/* Explicit_scan handling */
Heap _explicit_scan_allocator;
List_model<Explicit_scan> _explicit_scan_list { };
/*
* Action queue handling
*/
@ -1535,6 +1575,31 @@ struct Wifi::Manager : Wifi::Rfkill_notification_handler
});
});
_explicit_scan_list.update_from_xml(config_node,
[&] (Genode::Xml_node const &node) -> Explicit_scan & {
Accesspoint::Ssid const ssid =
node.attribute_value("ssid", Accesspoint::Ssid());
/*
* Always created the Explicit_scan object but ignore
* invalid ones during SCAN operation to satisfy the
* List_model requirements.
*/
return *new (_explicit_scan_allocator) Explicit_scan(ssid);
},
[&] (Explicit_scan &explicit_scan) {
Genode::destroy(_explicit_scan_allocator,
&explicit_scan);
},
[&] (Explicit_scan &explicit_scan, Genode::Xml_node const &node) {
/*
* Intentionally left empty as we never have to update the
* object as it only contains the SSID that also serves as
* identifier.
*/
});
_dispatch_action_if_needed();
}
@ -1623,7 +1688,13 @@ struct Wifi::Manager : Wifi::Rfkill_notification_handler
return;
}
_queue_action(*new (_actions_alloc) Scan_cmd(_msg), _config.verbose);
Scan_cmd &scan_cmd = *new (_actions_alloc) Scan_cmd(_msg);
_explicit_scan_list.for_each([&] (Explicit_scan const &explicit_scan) {
explicit_scan.with_ssid([&] (Accesspoint::Ssid const &ssid) {
scan_cmd.append_ssid(ssid);
});
});
_queue_action(scan_cmd, _config.verbose);
_dispatch_action_if_needed();
}
@ -2071,6 +2142,7 @@ struct Wifi::Manager : Wifi::Rfkill_notification_handler
Manager(Env &env)
:
_network_allocator(env.ram(), env.rm()),
_explicit_scan_allocator(env.ram(), env.rm()),
_actions_alloc(env.ram(), env.rm()),
_cmd_handler(env.ep(), *this, &Wifi::Manager::_handle_cmds),
_events_handler(env.ep(), *this, &Wifi::Manager::_handle_events),