]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
hostapd: add DPP ucode API for external frame handling
authorFelix Fietkau <nbd@nbd.name>
Tue, 3 Feb 2026 19:13:24 +0000 (19:13 +0000)
committerFelix Fietkau <nbd@nbd.name>
Sat, 7 Feb 2026 09:27:57 +0000 (09:27 +0000)
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>
package/network/services/hostapd/files/hostapd.uc
package/network/services/hostapd/files/wpa_supplicant.uc
package/network/services/hostapd/patches/161-wpa_supplicant-avoid-stopping-DPP-chirp-when-offchan.patch [new file with mode: 0644]
package/network/services/hostapd/patches/601-ucode_support.patch
package/network/services/hostapd/src/src/ap/ucode.c
package/network/services/hostapd/src/src/ap/ucode.h
package/network/services/hostapd/src/wpa_supplicant/ucode.c
package/network/services/hostapd/src/wpa_supplicant/ucode.h

index 5920478a18f61353ac9f38831596c39e92a986d8..6290b139e7566fac52c5ddba7c75bb7d6b4f6da2 100644 (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;
+       },
 };
index 76d7859af8149929b24b159a6a4fd0dd40a20591..ea60794245fbdb0e993c390ff5a2c1144679c2bf 100644 (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;
+       },
 };
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 (file)
index 0000000..4b6ed64
--- /dev/null
@@ -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);
+ }
index 74db671e4f4b80fced7158e457ef280e917487c7..02b7404254c87648511b0247de5612eab55ed7c2 100644 (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",
index 3867a07218633e010d6e1b0666eafd2771dac443..adaf20d19e5a4bb3cdf1ff9e7115dc213ef009a0 100644 (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"
 #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 },
index d0b00e59652a06973f14ed989493ced8e34b81ba..c287ab566549f6139015bfb5070c005082eda8af 100644 (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)
index 35345323c3e8eab35c285c692ec6cfe0d3908892..4de2e0453213f3d1bc104d69e9bc6298ce506afd 100644 (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;
 
index 1b9ef50f1b5f10e22efa6bfe537713b970561651..61d6c1f4d980af3cc5205e7c046b08f1e8e4baed 100644 (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