From: Felix Fietkau Date: Tue, 3 Feb 2026 19:13:24 +0000 (+0000) Subject: hostapd: add DPP ucode API for external frame handling X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=09de75950678db1d41aeb5840b41af432a4d4163;p=thirdparty%2Fopenwrt.git hostapd: add DPP ucode API for external frame handling 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 --- diff --git a/package/network/services/hostapd/files/hostapd.uc b/package/network/services/hostapd/files/hostapd.uc index 5920478a18f..6290b139e75 100644 --- a/package/network/services/hostapd/files/hostapd.uc +++ b/package/network/services/hostapd/files/hostapd.uc @@ -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; + }, }; diff --git a/package/network/services/hostapd/files/wpa_supplicant.uc b/package/network/services/hostapd/files/wpa_supplicant.uc index 76d7859af81..ea60794245f 100644 --- a/package/network/services/hostapd/files/wpa_supplicant.uc +++ b/package/network/services/hostapd/files/wpa_supplicant.uc @@ -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; + }, }; diff --git a/package/network/services/hostapd/patches/161-wpa_supplicant-avoid-stopping-DPP-chirp-when-offchan.patch b/package/network/services/hostapd/patches/161-wpa_supplicant-avoid-stopping-DPP-chirp-when-offchan.patch new file mode 100644 index 00000000000..4b6ed646f5f --- /dev/null +++ b/package/network/services/hostapd/patches/161-wpa_supplicant-avoid-stopping-DPP-chirp-when-offchan.patch @@ -0,0 +1,26 @@ +From: Felix Fietkau +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 +--- + +--- 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); + } diff --git a/package/network/services/hostapd/patches/601-ucode_support.patch b/package/network/services/hostapd/patches/601-ucode_support.patch index 74db671e4f4..02b7404254c 100644 --- a/package/network/services/hostapd/patches/601-ucode_support.patch +++ b/package/network/services/hostapd/patches/601-ucode_support.patch @@ -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", diff --git a/package/network/services/hostapd/src/src/ap/ucode.c b/package/network/services/hostapd/src/src/ap/ucode.c index 3867a072186..adaf20d19e5 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.c +++ b/package/network/services/hostapd/src/src/ap/ucode.c @@ -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 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 }, diff --git a/package/network/services/hostapd/src/src/ap/ucode.h b/package/network/services/hostapd/src/src/ap/ucode.h index d0b00e59652..c287ab56654 100644 --- a/package/network/services/hostapd/src/src/ap/ucode.h +++ b/package/network/services/hostapd/src/src/ap/ucode.h @@ -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) diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.c b/package/network/services/hostapd/src/wpa_supplicant/ucode.c index 35345323c3e..4de2e045321 100644 --- a/package/network/services/hostapd/src/wpa_supplicant/ucode.c +++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.c @@ -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; diff --git a/package/network/services/hostapd/src/wpa_supplicant/ucode.h b/package/network/services/hostapd/src/wpa_supplicant/ucode.h index 1b9ef50f1b5..61d6c1f4d98 100644 --- a/package/network/services/hostapd/src/wpa_supplicant/ucode.h +++ b/package/network/services/hostapd/src/wpa_supplicant/ucode.h @@ -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