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

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:
Felix Fietkau 2026-02-03 19:13:24 +00:00
parent 68c2ab8f5f
commit 09de759506
8 changed files with 866 additions and 2 deletions

View file

@ -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;
},
};

View file

@ -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;
},
};

View file

@ -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);
}

View file

@ -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",

View file

@ -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 },

View file

@ -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)

View file

@ -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;

View file

@ -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