hostapd: add enhanced MPSK support

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin
2024-07-03 08:46:15 +02:00
parent 6afc11838e
commit 33f8f22375
6 changed files with 613 additions and 1 deletions

View File

@@ -604,7 +604,7 @@ define Package/afcd/install
endef
define Install/hostapd
$(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/hostap
$(INSTALL_DIR) $(1)/usr/sbin $(1)/usr/share/hostap $(1)/etc/init.d
$(INSTALL_DATA) ./files/hostapd.uc $(1)/usr/share/hostap/
$(INSTALL_DIR) $(1)/etc/init.d $(1)/etc/config $(1)/etc/radius
ln -sf hostapd $(1)/usr/sbin/hostapd-radius
@@ -612,6 +612,8 @@ define Install/hostapd
$(INSTALL_DATA) ./files/radius.config $(1)/etc/config/radius
$(INSTALL_DATA) ./files/radius.clients $(1)/etc/radius/clients
$(INSTALL_DATA) ./files/radius.users $(1)/etc/radius/users
$(INSTALL_BIN) ./files/mpskd $(1)/usr/share/hostap/
$(INSTALL_BIN) ./files/mpskd.init $(1)/etc/init.d/mpskd
endef
define Install/supplicant

View File

@@ -0,0 +1,319 @@
#!/usr/bin/env ucode
'use strict';
import * as uloop from 'uloop';
import * as libubus from 'ubus';
uloop.init();
let ubus = libubus.connect();
let interfaces = {};
let ssids = {};
let cache = {};
let sub_6g = [];
let sub_6g_obj;
let reload_timer;
let gc_timer;
let timeout = 48 * 60 * 60;
function event_cb_6g(req) {
//printf('6g %s %.J\n', req.data, req.type);
if (req.type != 'auth' && req.type != 'probe')
return 0;
let addr = req.data.address;
let iface = interfaces[req.data.ifname];
if (!iface)
return 0;
let ssid = iface.ssid;
if (!ssid || !length(ssids[ssid].keys))
return 0;
let ssid_cache = cache[ssid];
if (ssid_cache && ssid_cache[addr])
return 0;
if (req.type == 'probe') {
printf(`Ignore probe ${req.type} on ${req.data.ifname} from ${addr}\n`);
return 1;
}
printf(`reject ${req.type} on ${req.data.ifname} from ${addr}\n`);
return 5;
}
function event_cb(req) {
//printf('normal %s %.J\n', req.data, req.type);
if (req.type != 'probe')
return 0;
let addr = req.data.address;
let iface = interfaces[req.data.ifname];
if (!iface)
return 0;
let ssid = iface.ssid;
if (!ssid || !length(ssids[ssid].keys))
return 0;
let ssid_cache = cache[ssid];
if (ssid_cache && ssid_cache[addr])
return 0;
printf(`reply to ${req.type} on ${req.data.ifname} from ${addr} without 6G RNR\n`);
return 2;
}
function create_6g_subscriber() {
for (let cur_sub in sub_6g)
cur_sub.remove();
sub_6g = [];
for (let ifname, iface in interfaces) {
let obj = 'hostapd.' + ifname;
let cur_sub;
if (iface.band == '6g')
cur_sub = ubus.subscriber((req) => event_cb_6g(req));
else
cur_sub = ubus.subscriber((req) => event_cb(req));
cur_sub.subscribe(obj);
push(sub_6g, cur_sub);
printf(`subscribe ${ifname}\n`);
ubus.call(obj, 'notify_response', { notify_response: 1 });
}
}
function cache_gc() {
let ts = time();
for (let ssid in keys(cache)) {
if (!ssids[ssid]) {
delete cache[ssid];
continue;
}
let ssid_cache = cache[ssid];
ssid = ssids[ssid];
for (let addr in keys(ssid_cache)) {
let sta = ssid_cache[addr];
let keep = ts < cache.timeout;
if (keep && !ssid.keys[sta.key])
keep = false;
if (keep)
sta.keydata = ssid.keys[sta.key];
if (!keep)
delete cache[addr];
}
}
}
function netifd_reload() {
let data = ubus.call('network.wireless', 'status');
ssids = {};
interfaces = {};
for (let radio_name, radio in data) {
if (!radio.up)
continue;
for (let iface in radio.interfaces) {
let config = iface.config;
if (config.mode != 'ap' || !iface.ifname)
continue;
let band = radio.config.band;
let nr_data = ubus.call('hostapd.' + iface.ifname, 'rrm_nr_get_own');
let nr;
if (nr_data && nr_data.value && nr_data.value[2])
nr = nr_data.value[2];
interfaces[iface.ifname] = {
band, nr,
ssid: config.ssid,
};
ssids[config.ssid] ??= {
interfaces: [],
keys: {},
bands: {},
};
let ssid = ssids[config.ssid];
push(ssid.interfaces, iface.ifname);
ssid.bands[band] = iface.ifname;
for (let sta in iface.stations) {
let stacfg = sta.config;
let key = stacfg.key;
if (!key)
continue;
let keydata = {};
let vid = stacfg.vid;
if (vid)
keydata.vlan = +vid;
ssid.keys[key] = keydata;
}
}
}
printf('New config: %.J\n', { ssids, interfaces });
cache_gc();
create_6g_subscriber();
}
function iface_ssid(ifname) {
let iface = interfaces[ifname];
if (!iface)
return;
return iface.ssid;
}
function sta_cache_entry_get(ssid, addr) {
let ssid_cache = cache[ssid] ?? {};
let entry = ssid_cache[addr];
if (entry)
entry.timeout = time() + timeout;
printf(`Get cache entry ssid=${ssid} addr=${addr}: ${entry}\n`);
return entry;
}
function sta_cache_entry_add(ssid, addr, key) {
cache[ssid] ??= {};
let ssid_cache = cache[ssid];
let ssid_data = ssids[ssid];
let keydata = ssid_data.keys[key];
let cache_data = {
timeout: time() + timeout,
ssid, key,
data: keydata ?? {},
};
ssid_cache[addr] = cache_data;
printf(`Added cache entry ssid=${ssid} addr=${addr}\n`);
return cache_data;
}
function ssid_psk(ssid) {
ssid = ssids[ssid];
if (!ssid)
return [];
return keys(ssid.keys);
}
function sta_auth_psk(ifname, addr) {
let ssid = iface_ssid(ifname);
if (!ssid)
return;
let cache = sta_cache_entry_get(ssid, addr);
if (cache)
return [ cache.key ];
return ssid_psk(ssid);
}
function sta_auth_cache(ifname, addr, idx) {
let ssid = iface_ssid(ifname);
if (!ssid)
return;
let cache = sta_cache_entry_get(ssid, addr);
if (cache)
return cache.data;
let psk = ssid_psk(ssid);
if (!psk)
return;
psk = psk[idx];
if (!psk)
return;
cache = sta_cache_entry_add(ssid, addr, psk);
if (!cache)
return;
let ssid_data = ssids[ssid];
if (!ssid_data)
return cache.data;
let target_ifname = ssid_data.bands['6g'];
if (!target_ifname)
return cache.data;
let target_iface = interfaces[target_ifname];
if (!target_iface)
return cache.data;
cache.timer = uloop.timer(30 * 1000, () => {
let msg = {
addr,
disassociation_imminent: false,
neighbors: [
target_iface.nr
],
abridged: false,
};
printf(`ubus call hostapd.${ifname} bss_transition_request '${msg}'\n`);
ubus.call('hostapd.' + ifname, 'bss_transition_request', msg);
delete cache.timer;
});
return cache.data;
}
function auth_cb(msg) {
let data = msg.data;
printf(`Event ${msg.type}: ${msg.data}\n`);
switch (msg.type) {
case 'sta_auth':
return {
psk: sta_auth_psk(data.iface, data.sta),
force_psk: true,
};
case 'sta_connected':
if (data.psk_idx == null)
return;
return sta_auth_cache(data.iface, data.sta, data.psk_idx);
case 'reload':
netifd_reload();
reload_timer.set(5000);
break;
}
}
let ubus_methods = {
state: {
call: function(req) {
return {
interfaces,
ssids,
cache
};
},
args: {
}
},
};
reload_timer = uloop.timer(-1, () => { netifd_reload(); });
gc_timer = uloop.timer(1000, () => { gc_timer.set(30 * 1000); cache_gc(); });
ubus.publish('mpsk', ubus_methods);
let sub = ubus.subscriber(auth_cb);
let listener = ubus.listener('ubus.object.add', (event, msg) => {
if (msg.path == 'hostapd-auth')
sub.subscribe(msg.path);
});
sub.subscribe('hostapd-auth');
netifd_reload();
uloop.run();

View File

@@ -0,0 +1,13 @@
#!/bin/sh /etc/rc.common
START=19
USE_PROCD=1
NAME=mpskd
start_service() {
procd_open_instance mpskd
procd_set_param command /usr/share/hostap/mpskd
procd_set_param respawn
procd_close_instance
}

View File

@@ -0,0 +1,156 @@
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -7740,7 +7740,7 @@ enum colocation_mode get_colocation_mode
}
-size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+size_t _hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, int add_6g)
{
size_t len = 0, current_len = 0;
enum colocation_mode mode = get_colocation_mode(hapd);
@@ -7753,7 +7753,7 @@ size_t hostapd_eid_rnr_len(struct hostap
/* fallthrough */
case WLAN_FC_STYPE_PROBE_RESP:
- if (mode == COLOCATED_LOWER_BAND)
+ if (add_6g && mode == COLOCATED_LOWER_BAND)
len += hostapd_eid_rnr_colocation_len(hapd,
&current_len);
@@ -7776,6 +7776,10 @@ size_t hostapd_eid_rnr_len(struct hostap
return len;
}
+size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type)
+{
+ return _hostapd_eid_rnr_len(hapd, type, 1);
+}
static u8 *hostapd_eid_rnr_iface(struct hostapd_data *hapd,
struct hostapd_data *reporting_hapd,
@@ -7938,7 +7942,7 @@ static u8 *hostapd_eid_neighbor_report_d
}
-u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+u8 * _hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type, int add_6g)
{
u8 *eid_start = eid;
size_t current_len = 0;
@@ -7955,7 +7959,7 @@ u8 * hostapd_eid_rnr(struct hostapd_data
/* fallthrough */
case WLAN_FC_STYPE_PROBE_RESP:
- if (mode == COLOCATED_LOWER_BAND)
+ if (add_6g && mode == COLOCATED_LOWER_BAND)
eid = hostapd_eid_rnr_colocation(hapd, eid,
&current_len);
@@ -7981,4 +7985,9 @@ u8 * hostapd_eid_rnr(struct hostapd_data
return eid;
}
+u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type)
+{
+ return _hostapd_eid_rnr(hapd, eid, type, 1);
+}
+
#endif /* CONFIG_NATIVE_WINDOWS */
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -135,6 +135,7 @@ u8 * hostapd_eid_time_zone(struct hostap
int hostapd_update_time_adv(struct hostapd_data *hapd);
void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
+u8 * _hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type, int add_6g);
u8 * hostapd_eid_rnr(struct hostapd_data *hapd, u8 *eid, u32 type);
u8 * hostapd_eid_multiple_bssid(struct hostapd_data *hapd,
struct hostapd_data *req_bss, u8 *eid, u8 *end,
@@ -146,6 +147,7 @@ size_t hostapd_eid_multiple_bssid_len(st
struct hostapd_data *req_bss, u32 type,
const u8 *known_bssids,
u8 known_bssids_len, size_t *rnr_len);
+size_t _hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type, int add_6g);
size_t hostapd_eid_rnr_len(struct hostapd_data *hapd, u32 type);
int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
#ifdef CONFIG_SAE
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -463,7 +463,8 @@ static u8 * hostapd_eid_supported_op_cla
static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
const struct ieee80211_mgmt *req,
int is_p2p, size_t *resp_len,
- const u8 *known_bssids, u8 known_bssids_len)
+ const u8 *known_bssids, u8 known_bssids_len,
+ int add_6g)
{
struct hostapd_data *req_bss = NULL;
struct ieee80211_mgmt *resp;
@@ -523,7 +524,7 @@ static u8 * hostapd_gen_probe_resp(struc
known_bssids,
known_bssids_len,
NULL);
- buflen += hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP);
+ buflen += _hostapd_eid_rnr_len(hapd, WLAN_FC_STYPE_PROBE_RESP, add_6g);
resp = os_zalloc(buflen);
if (resp == NULL)
@@ -706,7 +707,7 @@ static u8 * hostapd_gen_probe_resp(struc
pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos);
pos = hostapd_eid_dpp_cc(hapd, pos, (u8 *) resp + buflen - pos);
- pos = hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP);
+ pos = _hostapd_eid_rnr(hapd, pos, WLAN_FC_STYPE_PROBE_RESP, add_6g);
if (hapd->conf->vendor_elements) {
os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
@@ -930,6 +931,7 @@ void handle_probe_req(struct hostapd_dat
.ssi_signal = ssi_signal,
.elems = &elems,
};
+ int ubus_response;
if (hapd->iconf->rssi_ignore_probe_request && ssi_signal &&
ssi_signal < hapd->iconf->rssi_ignore_probe_request)
@@ -1118,7 +1120,12 @@ void handle_probe_req(struct hostapd_dat
}
#endif /* CONFIG_P2P */
- if (hostapd_ubus_handle_event(hapd, &req)) {
+ ubus_response = hostapd_ubus_handle_event(hapd, &req);
+
+ if (ubus_response == 2) {
+ wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " without 6G RNR.\n",
+ MAC2STR(mgmt->sa));
+ } else if (ubus_response) {
wpa_printf(MSG_DEBUG, "Probe request for " MACSTR " rejected by ubus handler.\n",
MAC2STR(mgmt->sa));
return;
@@ -1170,7 +1177,7 @@ void handle_probe_req(struct hostapd_dat
resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
&resp_len, elems.known_bssids,
- elems.known_bssids_len);
+ elems.known_bssids_len, ubus_response == 2 ? 0 : 1);
if (resp == NULL)
return;
@@ -1239,7 +1246,7 @@ static u8 * hostapd_probe_resp_offloads(
"this");
/* Generate a Probe Response template for the non-P2P case */
- return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0);
+ return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len, NULL, 0, 1);
}
#endif /* NEED_AP_MLME */
@@ -1269,7 +1276,7 @@ static u8 * hostapd_unsol_bcast_probe_re
return hostapd_gen_probe_resp(hapd, NULL, 0,
&params->unsol_bcast_probe_resp_tmpl_len,
- NULL, 0);
+ NULL, 0, 1);
}
#endif /* CONFIG_IEEE80211AX */

