mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-03-14 23:09:45 +01:00
hostapd: add DPP ucode API for external frame handling
Some checks are pending
Build all core packages / Build all core packages for selected target (push) Waiting to run
Some checks are pending
Build all core packages / Build all core packages for selected target (push) Waiting to run
Add a ucode API to hostapd and wpa_supplicant for external DPP frame handling. This allows an external controller to intercept DPP frames and handle the DPP protocol externally. The API provides: - RX callbacks (dpp_rx_action, dpp_rx_gas) called when DPP frames are received, allowing external handling before internal processing - TX methods (dpp_send_action, dpp_send_gas_resp/dpp_send_gas_req) for transmitting DPP frames - A ubus channel-based API (dpp_channel) for bidirectional communication with exclusive hook registration per interface - CCE control for hostapd (set_cce method) The wpa_supplicant API mirrors hostapd but adapted for STA role: - Uses tx_gas_req instead of tx_gas_resp - GAS RX provides full frame instead of parsed query - No CCE control (AP-only feature) Both implementations include: - Timeout handling with automatic channel disconnect after 3 failures - Hook cleanup on interface removal - Last-caller-wins semantics for hook registration Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
parent
68c2ab8f5f
commit
09de759506
8 changed files with 866 additions and 2 deletions
|
|
@ -60,6 +60,7 @@ hostapd.data.bss_info_fields = {
|
|||
};
|
||||
|
||||
hostapd.data.mld = {};
|
||||
hostapd.data.dpp_hooks = {};
|
||||
|
||||
function iface_remove(cfg)
|
||||
{
|
||||
|
|
@ -1128,6 +1129,103 @@ function mld_set_config(config)
|
|||
mld_reload_interface(name);
|
||||
}
|
||||
|
||||
function dpp_find_bss(ifname)
|
||||
{
|
||||
for (let phy, bss_list in hostapd.bss) {
|
||||
if (bss_list[ifname])
|
||||
return bss_list[ifname];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function dpp_channel_handle_request(channel, req)
|
||||
{
|
||||
let data = req.args ?? {};
|
||||
let bss;
|
||||
|
||||
switch (req.type) {
|
||||
case "start":
|
||||
if (!data.ifname)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
let old_hook = hostapd.data.dpp_hooks[data.ifname];
|
||||
if (old_hook && old_hook.channel != channel)
|
||||
old_hook.channel.disconnect();
|
||||
hostapd.data.dpp_hooks[data.ifname] = {
|
||||
channel: channel,
|
||||
timeout_count: 0,
|
||||
};
|
||||
return 0;
|
||||
|
||||
case "stop":
|
||||
if (!data.ifname)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
let hook = hostapd.data.dpp_hooks[data.ifname];
|
||||
if (hook && hook.channel == channel)
|
||||
delete hostapd.data.dpp_hooks[data.ifname];
|
||||
return 0;
|
||||
|
||||
case "tx_action":
|
||||
bss = dpp_find_bss(data.ifname);
|
||||
if (!bss)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
if (!bss.dpp_send_action(data.dst, data.freq ?? 0, data.frame))
|
||||
return libubus.STATUS_UNKNOWN_ERROR;
|
||||
return 0;
|
||||
|
||||
case "tx_gas_resp":
|
||||
bss = dpp_find_bss(data.ifname);
|
||||
if (!bss)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
if (!bss.dpp_send_gas_resp(data.dst, data.dialog_token, data.data, data.freq ?? 0))
|
||||
return libubus.STATUS_UNKNOWN_ERROR;
|
||||
return 0;
|
||||
|
||||
case "set_cce":
|
||||
bss = dpp_find_bss(data.ifname);
|
||||
if (!bss)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
let val = data.enable ? "dd04506f9a1e" : "";
|
||||
bss.ctrl("SET vendor_elements " + val);
|
||||
bss.ctrl("UPDATE_BEACON");
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return libubus.STATUS_METHOD_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
function dpp_channel_handle_disconnect(channel)
|
||||
{
|
||||
for (let ifname, hook in hostapd.data.dpp_hooks) {
|
||||
if (hook.channel == channel)
|
||||
delete hostapd.data.dpp_hooks[ifname];
|
||||
}
|
||||
}
|
||||
|
||||
function dpp_rx_via_channel(ifname, method, data)
|
||||
{
|
||||
let hook = hostapd.data.dpp_hooks[ifname];
|
||||
if (!hook)
|
||||
return null;
|
||||
|
||||
let response = hook.channel.request({
|
||||
method: method,
|
||||
data: data,
|
||||
});
|
||||
if (hook.channel.error(true) == libubus.STATUS_TIMEOUT) {
|
||||
hook.timeout_count++;
|
||||
if (hook.timeout_count >= 3) {
|
||||
hostapd.printf(`DPP channel timeout for ${ifname}, disconnecting`);
|
||||
hook.channel.disconnect();
|
||||
delete hostapd.data.dpp_hooks[ifname];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
hook.timeout_count = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
let main_obj = {
|
||||
reload: {
|
||||
args: {
|
||||
|
|
@ -1411,6 +1509,20 @@ let main_obj = {
|
|||
return { interfaces };
|
||||
}
|
||||
},
|
||||
dpp_channel: {
|
||||
args: {},
|
||||
call: function(req) {
|
||||
let channel;
|
||||
let on_request = (chan_req) => dpp_channel_handle_request(channel, chan_req);
|
||||
let on_disconnect = () => dpp_channel_handle_disconnect(channel);
|
||||
|
||||
channel = req.new_channel(on_request, on_disconnect, 1);
|
||||
if (!channel)
|
||||
return libubus.STATUS_UNKNOWN_ERROR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
hostapd.data.ubus = ubus;
|
||||
|
|
@ -1452,6 +1564,7 @@ return {
|
|||
bss_event("reload", name, { reconf: reconf != 0 });
|
||||
},
|
||||
bss_remove: function(phy, name, obj) {
|
||||
delete hostapd.data.dpp_hooks[name];
|
||||
bss_event("remove", name);
|
||||
},
|
||||
sta_auth: function(iface, sta) {
|
||||
|
|
@ -1474,4 +1587,20 @@ return {
|
|||
hostapd.data.auth_obj.notify("sta_connected", msg, data_cb, null, null, 1000);
|
||||
return ret;
|
||||
},
|
||||
dpp_rx_action: function(iface, src, frame_type, freq, frame) {
|
||||
let response = dpp_rx_via_channel(iface, "rx_action", {
|
||||
ifname: iface, src, frame_type, freq, frame,
|
||||
});
|
||||
if (response && response.handled)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
dpp_rx_gas: function(iface, src, dialog_token, query, freq) {
|
||||
let response = dpp_rx_via_channel(iface, "rx_gas", {
|
||||
ifname: iface, src, dialog_token, query, freq,
|
||||
});
|
||||
if (response && response.response)
|
||||
return response.response;
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ wpas.data.config = {};
|
|||
wpas.data.iface_phy = {};
|
||||
wpas.data.iface_ubus = {};
|
||||
wpas.data.macaddr_list = {};
|
||||
wpas.data.dpp_hooks = {};
|
||||
|
||||
function iface_stop(iface)
|
||||
{
|
||||
|
|
@ -388,6 +389,132 @@ function iface_status_fill_radio(mld, radio, msg, status)
|
|||
iface_status_fill_radio_link(mld, radio, msg, status);
|
||||
}
|
||||
|
||||
function dpp_find_iface(ifname)
|
||||
{
|
||||
return wpas.interfaces[ifname];
|
||||
}
|
||||
|
||||
function dpp_channel_handle_request(channel, req)
|
||||
{
|
||||
let data = req.args ?? {};
|
||||
let iface;
|
||||
|
||||
switch (req.type) {
|
||||
case "start":
|
||||
if (!data.ifname)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
if (!wpas.interfaces[data.ifname])
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
let old_hook = wpas.data.dpp_hooks[data.ifname];
|
||||
if (old_hook && old_hook.channel != channel)
|
||||
old_hook.channel.disconnect();
|
||||
wpas.data.dpp_hooks[data.ifname] = {
|
||||
channel: channel,
|
||||
timeout_count: 0,
|
||||
};
|
||||
return 0;
|
||||
|
||||
case "stop":
|
||||
if (!data.ifname)
|
||||
return libubus.STATUS_INVALID_ARGUMENT;
|
||||
let hook = wpas.data.dpp_hooks[data.ifname];
|
||||
if (hook && hook.channel == channel)
|
||||
delete wpas.data.dpp_hooks[data.ifname];
|
||||
return 0;
|
||||
|
||||
case "tx_action":
|
||||
iface = dpp_find_iface(data.ifname);
|
||||
if (!iface)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
if (!iface.dpp_send_action(data.dst, data.freq ?? 0, data.frame))
|
||||
return libubus.STATUS_UNKNOWN_ERROR;
|
||||
return 0;
|
||||
|
||||
case "tx_gas_req":
|
||||
iface = dpp_find_iface(data.ifname);
|
||||
if (!iface)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
if (!iface.dpp_send_gas_req(data.dst, data.freq ?? 0, data.data, data.dialog_token ?? 0))
|
||||
return libubus.STATUS_UNKNOWN_ERROR;
|
||||
return 0;
|
||||
|
||||
case "dpp_bootstrap_gen":
|
||||
iface = dpp_find_iface(data.ifname);
|
||||
if (!iface)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
let gen_cmd = "DPP_BOOTSTRAP_GEN type=qrcode";
|
||||
if (data.key)
|
||||
gen_cmd += " key=" + data.key;
|
||||
if (data.curve)
|
||||
gen_cmd += " curve=" + data.curve;
|
||||
let gen_result = iface.ctrl(gen_cmd);
|
||||
if (!gen_result || gen_result == "FAIL")
|
||||
return libubus.STATUS_UNKNOWN_ERROR;
|
||||
return { id: +gen_result };
|
||||
|
||||
case "dpp_bootstrap_remove":
|
||||
iface = dpp_find_iface(data.ifname);
|
||||
if (!iface)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
let remove_result = iface.ctrl("DPP_BOOTSTRAP_REMOVE " + (data.id ?? "*"));
|
||||
return (remove_result == "OK") ? 0 : libubus.STATUS_UNKNOWN_ERROR;
|
||||
|
||||
case "dpp_chirp":
|
||||
iface = dpp_find_iface(data.ifname);
|
||||
if (!iface)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
let chirp_cmd = "DPP_CHIRP own=" + data.id;
|
||||
if (data.iter)
|
||||
chirp_cmd += " iter=" + data.iter;
|
||||
if (data.scan_interval)
|
||||
chirp_cmd += " listen=" + data.scan_interval;
|
||||
let chirp_result = iface.ctrl(chirp_cmd);
|
||||
return (chirp_result == "OK") ? 0 : libubus.STATUS_UNKNOWN_ERROR;
|
||||
|
||||
case "dpp_stop_chirp":
|
||||
iface = dpp_find_iface(data.ifname);
|
||||
if (!iface)
|
||||
return libubus.STATUS_NOT_FOUND;
|
||||
iface.ctrl("DPP_STOP_CHIRP");
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return libubus.STATUS_METHOD_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
function dpp_channel_handle_disconnect(channel)
|
||||
{
|
||||
for (let ifname, hook in wpas.data.dpp_hooks) {
|
||||
if (hook.channel == channel)
|
||||
delete wpas.data.dpp_hooks[ifname];
|
||||
}
|
||||
}
|
||||
|
||||
function dpp_rx_via_channel(ifname, method, data)
|
||||
{
|
||||
let hook = wpas.data.dpp_hooks[ifname];
|
||||
if (!hook)
|
||||
return null;
|
||||
|
||||
let response = hook.channel.request({
|
||||
method: method,
|
||||
data: data,
|
||||
});
|
||||
if (hook.channel.error(true) == libubus.STATUS_TIMEOUT) {
|
||||
hook.timeout_count++;
|
||||
if (hook.timeout_count >= 3) {
|
||||
wpas.printf(`DPP channel timeout for ${ifname}, disconnecting`);
|
||||
hook.channel.disconnect();
|
||||
delete wpas.data.dpp_hooks[ifname];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
hook.timeout_count = 0;
|
||||
return response;
|
||||
}
|
||||
|
||||
let main_obj = {
|
||||
phy_set_state: {
|
||||
args: {
|
||||
|
|
@ -673,6 +800,20 @@ let main_obj = {
|
|||
return { interfaces };
|
||||
}
|
||||
},
|
||||
dpp_channel: {
|
||||
args: {},
|
||||
call: function(req) {
|
||||
let channel;
|
||||
let on_request = (chan_req) => dpp_channel_handle_request(channel, chan_req);
|
||||
let on_disconnect = () => dpp_channel_handle_disconnect(channel);
|
||||
|
||||
channel = req.new_channel(on_request, on_disconnect, 1);
|
||||
if (!channel)
|
||||
return libubus.STATUS_UNKNOWN_ERROR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
wpas.data.ubus = ubus;
|
||||
|
|
@ -877,6 +1018,7 @@ return {
|
|||
iface_remove: function(name, obj) {
|
||||
iface_event("remove", name);
|
||||
iface_ubus_remove(name);
|
||||
delete wpas.data.dpp_hooks[name];
|
||||
},
|
||||
ctrl_event: function(name, iface, ev) {
|
||||
iface_ubus_notify(name, ev);
|
||||
|
|
@ -919,5 +1061,21 @@ return {
|
|||
wps_credentials: function(ifname, iface, cred) {
|
||||
cred.ifname = ifname;
|
||||
ubus.event("wps_credentials", cred);
|
||||
}
|
||||
},
|
||||
dpp_rx_action: function(iface, src, frame_type, freq, frame) {
|
||||
let response = dpp_rx_via_channel(iface, "rx_action", {
|
||||
ifname: iface, src, frame_type, freq, frame,
|
||||
});
|
||||
if (response && response.handled)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
dpp_rx_gas: function(iface, src, freq, gas_frame) {
|
||||
let response = dpp_rx_via_channel(iface, "rx_gas", {
|
||||
ifname: iface, src, freq, gas_frame,
|
||||
});
|
||||
if (response && response.handled)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 7 Feb 2026 09:20:30 +0000
|
||||
Subject: [PATCH] wpa_supplicant: avoid stopping DPP chirp when offchannel tx
|
||||
fails
|
||||
|
||||
Offchannel tx failure can happen when the scan finds an AP on a DFS channel.
|
||||
Make chirping more reliable by immediately switching to the next channel
|
||||
when that happens.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/wpa_supplicant/dpp_supplicant.c
|
||||
+++ b/wpa_supplicant/dpp_supplicant.c
|
||||
@@ -5131,7 +5131,10 @@ static void wpas_dpp_chirp_start(struct
|
||||
wpa_s->own_addr, broadcast,
|
||||
wpabuf_head(msg), wpabuf_len(msg),
|
||||
2000, wpas_dpp_chirp_tx_status, 0) < 0)
|
||||
- wpas_dpp_chirp_stop(wpa_s);
|
||||
+ wpas_dpp_chirp_tx_status(wpa_s, wpa_s->dpp_chirp_freq,
|
||||
+ broadcast, wpa_s->own_addr, broadcast,
|
||||
+ wpabuf_head(msg), wpabuf_len(msg),
|
||||
+ OFFCHANNEL_SEND_ACTION_FAILED);
|
||||
|
||||
wpabuf_free(announce);
|
||||
}
|
||||
|
|
@ -988,7 +988,21 @@ as adding/removing interfaces.
|
|||
MULTI_AP_FRONTHAUL_BSS);
|
||||
wpa_s->multi_ap_ie = 1;
|
||||
}
|
||||
@@ -6293,6 +6300,7 @@ void supplicant_event(void *ctx, enum wp
|
||||
@@ -5500,6 +5507,13 @@ static void wpas_event_rx_mgmt_action(st
|
||||
}
|
||||
#endif /* CONFIG_WNM */
|
||||
|
||||
+#if defined(CONFIG_GAS) && defined(CONFIG_DPP)
|
||||
+ if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
|
||||
+ mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
|
||||
+ wpas_ucode_dpp_gas_rx(wpa_s, mgmt->sa, payload, plen, freq))
|
||||
+ return;
|
||||
+#endif /* CONFIG_GAS && CONFIG_DPP */
|
||||
+
|
||||
#ifdef CONFIG_GAS
|
||||
if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
|
||||
mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
|
||||
@@ -6293,6 +6307,7 @@ void supplicant_event(void *ctx, enum wp
|
||||
event_to_string(event), event);
|
||||
#endif /* CONFIG_NO_STDOUT_DEBUG */
|
||||
|
||||
|
|
@ -1014,3 +1028,88 @@ as adding/removing interfaces.
|
|||
gpriv = wpa_s->global->ctrl_iface;
|
||||
|
||||
if (type != WPA_MSG_NO_GLOBAL && gpriv &&
|
||||
--- a/src/ap/dpp_hostapd.c
|
||||
+++ b/src/ap/dpp_hostapd.c
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "wpa_auth.h"
|
||||
#include "beacon.h"
|
||||
#include "dpp_hostapd.h"
|
||||
+#include "ucode.h"
|
||||
|
||||
|
||||
static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx);
|
||||
@@ -3017,6 +3018,9 @@ void hostapd_dpp_rx_action(struct hostap
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
|
||||
" freq=%u type=%d", MAC2STR(src), freq, type);
|
||||
|
||||
+ if (hostapd_ucode_dpp_rx_action(hapd, src, type, freq, hdr, len + 6))
|
||||
+ return;
|
||||
+
|
||||
#ifdef CONFIG_DPP2
|
||||
if (dpp_relay_rx_action(hapd->iface->interfaces->dpp,
|
||||
src, hdr, buf, len, freq, NULL, NULL,
|
||||
@@ -3116,13 +3120,19 @@ void hostapd_dpp_rx_action(struct hostap
|
||||
|
||||
struct wpabuf *
|
||||
hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
|
||||
- const u8 *query, size_t query_len,
|
||||
+ u8 dialog_token, const u8 *query, size_t query_len,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
struct dpp_authentication *auth = hapd->dpp_auth;
|
||||
struct wpabuf *resp;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
|
||||
+
|
||||
+ resp = hostapd_ucode_dpp_gas_req(hapd, sa, dialog_token,
|
||||
+ query, query_len);
|
||||
+ if (resp)
|
||||
+ return resp;
|
||||
+
|
||||
eloop_cancel_timeout(hostapd_gas_req_wait, hapd, NULL);
|
||||
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
|
||||
!ether_addr_equal(sa, auth->peer_mac_addr)) {
|
||||
--- a/src/ap/dpp_hostapd.h
|
||||
+++ b/src/ap/dpp_hostapd.h
|
||||
@@ -25,7 +25,7 @@ void hostapd_dpp_tx_status(struct hostap
|
||||
const u8 *data, size_t data_len, int ok);
|
||||
struct wpabuf *
|
||||
hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
|
||||
- const u8 *query, size_t query_len,
|
||||
+ u8 dialog_token, const u8 *query, size_t query_len,
|
||||
const u8 *data, size_t data_len);
|
||||
void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok);
|
||||
int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd);
|
||||
--- a/src/ap/gas_serv.c
|
||||
+++ b/src/ap/gas_serv.c
|
||||
@@ -1402,8 +1402,8 @@ static void gas_serv_rx_gas_initial_req(
|
||||
if (dpp) {
|
||||
struct wpabuf *msg;
|
||||
|
||||
- msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen,
|
||||
- data, len);
|
||||
+ msg = hostapd_dpp_gas_req_handler(hapd, sa, dialog_token,
|
||||
+ pos, slen, data, len);
|
||||
if (!msg)
|
||||
return;
|
||||
gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg,
|
||||
--- a/wpa_supplicant/dpp_supplicant.c
|
||||
+++ b/wpa_supplicant/dpp_supplicant.c
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "scan.h"
|
||||
#include "notify.h"
|
||||
#include "dpp_supplicant.h"
|
||||
+#include "ucode.h"
|
||||
|
||||
|
||||
static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s,
|
||||
@@ -4118,6 +4119,9 @@ void wpas_dpp_rx_action(struct wpa_suppl
|
||||
return;
|
||||
}
|
||||
wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len);
|
||||
+ if (wpas_ucode_dpp_rx_action(wpa_s, src, type, freq, hdr, len + DPP_HDR_LEN))
|
||||
+ return;
|
||||
+
|
||||
if (dpp_check_attrs(buf, len) < 0) {
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_RX "src=" MACSTR
|
||||
" freq=%u type=%d ignore=invalid-attributes",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "utils/includes.h"
|
||||
#include "utils/common.h"
|
||||
#include "utils/ucode.h"
|
||||
#include "utils/base64.h"
|
||||
#include "sta_info.h"
|
||||
#include "beacon.h"
|
||||
#include "hw_features.h"
|
||||
|
|
@ -11,6 +12,11 @@
|
|||
#include "acs.h"
|
||||
#include "ieee802_11_auth.h"
|
||||
#include "neighbor_db.h"
|
||||
#include "gas_serv.h"
|
||||
#ifdef CONFIG_DPP
|
||||
#include "common/dpp.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#endif /* CONFIG_DPP */
|
||||
#include <libubox/uloop.h>
|
||||
|
||||
static uc_resource_type_t *global_type, *bss_type, *iface_type;
|
||||
|
|
@ -942,6 +948,190 @@ uc_wpa_rkh_derive_key(uc_vm_t *vm, size_t nargs)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DPP
|
||||
static uc_value_t *
|
||||
uc_hostapd_bss_dpp_send_action(uc_vm_t *vm, size_t nargs)
|
||||
{
|
||||
struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
|
||||
uc_value_t *dst_arg = uc_fn_arg(0);
|
||||
uc_value_t *freq_arg = uc_fn_arg(1);
|
||||
uc_value_t *frame_arg = uc_fn_arg(2);
|
||||
struct wpabuf *msg;
|
||||
const char *dst_str, *frame_b64;
|
||||
unsigned char *frame_data;
|
||||
size_t frame_len;
|
||||
u8 dst[ETH_ALEN];
|
||||
unsigned int freq;
|
||||
u8 frame_type;
|
||||
int ret;
|
||||
|
||||
if (!hapd || ucv_type(dst_arg) != UC_STRING ||
|
||||
ucv_type(frame_arg) != UC_STRING)
|
||||
return NULL;
|
||||
|
||||
dst_str = ucv_string_get(dst_arg);
|
||||
if (hwaddr_aton(dst_str, dst))
|
||||
return NULL;
|
||||
|
||||
freq = ucv_int64_get(freq_arg);
|
||||
if (!freq)
|
||||
freq = hapd->iface->freq;
|
||||
|
||||
frame_b64 = ucv_string_get(frame_arg);
|
||||
frame_data = base64_decode(frame_b64, os_strlen(frame_b64), &frame_len);
|
||||
if (!frame_data)
|
||||
return NULL;
|
||||
|
||||
if (frame_len < 6) {
|
||||
os_free(frame_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
frame_type = frame_data[5];
|
||||
msg = dpp_alloc_msg(frame_type, frame_len - 6);
|
||||
if (!msg) {
|
||||
os_free(frame_data);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put_data(msg, frame_data + 6, frame_len - 6);
|
||||
os_free(frame_data);
|
||||
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
|
||||
" freq=%u type=%d", MAC2STR(dst), freq, frame_type);
|
||||
ret = hostapd_drv_send_action(hapd, freq, 0, dst,
|
||||
wpabuf_head(msg), wpabuf_len(msg));
|
||||
wpabuf_free(msg);
|
||||
|
||||
return ucv_boolean_new(ret == 0);
|
||||
}
|
||||
|
||||
static uc_value_t *
|
||||
uc_hostapd_bss_dpp_send_gas_resp(uc_vm_t *vm, size_t nargs)
|
||||
{
|
||||
struct hostapd_data *hapd = uc_fn_thisval("hostapd.bss");
|
||||
uc_value_t *dst_arg = uc_fn_arg(0);
|
||||
uc_value_t *token_arg = uc_fn_arg(1);
|
||||
uc_value_t *data_arg = uc_fn_arg(2);
|
||||
uc_value_t *freq_arg = uc_fn_arg(3);
|
||||
const char *dst_str, *data_b64;
|
||||
unsigned char *data;
|
||||
size_t data_len;
|
||||
struct wpabuf *buf;
|
||||
u8 dst[ETH_ALEN];
|
||||
u8 dialog_token;
|
||||
int freq;
|
||||
|
||||
if (!hapd || ucv_type(dst_arg) != UC_STRING ||
|
||||
ucv_type(token_arg) != UC_INTEGER ||
|
||||
ucv_type(data_arg) != UC_STRING)
|
||||
return NULL;
|
||||
|
||||
dst_str = ucv_string_get(dst_arg);
|
||||
if (hwaddr_aton(dst_str, dst))
|
||||
return NULL;
|
||||
|
||||
dialog_token = ucv_int64_get(token_arg);
|
||||
if (dialog_token == 0)
|
||||
return NULL;
|
||||
|
||||
freq = ucv_int64_get(freq_arg);
|
||||
if (!freq)
|
||||
freq = hapd->iface->freq;
|
||||
|
||||
data_b64 = ucv_string_get(data_arg);
|
||||
data = base64_decode(data_b64, os_strlen(data_b64), &data_len);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
buf = wpabuf_alloc_copy(data, data_len);
|
||||
os_free(data);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
|
||||
gas_serv_req_dpp_processing(hapd, dst, dialog_token, 0, buf, freq);
|
||||
|
||||
return ucv_boolean_new(true);
|
||||
}
|
||||
|
||||
int hostapd_ucode_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||
u8 frame_type, unsigned int freq,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
uc_value_t *val;
|
||||
char addr[18];
|
||||
char *frame_b64;
|
||||
size_t frame_b64_len;
|
||||
int ret = 0;
|
||||
|
||||
if (wpa_ucode_call_prepare("dpp_rx_action"))
|
||||
return 0;
|
||||
|
||||
os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(src));
|
||||
frame_b64 = base64_encode_no_lf(data, data_len, &frame_b64_len);
|
||||
if (!frame_b64) {
|
||||
ucv_put(wpa_ucode_call(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uc_value_push(ucv_string_new(hapd->conf->iface));
|
||||
uc_value_push(ucv_string_new(addr));
|
||||
uc_value_push(ucv_int64_new(frame_type));
|
||||
uc_value_push(ucv_int64_new(freq));
|
||||
uc_value_push(ucv_string_new(frame_b64));
|
||||
os_free(frame_b64);
|
||||
|
||||
val = wpa_ucode_call(5);
|
||||
ret = ucv_is_truish(val);
|
||||
ucv_put(val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct wpabuf *hostapd_ucode_dpp_gas_req(struct hostapd_data *hapd,
|
||||
const u8 *sa, u8 dialog_token,
|
||||
const u8 *query, size_t query_len)
|
||||
{
|
||||
uc_value_t *val;
|
||||
char addr[18];
|
||||
char *query_b64;
|
||||
size_t query_b64_len;
|
||||
struct wpabuf *ret = NULL;
|
||||
|
||||
if (wpa_ucode_call_prepare("dpp_rx_gas"))
|
||||
return NULL;
|
||||
|
||||
os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sa));
|
||||
query_b64 = base64_encode_no_lf(query, query_len, &query_b64_len);
|
||||
if (!query_b64) {
|
||||
ucv_put(wpa_ucode_call(0));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uc_value_push(ucv_string_new(hapd->conf->iface));
|
||||
uc_value_push(ucv_string_new(addr));
|
||||
uc_value_push(ucv_int64_new(dialog_token));
|
||||
uc_value_push(ucv_string_new(query_b64));
|
||||
os_free(query_b64);
|
||||
|
||||
val = wpa_ucode_call(4);
|
||||
if (ucv_type(val) == UC_STRING) {
|
||||
const char *resp_b64 = ucv_string_get(val);
|
||||
size_t resp_len;
|
||||
unsigned char *resp_data;
|
||||
|
||||
resp_data = base64_decode(resp_b64, os_strlen(resp_b64),
|
||||
&resp_len);
|
||||
if (resp_data) {
|
||||
ret = wpabuf_alloc_copy(resp_data, resp_len);
|
||||
os_free(resp_data);
|
||||
}
|
||||
}
|
||||
ucv_put(val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
int hostapd_ucode_init(struct hapd_interfaces *ifaces)
|
||||
{
|
||||
static const uc_function_list_t global_fns[] = {
|
||||
|
|
@ -959,6 +1149,10 @@ int hostapd_ucode_init(struct hapd_interfaces *ifaces)
|
|||
{ "set_config", uc_hostapd_bss_set_config },
|
||||
{ "rename", uc_hostapd_bss_rename },
|
||||
{ "delete", uc_hostapd_bss_delete },
|
||||
#ifdef CONFIG_DPP
|
||||
{ "dpp_send_action", uc_hostapd_bss_dpp_send_action },
|
||||
{ "dpp_send_gas_resp", uc_hostapd_bss_dpp_send_gas_resp },
|
||||
#endif /* CONFIG_DPP */
|
||||
};
|
||||
static const uc_function_list_t iface_fns[] = {
|
||||
{ "state", uc_hostapd_iface_state },
|
||||
|
|
|
|||
|
|
@ -32,6 +32,15 @@ void hostapd_ucode_sta_connected(struct hostapd_data *hapd, struct sta_info *sta
|
|||
void hostapd_ucode_apup_newpeer(struct hostapd_data *hapd, const char *ifname);
|
||||
#endif // def CONFIG_APUP
|
||||
|
||||
#ifdef CONFIG_DPP
|
||||
int hostapd_ucode_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||
u8 frame_type, unsigned int freq,
|
||||
const u8 *data, size_t data_len);
|
||||
struct wpabuf *hostapd_ucode_dpp_gas_req(struct hostapd_data *hapd,
|
||||
const u8 *sa, u8 dialog_token,
|
||||
const u8 *query, size_t query_len);
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
#else
|
||||
|
||||
static inline int hostapd_ucode_init(struct hapd_interfaces *ifaces)
|
||||
|
|
@ -58,6 +67,24 @@ static inline void hostapd_ucode_free_bss(struct hostapd_data *hapd)
|
|||
{
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DPP
|
||||
static inline int hostapd_ucode_dpp_rx_action(struct hostapd_data *hapd,
|
||||
const u8 *src, u8 frame_type,
|
||||
unsigned int freq,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline struct wpabuf *hostapd_ucode_dpp_gas_req(struct hostapd_data *hapd,
|
||||
const u8 *sa,
|
||||
u8 dialog_token,
|
||||
const u8 *query,
|
||||
size_t query_len)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
#endif
|
||||
|
||||
static inline void hostapd_ucode_create_bss(struct hostapd_data *hapd)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "utils/includes.h"
|
||||
#include "utils/common.h"
|
||||
#include "utils/ucode.h"
|
||||
#include "utils/base64.h"
|
||||
#include "drivers/driver.h"
|
||||
#include "ap/hostapd.h"
|
||||
#include "wpa_supplicant_i.h"
|
||||
|
|
@ -9,6 +10,12 @@
|
|||
#include "config.h"
|
||||
#include "bss.h"
|
||||
#include "ucode.h"
|
||||
#include "offchannel.h"
|
||||
#ifdef CONFIG_DPP
|
||||
#include "common/dpp.h"
|
||||
#include "common/wpa_ctrl.h"
|
||||
#include "common/gas.h"
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
static struct wpa_global *wpa_global;
|
||||
static uc_resource_type_t *global_type, *iface_type;
|
||||
|
|
@ -236,6 +243,189 @@ void wpas_ucode_wps_complete(struct wpa_supplicant *wpa_s,
|
|||
#endif /* CONFIG_WPS */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DPP
|
||||
int wpas_ucode_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
|
||||
u8 frame_type, unsigned int freq,
|
||||
const u8 *data, size_t data_len)
|
||||
{
|
||||
uc_value_t *val;
|
||||
char addr[18];
|
||||
char *frame_b64;
|
||||
size_t frame_b64_len;
|
||||
int ret = 0;
|
||||
|
||||
if (wpa_ucode_call_prepare("dpp_rx_action"))
|
||||
return 0;
|
||||
|
||||
os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(src));
|
||||
frame_b64 = base64_encode_no_lf(data, data_len, &frame_b64_len);
|
||||
if (!frame_b64) {
|
||||
ucv_put(wpa_ucode_call(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uc_value_push(ucv_string_new(wpa_s->ifname));
|
||||
uc_value_push(ucv_string_new(addr));
|
||||
uc_value_push(ucv_int64_new(frame_type));
|
||||
uc_value_push(ucv_int64_new(freq));
|
||||
uc_value_push(ucv_string_new(frame_b64));
|
||||
os_free(frame_b64);
|
||||
|
||||
val = wpa_ucode_call(5);
|
||||
ret = ucv_is_truish(val);
|
||||
ucv_put(val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wpas_ucode_dpp_gas_rx(struct wpa_supplicant *wpa_s, const u8 *src,
|
||||
const u8 *data, size_t data_len, unsigned int freq)
|
||||
{
|
||||
uc_value_t *val;
|
||||
char addr[18];
|
||||
char *gas_b64;
|
||||
size_t gas_b64_len;
|
||||
int ret = 0;
|
||||
|
||||
if (data_len < 2)
|
||||
return 0;
|
||||
|
||||
if (wpa_ucode_call_prepare("dpp_rx_gas"))
|
||||
return 0;
|
||||
|
||||
os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(src));
|
||||
gas_b64 = base64_encode_no_lf(data, data_len, &gas_b64_len);
|
||||
if (!gas_b64) {
|
||||
ucv_put(wpa_ucode_call(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
uc_value_push(ucv_string_new(wpa_s->ifname));
|
||||
uc_value_push(ucv_string_new(addr));
|
||||
uc_value_push(ucv_int64_new(freq));
|
||||
uc_value_push(ucv_string_new(gas_b64));
|
||||
os_free(gas_b64);
|
||||
|
||||
val = wpa_ucode_call(4);
|
||||
ret = ucv_is_truish(val);
|
||||
ucv_put(val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uc_value_t *
|
||||
uc_wpas_iface_dpp_send_action(uc_vm_t *vm, size_t nargs)
|
||||
{
|
||||
struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
|
||||
uc_value_t *dst_arg = uc_fn_arg(0);
|
||||
uc_value_t *freq_arg = uc_fn_arg(1);
|
||||
uc_value_t *frame_arg = uc_fn_arg(2);
|
||||
struct wpabuf *msg;
|
||||
const char *dst_str, *frame_b64;
|
||||
unsigned char *frame_data;
|
||||
size_t frame_len;
|
||||
u8 dst[ETH_ALEN];
|
||||
unsigned int freq;
|
||||
u8 frame_type;
|
||||
int ret;
|
||||
|
||||
if (!wpa_s || ucv_type(dst_arg) != UC_STRING ||
|
||||
ucv_type(frame_arg) != UC_STRING)
|
||||
return NULL;
|
||||
|
||||
dst_str = ucv_string_get(dst_arg);
|
||||
if (hwaddr_aton(dst_str, dst))
|
||||
return NULL;
|
||||
|
||||
freq = ucv_int64_get(freq_arg);
|
||||
if (!freq)
|
||||
freq = wpa_s->assoc_freq;
|
||||
|
||||
frame_b64 = ucv_string_get(frame_arg);
|
||||
frame_data = base64_decode(frame_b64, os_strlen(frame_b64), &frame_len);
|
||||
if (!frame_data)
|
||||
return NULL;
|
||||
|
||||
if (frame_len < DPP_HDR_LEN) {
|
||||
os_free(frame_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
frame_type = frame_data[5];
|
||||
msg = dpp_alloc_msg(frame_type, frame_len - DPP_HDR_LEN);
|
||||
if (!msg) {
|
||||
os_free(frame_data);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put_data(msg, frame_data + DPP_HDR_LEN, frame_len - DPP_HDR_LEN);
|
||||
os_free(frame_data);
|
||||
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
|
||||
MAC2STR(dst), freq, frame_type);
|
||||
ret = offchannel_send_action(wpa_s, freq, dst, wpa_s->own_addr,
|
||||
broadcast_ether_addr,
|
||||
wpabuf_head(msg), wpabuf_len(msg),
|
||||
500, NULL, 0);
|
||||
wpabuf_free(msg);
|
||||
|
||||
return ucv_boolean_new(ret == 0);
|
||||
}
|
||||
|
||||
static uc_value_t *
|
||||
uc_wpas_iface_dpp_send_gas_req(uc_vm_t *vm, size_t nargs)
|
||||
{
|
||||
struct wpa_supplicant *wpa_s = uc_fn_thisval("wpas.iface");
|
||||
uc_value_t *dst_arg = uc_fn_arg(0);
|
||||
uc_value_t *freq_arg = uc_fn_arg(1);
|
||||
uc_value_t *data_arg = uc_fn_arg(2);
|
||||
uc_value_t *token_arg = uc_fn_arg(3);
|
||||
const char *dst_str, *data_b64;
|
||||
unsigned char *data;
|
||||
size_t data_len;
|
||||
u8 dst[ETH_ALEN];
|
||||
unsigned int freq;
|
||||
u8 dialog_token;
|
||||
struct wpabuf *buf;
|
||||
int ret;
|
||||
|
||||
if (!wpa_s || ucv_type(dst_arg) != UC_STRING ||
|
||||
ucv_type(data_arg) != UC_STRING)
|
||||
return NULL;
|
||||
|
||||
dst_str = ucv_string_get(dst_arg);
|
||||
if (hwaddr_aton(dst_str, dst))
|
||||
return NULL;
|
||||
|
||||
freq = ucv_int64_get(freq_arg);
|
||||
if (!freq)
|
||||
freq = wpa_s->assoc_freq;
|
||||
|
||||
dialog_token = ucv_int64_get(token_arg);
|
||||
if (!dialog_token)
|
||||
dialog_token = 1;
|
||||
|
||||
data_b64 = ucv_string_get(data_arg);
|
||||
data = base64_decode(data_b64, os_strlen(data_b64), &data_len);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
buf = gas_build_initial_req(dialog_token, data_len);
|
||||
if (!buf) {
|
||||
os_free(data);
|
||||
return NULL;
|
||||
}
|
||||
wpabuf_put_data(buf, data, data_len);
|
||||
os_free(data);
|
||||
|
||||
ret = offchannel_send_action(wpa_s, freq, dst, wpa_s->own_addr,
|
||||
broadcast_ether_addr, wpabuf_head(buf),
|
||||
wpabuf_len(buf), 500, NULL, 0);
|
||||
wpabuf_free(buf);
|
||||
|
||||
return ucv_boolean_new(ret == 0);
|
||||
}
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
static const char *obj_stringval(uc_value_t *obj, const char *name)
|
||||
{
|
||||
uc_value_t *val = ucv_object_get(obj, name, NULL);
|
||||
|
|
@ -502,6 +692,10 @@ int wpas_ucode_init(struct wpa_global *gl)
|
|||
{ "status", uc_wpas_iface_status },
|
||||
{ "ctrl", uc_wpas_iface_ctrl },
|
||||
{ "config", uc_wpas_iface_config },
|
||||
#ifdef CONFIG_DPP
|
||||
{ "dpp_send_action", uc_wpas_iface_dpp_send_action },
|
||||
{ "dpp_send_gas_req", uc_wpas_iface_dpp_send_gas_req },
|
||||
#endif /* CONFIG_DPP */
|
||||
};
|
||||
uc_value_t *data, *proto;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,28 @@ void wpas_ucode_ctrl_event(struct wpa_supplicant *wpa_s, const char *str, size_t
|
|||
bool wpas_ucode_bss_allowed(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
|
||||
void wpas_ucode_wps_complete(struct wpa_supplicant *wpa_s,
|
||||
const struct wps_credential *cred);
|
||||
#ifdef CONFIG_DPP
|
||||
int wpas_ucode_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
|
||||
u8 frame_type, unsigned int freq,
|
||||
const u8 *data, size_t data_len);
|
||||
int wpas_ucode_dpp_gas_rx(struct wpa_supplicant *wpa_s, const u8 *src,
|
||||
const u8 *data, size_t data_len, unsigned int freq);
|
||||
#else
|
||||
static inline int wpas_ucode_dpp_rx_action(struct wpa_supplicant *wpa_s,
|
||||
const u8 *src, u8 frame_type,
|
||||
unsigned int freq, const u8 *data,
|
||||
size_t data_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wpas_ucode_dpp_gas_rx(struct wpa_supplicant *wpa_s,
|
||||
const u8 *src, const u8 *data,
|
||||
size_t data_len, unsigned int freq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_DPP */
|
||||
#else
|
||||
static inline int wpas_ucode_init(struct wpa_global *gl)
|
||||
{
|
||||
|
|
@ -62,6 +84,21 @@ static inline bool wpas_ucode_bss_allowed(struct wpa_supplicant *wpa_s, struct w
|
|||
static inline void wpas_ucode_wps_complete(struct wpa_supplicant *wpa_s, const struct wps_credential *cred)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int wpas_ucode_dpp_rx_action(struct wpa_supplicant *wpa_s,
|
||||
const u8 *src, u8 frame_type,
|
||||
unsigned int freq, const u8 *data,
|
||||
size_t data_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int wpas_ucode_dpp_gas_rx(struct wpa_supplicant *wpa_s,
|
||||
const u8 *src, const u8 *data,
|
||||
size_t data_len, unsigned int freq)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue