};
hostapd.data.mld = {};
+hostapd.data.dpp_hooks = {};
function iface_remove(cfg)
{
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: {
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;
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) {
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;
+ },
};
wpas.data.iface_phy = {};
wpas.data.iface_ubus = {};
wpas.data.macaddr_list = {};
+wpas.data.dpp_hooks = {};
function iface_stop(iface)
{
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: {
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;
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);
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;
+ },
};
--- /dev/null
+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);
+ }
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 */
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",
#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"
#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;
#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[] = {
{ "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 },
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)
{
}
+#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)
#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"
#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;
#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);
{ "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;
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)
{
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