View File

@@ -1864,6 +1864,7 @@ int hostapd_ubus_handle_event(struct hostapd_data *hapd, struct hostapd_ubus_req
blob_buf_init(&b, 0);
blobmsg_add_macaddr(&b, "address", addr);
blobmsg_add_string(&b, "ifname", hapd->conf->iface);
if (req->mgmt_frame)
blobmsg_add_macaddr(&b, "target", req->mgmt_frame->da);
if (req->ssi_signal)

View File

@@ -0,0 +1,121 @@
{
"uuid": 2,
"radios": [
{
"band": "2G",
"country": "US",
"channel-mode": "HE",
"channel-width": 20,
"channel": "auto"
}, {
"band": "5G",
"country": "US",
"channel-mode": "HE",
"channel-width": 80,
"channel": 36
}, {
"band": "6G",
"country": "US",
"channel-mode": "HE",
"channel-width": 80,
"channel": 33
}
],
"interfaces": [
{
"name": "WAN",
"role": "upstream",
"services": [ "lldp" ],
"ethernet": [
{
"select-ports": [
"WAN*"
]
}
],
"ipv4": {
"addressing": "dynamic"
},
"ssids": [
{
"name": "OpenWifi-roam",
"wifi-bands": [
"2G", "5G"
],
"bss-mode": "ap",
"encryption": {
"proto": "psk2",
"key": "OpenWifi",
"ieee80211w": "optional"
},
"rrm": {
"reduced-neighbor-reporting": true
},
"multi-psk": [
{
"key": "aaaaaaaa"
}, {
"key": "bbbbbbbb"
}
],
"roaming": true
}, {
"name": "OpenWifi-roam",
"wifi-bands": [
"6G"
],
"bss-mode": "ap",
"encryption": {
"proto": "sae",
"key": "OpenWifi",
"ieee80211w": "required"
},
"rrm": {
"reduced-neighbor-reporting": true
},
"roaming": true
}
]
},
{
"name": "LAN",
"role": "downstream",
"services": [ "ssh", "lldp" ],
"ethernet": [
{
"select-ports": [
"LAN*"
]
}
],
"ipv4": {
"addressing": "static",
"subnet": "192.168.1.1/24",
"dhcp": {
"lease-first": 10,
"lease-count": 100,
"lease-time": "6h"
}
}
}
],
"metrics": {
"statistics": {
"interval": 120,
"types": [ "ssids", "lldp", "clients" ]
},
"health": {
"interval": 120
}
},
"services": {
"lldp": {
"describe": "uCentral",
"location": "universe"
},
"ssh": {
"port": 22
}
}
}