From fa7c05d29ac1515c61de7169de70b82a590a6007 Mon Sep 17 00:00:00 2001 From: cr Date: Fri, 30 Oct 2026 13:55:31 +0400 Subject: [PATCH] commit for tech_9 --- common.h | 64 ++++ includes.h | 27 ++ jl_wifi_ctrl.c | 603 ++++++++++++++++++++++++++++++++++++ jl_wifi_ctrl.h | 86 ++++++ jl_wpa_os_shim.c | 48 +++ wpa_ctrl.c | 776 +++++++++++++++++++++++++++++++++++++++++++++++ wpa_ctrl.h | 679 +++++++++++++++++++++++++++++++++++++++++ wpa_ctrl.mk | 21 ++ 8 files changed, 2304 insertions(+) create mode 100644 common.h create mode 100644 includes.h create mode 100644 jl_wifi_ctrl.c create mode 100644 jl_wifi_ctrl.h create mode 100644 jl_wpa_os_shim.c create mode 100644 wpa_ctrl.c create mode 100644 wpa_ctrl.h create mode 100644 wpa_ctrl.mk diff --git a/common.h b/common.h new file mode 100644 index 0000000..4fc2e29 --- /dev/null +++ b/common.h @@ -0,0 +1,64 @@ +/* + * Minimal replacement for wpa_supplicant's src/utils/common.h + the parts + * of src/utils/os.h that wpa_ctrl.c (CONFIG_CTRL_IFACE_UNIX build path) + * actually needs. Trivial os_* helpers are mapped directly onto libc via + * macros (matching upstream's own os.h fallback definitions); the handful + * that need a real body (os_zalloc, os_strlcpy, os_get_reltime, os_sleep) + * are implemented in jl_wpa_os_shim.c. + */ + +#ifndef JL_WPA_CTRL_COMMON_H +#define JL_WPA_CTRL_COMMON_H + +typedef long os_time_t; + +struct os_reltime { + os_time_t sec; + os_time_t usec; +}; + +void *os_zalloc(size_t size); +size_t os_strlcpy(char *dest, const char *src, size_t siz); +int os_get_reltime(struct os_reltime *t); +void os_sleep(os_time_t sec, os_time_t usec); + +#define os_free(p) free((p)) +#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#define os_snprintf snprintf + +static inline int os_snprintf_error(size_t size, int res) +{ + return res < 0 || (unsigned int) res >= size; +} + +static inline int os_reltime_before(struct os_reltime *a, + struct os_reltime *b) +{ + return (a->sec < b->sec) || + (a->sec == b->sec && a->usec < b->usec); +} + +static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b, + struct os_reltime *res) +{ + res->sec = a->sec - b->sec; + res->usec = a->usec - b->usec; + if (res->usec < 0) { + res->sec--; + res->usec += 1000000; + } +} + +static inline int os_reltime_expired(struct os_reltime *now, + struct os_reltime *ts, + os_time_t timeout_secs) +{ + struct os_reltime age; + + os_reltime_sub(now, ts, &age); + return (age.sec > timeout_secs) || + (age.sec == timeout_secs && age.usec > 0); +} + +#endif /* JL_WPA_CTRL_COMMON_H */ diff --git a/includes.h b/includes.h new file mode 100644 index 0000000..186d92b --- /dev/null +++ b/includes.h @@ -0,0 +1,27 @@ +/* + * Minimal replacement for wpa_supplicant's build-time includes.h. + * + * The real wpa_supplicant tree uses this file as the single place to select + * OS/libc specific headers before any other header is included. We only + * vendor wpa_ctrl.c (CONFIG_CTRL_IFACE_UNIX build path) into this project, + * so this file only needs to provide what that one translation unit uses. + */ + +#ifndef JL_WPA_CTRL_INCLUDES_H +#define JL_WPA_CTRL_INCLUDES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif /* JL_WPA_CTRL_INCLUDES_H */ diff --git a/jl_wifi_ctrl.c b/jl_wifi_ctrl.c new file mode 100644 index 0000000..920bad8 --- /dev/null +++ b/jl_wifi_ctrl.c @@ -0,0 +1,603 @@ +#include +#include +#include +#include +#include + +#include "jl_modbus.h" +#include "jl_wifi_ctrl.h" + +extern uint32_t custom_tick_get(void); + +#ifndef JL_WIFI_CTRL_PATH +#define JL_WIFI_CTRL_PATH "/var/run/wpa_supplicant/wlan0" +#endif + +#define JL_WIFI_CTRL_RECONNECT_COOLDOWN_MS 2000 +/* Used by the periodic SCAN trigger (JLV_WLAN_SCAN_INTERVAL_MS apart, so a + * blocking wait here is cheap) and other short one-shot commands. STATUS and + * SCAN_RESULTS reads no longer go through this: they're polled on a tight UI + * cycle (every 1-2s) and are handled asynchronously instead (see + * jl_wifi_ctrl_request() / jl_wifi_ctrl_poll()) so they never block the UI + * task thread. */ +#define JL_WIFI_CTRL_TXN_TIMEOUT_MS 250 +/* Used by one-shot config/action commands (SET_NETWORK, ENABLE_NETWORK, + * RECONNECT, ...) triggered from a button press rather than a poll tick. + * wpa_supplicant can take a while to reply while it's busy processing a + * just-finished scan, which is exactly when the user tends to fire these + * (right after picking a network from the scan list) - 250ms was too + * tight and made the command silently no-op, leaving the network half + * configured. */ +#define JL_WIFI_CTRL_ACTION_TIMEOUT_MS 1500 +/* Safety net for an async STATUS/SCAN_RESULTS request that never gets a + * reply (e.g. wpa_supplicant wedged) - without this pending_req would stay + * stuck forever and every future request would silently no-op. */ +#define JL_WIFI_CTRL_REQ_TIMEOUT_MS 2000 +#define JL_WIFI_CTRL_SSID_RAW_MAX (JLV_WIFI_SSID_BYTES*4+1) +#define JL_WIFI_CTRL_SCAN_CACHE_MAX 32 + +typedef enum { + JL_WIFI_CTRL_REQ_NONE = 0, + JL_WIFI_CTRL_REQ_STATUS, + JL_WIFI_CTRL_REQ_SCAN_RESULTS, +} jl_wifi_ctrl_req_t; + +static struct wpa_ctrl* ctrl_cmd; +static struct wpa_ctrl* ctrl_mon; +static uint32_t last_connect_attempt_ms; +static uint32_t pending_events; + +/* STATUS/SCAN_RESULTS are request/response commands on the single ctrl_cmd + * socket, so only one can be in flight at a time. jl_wifi_ctrl_request() + * sends without waiting; the reply is picked up later, non-blocking, from + * jl_wifi_ctrl_poll() (already ticking every 100ms) and cached below. */ +static jl_wifi_ctrl_req_t pending_req = JL_WIFI_CTRL_REQ_NONE; +static uint32_t pending_req_sent_ms; + +static jl_wifi_ctrl_status_t cached_status; +static bool cached_status_valid; + +static jl_wifi_ctrl_scan_result_t cached_scan[JL_WIFI_CTRL_SCAN_CACHE_MAX]; +static int cached_scan_count; + +static void jl_wifi_ctrl_apply_reply(jl_wifi_ctrl_req_t req, char* reply, size_t reply_len); + +static void jl_wifi_ctrl_teardown(void) +{ + if (ctrl_cmd) { + wpa_ctrl_close(ctrl_cmd); + ctrl_cmd = NULL; + } + if (ctrl_mon) { + wpa_ctrl_close(ctrl_mon); + ctrl_mon = NULL; + } + + /* Any request in flight belonged to the now-closed socket; its reply, + * if it ever arrives, is not coming back to us. Drop cached data too, + * so callers see "unavailable" instead of stale pre-disconnect state. */ + pending_req = JL_WIFI_CTRL_REQ_NONE; + cached_status_valid = false; + cached_scan_count = 0; +} + +static bool jl_wifi_ctrl_ensure_connected(void) +{ + if (ctrl_cmd && ctrl_mon) { + return true; + } + + uint32_t now = custom_tick_get(); + if (last_connect_attempt_ms != 0 && + (uint32_t)(now - last_connect_attempt_ms) < JL_WIFI_CTRL_RECONNECT_COOLDOWN_MS) { + return false; + } + last_connect_attempt_ms = now; + + jl_wifi_ctrl_teardown(); + + ctrl_cmd = wpa_ctrl_open(JL_WIFI_CTRL_PATH); + if (!ctrl_cmd) { + return false; + } + + ctrl_mon = wpa_ctrl_open(JL_WIFI_CTRL_PATH); + if (!ctrl_mon || wpa_ctrl_attach(ctrl_mon) != 0) { + log_debug("jl_wifi_ctrl: attach failed"); + jl_wifi_ctrl_teardown(); + return false; + } + + log_info("jl_wifi_ctrl: connected to %s", JL_WIFI_CTRL_PATH); + return true; +} + +/* Bounded (~JL_WIFI_CTRL_TXN_TIMEOUT_MS) request/response, bypassing + * wpa_ctrl_request()'s internal 10s blocking retry loop - it's fine for the + * rare ATTACH/DETACH call (done via wpa_ctrl_attach() above), but far too + * coarse for commands issued from a UI poll tick. */ +static bool jl_wifi_ctrl_txn_timeout(struct wpa_ctrl* ctrl, const char* cmd, char* reply, size_t* reply_len, int timeout_ms) +{ + int fd = wpa_ctrl_get_fd(ctrl); + if (fd < 0) { + return false; + } + + if (send(fd, cmd, strlen(cmd), 0) < 0) { + log_debug("jl_wifi_ctrl: send failed, reconnecting"); + jl_wifi_ctrl_teardown(); + return false; + } + + struct pollfd pfd = { .fd = fd, .events = POLLIN }; + if (poll(&pfd, 1, timeout_ms) <= 0 || !(pfd.revents & POLLIN)) { + return false; + } + + return wpa_ctrl_recv(ctrl, reply, reply_len) == 0; +} + +/* Waits (up to timeout_ms) for a STATUS/SCAN_RESULTS request already in + * flight to be answered, before a synchronous command is allowed to send on + * the same socket - otherwise the two replies could arrive out of order and + * get attributed to the wrong request. Rare in practice (async requests are + * answered within a poll tick or two), so this basically never actually + * waits. */ +static void jl_wifi_ctrl_drain_pending(int timeout_ms) +{ + if (pending_req == JL_WIFI_CTRL_REQ_NONE) { + return; + } + + int fd = wpa_ctrl_get_fd(ctrl_cmd); + struct pollfd pfd = { .fd = fd, .events = POLLIN }; + if (fd >= 0 && poll(&pfd, 1, timeout_ms) > 0 && (pfd.revents & POLLIN)) { + static char reply[8192]; + size_t reply_len = sizeof(reply) - 1; + if (wpa_ctrl_recv(ctrl_cmd, reply, &reply_len) == 0) { + jl_wifi_ctrl_apply_reply(pending_req, reply, reply_len); + } + } + pending_req = JL_WIFI_CTRL_REQ_NONE; +} + +static bool jl_wifi_ctrl_cmd_ok_timeout(const char* cmd, int timeout_ms) +{ + if (!jl_wifi_ctrl_ensure_connected()) { + return false; + } + + jl_wifi_ctrl_drain_pending(timeout_ms); + + char reply[64]; + size_t reply_len = sizeof(reply) - 1; + if (!jl_wifi_ctrl_txn_timeout(ctrl_cmd, cmd, reply, &reply_len, timeout_ms)) { + log_warn("jl_wifi_ctrl: '%s' timed out after %dms", cmd, timeout_ms); + return false; + } + + reply[reply_len] = '\0'; + bool ok = strncmp(reply, "OK", 2) == 0; + if (!ok) { + log_warn("jl_wifi_ctrl: '%s' -> '%s'", cmd, reply); + } + return ok; +} + +static bool jl_wifi_ctrl_cmd_ok(const char* cmd) +{ + return jl_wifi_ctrl_cmd_ok_timeout(cmd, JL_WIFI_CTRL_TXN_TIMEOUT_MS); +} + +static bool jl_wifi_ctrl_action_ok(const char* cmd) +{ + return jl_wifi_ctrl_cmd_ok_timeout(cmd, JL_WIFI_CTRL_ACTION_TIMEOUT_MS); +} + +static const char* jl_wifi_ctrl_find_field(const char* buf, const char* key) +{ + size_t keylen = strlen(key); + if (strncmp(buf, key, keylen) == 0) { + return buf + keylen; + } + + const char* p = buf; + while ((p = strstr(p, key)) != NULL) { + if (p[-1] == '\n') { + return p + keylen; + } + p++; + } + + return NULL; +} + +static void jl_wifi_ctrl_copy_field(const char* val, char* out, size_t outsz) +{ + const char* nl = strchr(val, '\n'); + size_t len = nl ? (size_t)(nl - val) : strlen(val); + if (len >= outsz) { + len = outsz - 1; + } + memcpy(out, val, len); + out[len] = '\0'; +} + +static jl_wifi_ctrl_state_t jl_wifi_ctrl_map_state(const char* s) +{ + if (strcmp(s, "COMPLETED") == 0) return JL_WIFI_CTRL_STATE_COMPLETED; + if (strcmp(s, "DISCONNECTED") == 0) return JL_WIFI_CTRL_STATE_DISCONNECTED; + if (strcmp(s, "SCANNING") == 0) return JL_WIFI_CTRL_STATE_SCANNING; + if (strcmp(s, "ASSOCIATED") == 0) return JL_WIFI_CTRL_STATE_ASSOCIATED; + if (strcmp(s, "4WAY_HANDSHAKE") == 0) return JL_WIFI_CTRL_STATE_4WAY_HANDSHAKE; + if (strcmp(s, "INACTIVE") == 0) return JL_WIFI_CTRL_STATE_INACTIVE; + return JL_WIFI_CTRL_STATE_UNKNOWN; +} + +static void jl_wifi_ctrl_parse_status(const char* reply, jl_wifi_ctrl_status_t* out) +{ + const char* val; + + if ((val = jl_wifi_ctrl_find_field(reply, "ssid=")) != NULL) { + char tmp[JL_WIFI_CTRL_SSID_RAW_MAX]; + jl_wifi_ctrl_copy_field(val, tmp, sizeof(tmp)); + jl_wifi_ssid_decode(out->ssid, sizeof(out->ssid), tmp); + } + + if ((val = jl_wifi_ctrl_find_field(reply, "id=")) != NULL) { + out->id_bss = (int)strtol(val, NULL, 10); + } + + if ((val = jl_wifi_ctrl_find_field(reply, "wpa_state=")) != NULL) { + char tmp[32]; + jl_wifi_ctrl_copy_field(val, tmp, sizeof(tmp)); + out->state = jl_wifi_ctrl_map_state(tmp); + } + + if ((val = jl_wifi_ctrl_find_field(reply, "ip_address=")) != NULL) { + jl_wifi_ctrl_copy_field(val, out->ip_address, sizeof(out->ip_address)); + } + + if ((val = jl_wifi_ctrl_find_field(reply, "address=")) != NULL) { + jl_wifi_ctrl_copy_field(val, out->mac_address, sizeof(out->mac_address)); + } +} + +static int jl_wifi_ctrl_parse_scan_results(char* reply, jl_wifi_ctrl_scan_result_t* out, int max) +{ + int count = 0; + bool header_skipped = false; + char* line_save = NULL; + char* line = strtok_r(reply, "\n", &line_save); + + while (line && count < max) { + if (!header_skipped) { + header_skipped = true; + line = strtok_r(NULL, "\n", &line_save); + continue; + } + + jl_wifi_ctrl_scan_result_t* ap = &out[count]; + memset(ap, 0, sizeof(*ap)); + + char* field_save = NULL; + char* field = strtok_r(line, "\t", &field_save); + int step = 0; + while (field) { + switch (step) { + case 0: + snprintf(ap->bssid, sizeof(ap->bssid), "%s", field); + break; + case 1: + ap->freq = atoi(field); + break; + case 2: + ap->level = strtol(field, NULL, 10); + break; + case 3: + ap->wpa2 = true; + break; + case 4: + jl_wifi_ssid_decode(ap->ssid, sizeof(ap->ssid), field); + break; + } + field = strtok_r(NULL, "\t", &field_save); + step++; + } + + if (step == 5) { + count++; + } + + line = strtok_r(NULL, "\n", &line_save); + } + + return count; +} + +/* Parses a completed async reply into the cache matching the request that + * produced it. Shared by jl_wifi_ctrl_poll() (normal, non-blocking path) and + * jl_wifi_ctrl_drain_pending() (the rare fallback used by synchronous action + * commands). */ +static void jl_wifi_ctrl_apply_reply(jl_wifi_ctrl_req_t req, char* reply, size_t reply_len) +{ + reply[reply_len] = '\0'; + + if (req == JL_WIFI_CTRL_REQ_STATUS) { + jl_wifi_ctrl_status_t st = {0}; + st.state = JL_WIFI_CTRL_STATE_UNKNOWN; + jl_wifi_ctrl_parse_status(reply, &st); + st.valid = true; + cached_status = st; + cached_status_valid = true; + } else if (req == JL_WIFI_CTRL_REQ_SCAN_RESULTS) { + cached_scan_count = jl_wifi_ctrl_parse_scan_results(reply, cached_scan, JL_WIFI_CTRL_SCAN_CACHE_MAX); + } +} + +/* Fires a STATUS/SCAN_RESULTS request without waiting for the reply. A + * no-op if one is already in flight - the caller just keeps seeing the last + * cached result until it resolves. */ +static void jl_wifi_ctrl_request(jl_wifi_ctrl_req_t req, const char* cmd) +{ + if (pending_req != JL_WIFI_CTRL_REQ_NONE) { + return; + } + + int fd = wpa_ctrl_get_fd(ctrl_cmd); + if (fd < 0) { + return; + } + + if (send(fd, cmd, strlen(cmd), 0) < 0) { + log_debug("jl_wifi_ctrl: send failed, reconnecting"); + jl_wifi_ctrl_teardown(); + return; + } + + pending_req = req; + pending_req_sent_ms = custom_tick_get(); +} + +static void jl_wifi_ctrl_handle_event(const char* msg) +{ + const char* p = msg; + if (*p == '<') { + const char* gt = strchr(p, '>'); + if (gt) { + p = gt + 1; + } + } + + if (strncmp(p, WPA_EVENT_SCAN_RESULTS, strlen(WPA_EVENT_SCAN_RESULTS)) == 0) { + pending_events |= JL_WIFI_EVT_SCAN_RESULTS; + } else if (strncmp(p, WPA_EVENT_CONNECTED, strlen(WPA_EVENT_CONNECTED)) == 0) { + pending_events |= JL_WIFI_EVT_CONNECTED; + } else if (strncmp(p, WPA_EVENT_DISCONNECTED, strlen(WPA_EVENT_DISCONNECTED)) == 0) { + pending_events |= JL_WIFI_EVT_DISCONNECTED; + } else if (strncmp(p, WPA_EVENT_TERMINATING, strlen(WPA_EVENT_TERMINATING)) == 0) { + pending_events |= JL_WIFI_EVT_TERMINATING; + } else if (strncmp(p, WPA_EVENT_TEMP_DISABLED, strlen(WPA_EVENT_TEMP_DISABLED)) == 0) { + pending_events |= JL_WIFI_EVT_AUTH_FAILED; + } +} + +void jl_wifi_ctrl_poll(void) +{ + if (!jl_wifi_ctrl_ensure_connected()) { + return; + } + + if (pending_req != JL_WIFI_CTRL_REQ_NONE) { + int cmd_fd = wpa_ctrl_get_fd(ctrl_cmd); + struct pollfd pfd_cmd = { .fd = cmd_fd, .events = POLLIN }; + if (poll(&pfd_cmd, 1, 0) > 0 && (pfd_cmd.revents & POLLIN)) { + static char reply[8192]; + size_t reply_len = sizeof(reply) - 1; + if (wpa_ctrl_recv(ctrl_cmd, reply, &reply_len) == 0) { + jl_wifi_ctrl_apply_reply(pending_req, reply, reply_len); + } + pending_req = JL_WIFI_CTRL_REQ_NONE; + } else if ((uint32_t)(custom_tick_get() - pending_req_sent_ms) > JL_WIFI_CTRL_REQ_TIMEOUT_MS) { + log_warn("jl_wifi_ctrl: async request timed out after %dms", JL_WIFI_CTRL_REQ_TIMEOUT_MS); + pending_req = JL_WIFI_CTRL_REQ_NONE; + } + } + + struct pollfd pfd_mon = { .fd = wpa_ctrl_get_fd(ctrl_mon), .events = POLLIN }; + while (poll(&pfd_mon, 1, 0) > 0 && (pfd_mon.revents & POLLIN)) { + static char buf[4096]; + size_t len = sizeof(buf) - 1; + + if (wpa_ctrl_recv(ctrl_mon, buf, &len) < 0) { + log_debug("jl_wifi_ctrl: monitor recv failed, reconnecting"); + jl_wifi_ctrl_teardown(); + return; + } + + buf[len] = '\0'; + jl_wifi_ctrl_handle_event(buf); + } +} + +uint32_t jl_wifi_ctrl_events_get_and_clear(void) +{ + uint32_t events = pending_events; + pending_events = 0; + return events; +} + +bool jl_wifi_ctrl_status_get(jl_wifi_ctrl_status_t* out) +{ + if (!jl_wifi_ctrl_ensure_connected()) { + memset(out, 0, sizeof(*out)); + out->state = JL_WIFI_CTRL_STATE_UNKNOWN; + return false; + } + + jl_wifi_ctrl_request(JL_WIFI_CTRL_REQ_STATUS, "STATUS"); + + if (!cached_status_valid) { + memset(out, 0, sizeof(*out)); + out->state = JL_WIFI_CTRL_STATE_UNKNOWN; + return false; + } + + *out = cached_status; + return true; +} + +bool jl_wifi_ctrl_enable_network(int id) +{ + char cmd[32]; + snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id); + return jl_wifi_ctrl_action_ok(cmd); +} + +bool jl_wifi_ctrl_disable_network(int id) +{ + char cmd[32]; + snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %d", id); + return jl_wifi_ctrl_action_ok(cmd); +} + +bool jl_wifi_ctrl_reconnect(void) +{ + return jl_wifi_ctrl_action_ok("RECONNECT"); +} + +bool jl_wifi_ctrl_disconnect(void) +{ + return jl_wifi_ctrl_action_ok("DISCONNECT"); +} + +static bool jl_wifi_ctrl_txn_reply(const char* cmd, char* reply, size_t reply_sz, int timeout_ms) +{ + if (!jl_wifi_ctrl_ensure_connected()) { + return false; + } + + jl_wifi_ctrl_drain_pending(timeout_ms); + + size_t reply_len = reply_sz - 1; + if (!jl_wifi_ctrl_txn_timeout(ctrl_cmd, cmd, reply, &reply_len, timeout_ms)) { + log_warn("jl_wifi_ctrl: '%s' timed out after %dms", cmd, timeout_ms); + return false; + } + reply[reply_len] = '\0'; + return true; +} + +int jl_wifi_ctrl_add_network(void) +{ + char reply[32]; + if (!jl_wifi_ctrl_txn_reply("ADD_NETWORK", reply, sizeof(reply), + JL_WIFI_CTRL_ACTION_TIMEOUT_MS)) { + return -1; + } + if (strncmp(reply, "FAIL", 4) == 0) { + log_warn("jl_wifi_ctrl: ADD_NETWORK -> '%s'", reply); + return -1; + } + int id = (int)strtol(reply, NULL, 10); + log_info("jl_wifi_ctrl: ADD_NETWORK -> %d", id); + return id; +} + +static bool jl_wifi_ctrl_list_has_id0(void) +{ + char reply[512]; + if (!jl_wifi_ctrl_txn_reply("LIST_NETWORKS", reply, sizeof(reply), + JL_WIFI_CTRL_ACTION_TIMEOUT_MS)) { + return false; + } + + char* line = strchr(reply, '\n'); + if (!line) { + return false; + } + for (line++; line && *line; ) { + if (line[0] == '0' && (line[1] == '\t' || line[1] == ' ' || + line[1] == '\n' || line[1] == '\0')) { + return true; + } + char* nl = strchr(line, '\n'); + line = nl ? nl + 1 : NULL; + } + return false; +} + +bool jl_wifi_ctrl_ensure_network0(void) +{ + if (jl_wifi_ctrl_list_has_id0()) { + return true; + } + if (jl_wifi_ctrl_add_network() != 0) { + return false; + } + jl_wifi_ctrl_save_config(); + return true; +} + +bool jl_wifi_ctrl_set_network_ssid_hex(int id, const char* ssid_hex) +{ + char cmd[JL_WIFI_CTRL_SSID_RAW_MAX + 32]; + int n = snprintf(cmd, sizeof(cmd), "SET_NETWORK %d ssid %s", id, ssid_hex); + if (n < 0 || (size_t)n >= sizeof(cmd)) { + return false; + } + if (jl_wifi_ctrl_action_ok(cmd)) { + return true; + } + if (id != 0) { + return false; + } + /* Empty factory network={} can occupy id 0 and still reject SET. */ + jl_wifi_ctrl_action_ok("REMOVE_NETWORK 0"); + if (jl_wifi_ctrl_add_network() != 0) { + return false; + } + return jl_wifi_ctrl_action_ok(cmd); +} + +bool jl_wifi_ctrl_set_network_psk(int id, const char* psk_plaintext) +{ + char cmd[128]; + int n = snprintf(cmd, sizeof(cmd), "SET_NETWORK %d psk \"%s\"", id, psk_plaintext); + if (n < 0 || (size_t)n >= sizeof(cmd)) { + return false; + } + return jl_wifi_ctrl_action_ok(cmd); +} + +bool jl_wifi_ctrl_set_network_scan_ssid(int id, bool enable) +{ + char cmd[32]; + snprintf(cmd, sizeof(cmd), "SET_NETWORK %d scan_ssid %d", id, enable ? 1 : 0); + return jl_wifi_ctrl_action_ok(cmd); +} + +bool jl_wifi_ctrl_save_config(void) +{ + return jl_wifi_ctrl_action_ok("SAVE_CONFIG"); +} + +bool jl_wifi_ctrl_scan(void) +{ + return jl_wifi_ctrl_cmd_ok("SCAN"); +} + +int jl_wifi_ctrl_scan_results_get(jl_wifi_ctrl_scan_result_t* out, int max) +{ + if (!jl_wifi_ctrl_ensure_connected()) { + return 0; + } + + jl_wifi_ctrl_request(JL_WIFI_CTRL_REQ_SCAN_RESULTS, "SCAN_RESULTS"); + + int n = cached_scan_count < max ? cached_scan_count : max; + if (n > 0) { + memcpy(out, cached_scan, n * sizeof(*out)); + } + return n; +} diff --git a/jl_wifi_ctrl.h b/jl_wifi_ctrl.h new file mode 100644 index 0000000..531c113 --- /dev/null +++ b/jl_wifi_ctrl.h @@ -0,0 +1,86 @@ +#ifndef JL_WIFI_CTRL_H +#define JL_WIFI_CTRL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "wpa_ctrl.h" +#include "includes.h" +#include "common.h" + +#define JL_WIFI_CTRL_SSID_MAX 33 + +typedef enum { + JL_WIFI_CTRL_STATE_UNKNOWN = -1, + JL_WIFI_CTRL_STATE_COMPLETED = 0, + JL_WIFI_CTRL_STATE_DISCONNECTED = 1, + JL_WIFI_CTRL_STATE_SCANNING = 2, + JL_WIFI_CTRL_STATE_ASSOCIATED = 3, + JL_WIFI_CTRL_STATE_4WAY_HANDSHAKE = 4, + JL_WIFI_CTRL_STATE_INACTIVE = 5, +} jl_wifi_ctrl_state_t; + +typedef struct { + bool valid; /* false = wpa_supplicant unreachable */ + jl_wifi_ctrl_state_t state; + char ssid[JL_WIFI_CTRL_SSID_MAX]; + int id_bss; + char ip_address[64]; + char mac_address[32]; +} jl_wifi_ctrl_status_t; + +typedef struct { + char bssid[18]; + int freq; + int level; + bool wpa2; + char ssid[JL_WIFI_CTRL_SSID_MAX]; +} jl_wifi_ctrl_scan_result_t; + +typedef enum { + JL_WIFI_EVT_NONE = 0, + JL_WIFI_EVT_SCAN_RESULTS = 1u << 0, + JL_WIFI_EVT_CONNECTED = 1u << 1, + JL_WIFI_EVT_DISCONNECTED = 1u << 2, + JL_WIFI_EVT_TERMINATING = 1u << 3, + /* wpa_supplicant gave up on the configured network after repeated + * 4-way-handshake failures (CTRL-EVENT-SSID-TEMP-DISABLED) - the + * reliable "wrong PSK" signal. Do NOT infer this from momentarily + * observing wpa_state=4WAY_HANDSHAKE via STATUS polling: that state is + * a normal transient step of every successful connection too. */ + JL_WIFI_EVT_AUTH_FAILED = 1u << 4, +} jl_wifi_ctrl_event_t; + +/* Call periodically (e.g. every 100ms lv_task tick). Non-blocking: manages + * connect/reconnect (internally rate-limited) and drains the attach/monitor + * socket. */ +void jl_wifi_ctrl_poll(void); + +/* Event bits accumulated since the last call; clears them. */ +uint32_t jl_wifi_ctrl_events_get_and_clear(void); + +bool jl_wifi_ctrl_status_get(jl_wifi_ctrl_status_t* out); +bool jl_wifi_ctrl_enable_network(int id); +bool jl_wifi_ctrl_disable_network(int id); +bool jl_wifi_ctrl_reconnect(void); +bool jl_wifi_ctrl_disconnect(void); +/* ADD_NETWORK if LIST_NETWORKS has no id 0 (factory empty network={} + * is often ignored by wpa, so SET_NETWORK 0 FAIL). */ +bool jl_wifi_ctrl_ensure_network0(void); +int jl_wifi_ctrl_add_network(void); +bool jl_wifi_ctrl_set_network_ssid_hex(int id, const char* ssid_hex); +bool jl_wifi_ctrl_set_network_psk(int id, const char* psk_plaintext); +/* scan_ssid=1 makes wpa_supplicant actively probe for this SSID by name + * during scans instead of only listening for beacons - required to find + * and connect to networks that don't broadcast their SSID. */ +bool jl_wifi_ctrl_set_network_scan_ssid(int id, bool enable); +bool jl_wifi_ctrl_save_config(void); +bool jl_wifi_ctrl_scan(void); +int jl_wifi_ctrl_scan_results_get(jl_wifi_ctrl_scan_result_t* out, int max); + +#ifdef __cplusplus +} +#endif + +#endif //JL_WIFI_CTRL_H diff --git a/jl_wpa_os_shim.c b/jl_wpa_os_shim.c new file mode 100644 index 0000000..8dd67a7 --- /dev/null +++ b/jl_wpa_os_shim.c @@ -0,0 +1,48 @@ +#include "includes.h" +#include "common.h" +#include + +void *os_zalloc(size_t size) +{ + return calloc(1, size); +} + +size_t os_strlcpy(char *dest, const char *src, size_t siz) +{ + const char *s = src; + size_t left = siz; + + if (left) { + while (--left != 0) { + if ((*dest++ = *s++) == '\0') + break; + } + } + + if (left == 0) { + if (siz != 0) + *dest = '\0'; + while (*s++) + ; + } + + return s - src - 1; +} + +int os_get_reltime(struct os_reltime *t) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) + return -1; + + t->sec = ts.tv_sec; + t->usec = ts.tv_nsec / 1000; + return 0; +} + +void os_sleep(os_time_t sec, os_time_t usec) +{ + const struct timespec req = { sec, usec * 1000 }; + nanosleep(&req, NULL); +} diff --git a/wpa_ctrl.c b/wpa_ctrl.c new file mode 100644 index 0000000..f4020a8 --- /dev/null +++ b/wpa_ctrl.c @@ -0,0 +1,776 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2007, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "jl_wifi_ctrl.h" + +#ifdef CONFIG_CTRL_IFACE + +#ifdef CONFIG_CTRL_IFACE_UNIX +#include +#include +#include +#include +#include +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE +#include +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef ANDROID +#include +#include +#include +#include "private/android_filesystem_config.h" +#endif /* ANDROID */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 +#include +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + +#include "wpa_ctrl.h" +#include "common.h" + + +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) +#define CTRL_IFACE_SOCKET +#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */ + + +/** + * struct wpa_ctrl - Internal structure for control interface library + * + * This structure is used by the wpa_supplicant/hostapd control interface + * library to store internal data. Programs using the library should not touch + * this data directly. They can only use the pointer to the data structure as + * an identifier for the control interface connection and use this as one of + * the arguments for most of the control interface library functions. + */ +struct wpa_ctrl { +#ifdef CONFIG_CTRL_IFACE_UDP + int s; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 local; + struct sockaddr_in6 dest; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + struct sockaddr_in local; + struct sockaddr_in dest; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + char *cookie; + char *remote_ifname; + char *remote_ip; +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + int s; + struct sockaddr_un local; + struct sockaddr_un dest; +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + HANDLE pipe; +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ +}; + + +#ifdef CONFIG_CTRL_IFACE_UNIX + +#ifndef CONFIG_CTRL_IFACE_CLIENT_DIR +#define CONFIG_CTRL_IFACE_CLIENT_DIR "/tmp" +#endif /* CONFIG_CTRL_IFACE_CLIENT_DIR */ +#ifndef CONFIG_CTRL_IFACE_CLIENT_PREFIX +#define CONFIG_CTRL_IFACE_CLIENT_PREFIX "wpa_ctrl_" +#endif /* CONFIG_CTRL_IFACE_CLIENT_PREFIX */ + + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + return wpa_ctrl_open2(ctrl_path, NULL); +} + + +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, + const char *cli_path) +{ + struct wpa_ctrl *ctrl; + static int counter = 0; + int ret; + size_t res; + int tries = 0; + int flags; + + if (ctrl_path == NULL) + return NULL; + + ctrl = os_zalloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + + ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + os_free(ctrl); + return NULL; + } + + ctrl->local.sun_family = AF_UNIX; + counter++; +try_again: + if (cli_path && cli_path[0] == '/') { + ret = os_snprintf(ctrl->local.sun_path, + sizeof(ctrl->local.sun_path), + "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + cli_path, (int) getpid(), counter); + } else { + ret = os_snprintf(ctrl->local.sun_path, + sizeof(ctrl->local.sun_path), + CONFIG_CTRL_IFACE_CLIENT_DIR "/" + CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d", + (int) getpid(), counter); + } + if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } + tries++; +#ifdef ANDROID + /* Set client socket file permissions so that bind() creates the client + * socket with these permissions and there is no need to try to change + * them with chmod() after bind() which would have potential issues with + * race conditions. These permissions are needed to make sure the server + * side (wpa_supplicant or hostapd) can reply to the control interface + * messages. + * + * The lchown() calls below after bind() are also part of the needed + * operations to allow the response to go through. Those are using the + * no-deference-symlinks version to avoid races. */ + fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +#endif /* ANDROID */ + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + if (errno == EADDRINUSE && tries < 2) { + /* + * getpid() returns unique identifier for this instance + * of wpa_ctrl, so the existing socket file must have + * been left by unclean termination of an earlier run. + * Remove the file and try again. + */ + unlink(ctrl->local.sun_path); + goto try_again; + } + close(ctrl->s); + os_free(ctrl); + return NULL; + } + +#ifdef ANDROID + /* Set group even if we do not have privileges to change owner */ + lchown(ctrl->local.sun_path, -1, AID_WIFI); + lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI); + + if (os_strncmp(ctrl_path, "@android:", 9) == 0) { + if (socket_local_client_connect( + ctrl->s, ctrl_path + 9, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + return ctrl; + } + + /* + * If the ctrl_path isn't an absolute pathname, assume that + * it's the name of a socket in the Android reserved namespace. + * Otherwise, it's a normal UNIX domain socket appearing in the + * filesystem. + */ + if (*ctrl_path != '/') { + char buf[21]; + os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path); + if (socket_local_client_connect( + ctrl->s, buf, + ANDROID_SOCKET_NAMESPACE_RESERVED, + SOCK_DGRAM) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + return ctrl; + } +#endif /* ANDROID */ + + ctrl->dest.sun_family = AF_UNIX; + if (os_strncmp(ctrl_path, "@abstract:", 10) == 0) { + ctrl->dest.sun_path[0] = '\0'; + os_strlcpy(ctrl->dest.sun_path + 1, ctrl_path + 10, + sizeof(ctrl->dest.sun_path) - 1); + } else { + res = os_strlcpy(ctrl->dest.sun_path, ctrl_path, + sizeof(ctrl->dest.sun_path)); + if (res >= sizeof(ctrl->dest.sun_path)) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } + } + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + os_free(ctrl); + return NULL; + } + + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(ctrl->s, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(ctrl->s, F_SETFL, flags) < 0) { + perror("fcntl(ctrl->s, O_NONBLOCK)"); + /* Not fatal, continue on.*/ + } + } + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ + if (ctrl == NULL) + return; + unlink(ctrl->local.sun_path); + if (ctrl->s >= 0) + close(ctrl->s); + os_free(ctrl); +} + + +#ifdef ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void) +{ + DIR *dir; + struct dirent *result; + size_t dirnamelen; + size_t maxcopy; + char pathname[PATH_MAX]; + char *namep; + + if ((dir = opendir(CONFIG_CTRL_IFACE_CLIENT_DIR)) == NULL) + return; + + dirnamelen = (size_t) os_snprintf(pathname, sizeof(pathname), "%s/", + CONFIG_CTRL_IFACE_CLIENT_DIR); + if (dirnamelen >= sizeof(pathname)) { + closedir(dir); + return; + } + namep = pathname + dirnamelen; + maxcopy = PATH_MAX - dirnamelen; + while ((result = readdir(dir)) != NULL) { + if (os_strlcpy(namep, result->d_name, maxcopy) < maxcopy) + unlink(pathname); + } + closedir(dir); +} +#endif /* ANDROID */ + +#else /* CONFIG_CTRL_IFACE_UNIX */ + +#ifdef ANDROID +void wpa_ctrl_cleanup(void) +{ +} +#endif /* ANDROID */ + +#endif /* CONFIG_CTRL_IFACE_UNIX */ + + +#ifdef CONFIG_CTRL_IFACE_UDP + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; + char buf[128]; + size_t len; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + struct hostent *h; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + + ctrl = os_zalloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + if (ctrl->s < 0) { + perror("socket"); + os_free(ctrl); + return NULL; + } + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + ctrl->local.sin6_family = AF_INET6; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ctrl->local.sin6_addr = in6addr_any; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + inet_pton(AF_INET6, "::1", &ctrl->local.sin6_addr); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + ctrl->local.sin_family = AF_INET; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + ctrl->local.sin_addr.s_addr = INADDR_ANY; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + ctrl->dest.sin6_family = AF_INET6; + inet_pton(AF_INET6, "::1", &ctrl->dest.sin6_addr); + ctrl->dest.sin6_port = htons(WPA_CTRL_IFACE_PORT); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + ctrl->dest.sin_family = AF_INET; + ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + if (ctrl_path) { + char *port, *name; + int port_id; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + char *scope; + int scope_id = 0; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + + name = os_strdup(ctrl_path); + if (name == NULL) { + close(ctrl->s); + os_free(ctrl); + return NULL; + } +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + port = os_strchr(name, ','); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + port = os_strchr(name, ':'); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + + if (port) { + port_id = atoi(&port[1]); + port[0] = '\0'; + } else + port_id = WPA_CTRL_IFACE_PORT; + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + scope = os_strchr(name, '%'); + if (scope) { + scope_id = if_nametoindex(&scope[1]); + scope[0] = '\0'; + } + h = gethostbyname2(name, AF_INET6); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + h = gethostbyname(name); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + ctrl->remote_ip = os_strdup(name); + os_free(name); + if (h == NULL) { + perror("gethostbyname"); + close(ctrl->s); + os_free(ctrl->remote_ip); + os_free(ctrl); + return NULL; + } +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + ctrl->dest.sin6_scope_id = scope_id; + ctrl->dest.sin6_port = htons(port_id); + os_memcpy(&ctrl->dest.sin6_addr, h->h_addr, h->h_length); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + ctrl->dest.sin_port = htons(port_id); + os_memcpy(&ctrl->dest.sin_addr.s_addr, h->h_addr, h->h_length); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + } else + ctrl->remote_ip = os_strdup("localhost"); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + char addr[INET6_ADDRSTRLEN]; + wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s", + inet_ntop(AF_INET6, &ctrl->dest.sin6_addr, addr, + sizeof(ctrl->dest)), + ntohs(ctrl->dest.sin6_port), + strerror(errno)); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s", + inet_ntoa(ctrl->dest.sin_addr), + ntohs(ctrl->dest.sin_port), + strerror(errno)); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + close(ctrl->s); + os_free(ctrl->remote_ip); + os_free(ctrl); + return NULL; + } + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) { + buf[len] = '\0'; + ctrl->cookie = os_strdup(buf); + } + + if (wpa_ctrl_request(ctrl, "IFNAME", 6, buf, &len, NULL) == 0) { + buf[len] = '\0'; + ctrl->remote_ifname = os_strdup(buf); + } + + return ctrl; +} + + +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl) +{ +#define WPA_CTRL_MAX_PS_NAME 100 + static char ps[WPA_CTRL_MAX_PS_NAME] = {}; + os_snprintf(ps, WPA_CTRL_MAX_PS_NAME, "%s/%s", + ctrl->remote_ip, ctrl->remote_ifname); + return ps; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ + close(ctrl->s); + os_free(ctrl->cookie); + os_free(ctrl->remote_ifname); + os_free(ctrl->remote_ip); + os_free(ctrl); +} + +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +#ifdef CTRL_IFACE_SOCKET +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + struct timeval tv; + struct os_reltime started_at, ending_at; + int res; + fd_set rfds; + const char *_cmd; + char *cmd_buf = NULL; + size_t _cmd_len; + +#ifdef CONFIG_CTRL_IFACE_UDP + if (ctrl->cookie) { + char *pos; + _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len; + cmd_buf = os_malloc(_cmd_len); + if (cmd_buf == NULL) + return -1; + _cmd = cmd_buf; + pos = cmd_buf; + os_strlcpy(pos, ctrl->cookie, _cmd_len); + pos += os_strlen(ctrl->cookie); + *pos++ = ' '; + os_memcpy(pos, cmd, cmd_len); + } else +#endif /* CONFIG_CTRL_IFACE_UDP */ + { + _cmd = cmd; + _cmd_len = cmd_len; + } + + errno = 0; + started_at.sec = 0; + started_at.usec = 0; +retry_send: + if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) { + if (errno == EAGAIN || errno == EBUSY || errno == EWOULDBLOCK) + { + /* + * Must be a non-blocking socket... Try for a bit + * longer before giving up. + */ + if (started_at.sec == 0) + os_get_reltime(&started_at); + else { + struct os_reltime n; + os_get_reltime(&n); + /* Try for a few seconds. */ + if (os_reltime_expired(&n, &started_at, 5)) + goto send_err; + } + os_sleep(1, 0); + goto retry_send; + } + send_err: + os_free(cmd_buf); + return -1; + } + os_free(cmd_buf); + + os_get_reltime(&ending_at); + ending_at.sec += 10; + + for (;;) { + struct os_reltime diff; + + os_get_reltime(&started_at); + if (os_reltime_before(&ending_at, &started_at)) + return -2; + os_reltime_sub(&ending_at, &started_at, &diff); + tv.tv_sec = diff.sec; + tv.tv_usec = diff.usec; + + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno == EINTR) + continue; + if (res < 0) + return res; + if (FD_ISSET(ctrl->s, &rfds)) { + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + if ((res > 0 && reply[0] == '<') || + (res > 6 && strncmp(reply, "IFNAME=", 7) == 0)) { + /* This is an unsolicited message from + * wpa_supplicant, not the reply to the + * request. Use msg_cb to report this to the + * caller. */ + if (msg_cb) { + /* Make sure the message is nul + * terminated. */ + if ((size_t) res == *reply_len) + res = (*reply_len) - 1; + reply[res] = '\0'; + msg_cb(reply, res); + } + continue; + } + *reply_len = res; + break; + } else { + return -2; + } + } + return 0; +} +#endif /* CTRL_IFACE_SOCKET */ + + +static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) +{ + char buf[10]; + int ret; + size_t len = 10; + + ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, + buf, &len, NULL); + if (ret < 0) + return ret; + if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0) + return 0; + return -1; +} + + +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 1); +} + + +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 0); +} + + +#ifdef CTRL_IFACE_SOCKET + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + int res; + + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + *reply_len = res; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + struct timeval tv; + fd_set rfds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + return FD_ISSET(ctrl->s, &rfds); +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return ctrl->s; +} + +#endif /* CTRL_IFACE_SOCKET */ + + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + +#ifndef WPA_SUPPLICANT_NAMED_PIPE +#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" +#endif +#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; + DWORD mode; + TCHAR name[256]; + int i, ret; + + ctrl = os_malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + os_memset(ctrl, 0, sizeof(*ctrl)); + +#ifdef UNICODE + if (ctrl_path == NULL) + ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX); + else + ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), + ctrl_path); +#else /* UNICODE */ + if (ctrl_path == NULL) + ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX); + else + ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", + ctrl_path); +#endif /* UNICODE */ + if (os_snprintf_error(256, ret)) { + os_free(ctrl); + return NULL; + } + + for (i = 0; i < 10; i++) { + ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, 0, NULL); + /* + * Current named pipe server side in wpa_supplicant is + * re-opening the pipe for new clients only after the previous + * one is taken into use. This leaves a small window for race + * conditions when two connections are being opened at almost + * the same time. Retry if that was the case. + */ + if (ctrl->pipe != INVALID_HANDLE_VALUE || + GetLastError() != ERROR_PIPE_BUSY) + break; + WaitNamedPipe(name, 1000); + } + if (ctrl->pipe == INVALID_HANDLE_VALUE) { + os_free(ctrl); + return NULL; + } + + mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) { + CloseHandle(ctrl->pipe); + os_free(ctrl); + return NULL; + } + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ + CloseHandle(ctrl->pipe); + os_free(ctrl); +} + + +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + DWORD written; + DWORD readlen = *reply_len; + + if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL)) + return -1; + + if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL)) + return -1; + *reply_len = readlen; + + return 0; +} + + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + DWORD len = *reply_len; + if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL)) + return -1; + *reply_len = len; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + DWORD left; + + if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL)) + return -1; + return left ? 1 : 0; +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return -1; +} + +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + +#endif /* CONFIG_CTRL_IFACE */ diff --git a/wpa_ctrl.h b/wpa_ctrl.h new file mode 100644 index 0000000..5366177 --- /dev/null +++ b/wpa_ctrl.h @@ -0,0 +1,679 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_CTRL_H +#define WPA_CTRL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "includes.h" + +/* wpa_supplicant control interface - fixed message prefixes */ + +/** Interactive request for identity/password/pin */ +#define WPA_CTRL_REQ "CTRL-REQ-" + +/** Response to identity/password/pin request */ +#define WPA_CTRL_RSP "CTRL-RSP-" + +/* Event messages with fixed prefix */ +/** Authentication completed successfully and data connection enabled */ +#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " +/** Disconnected, data connection is not available */ +#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** Association rejected during connection attempt */ +#define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT " +/** Authentication rejected during connection attempt */ +#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT " +/** wpa_supplicant is exiting */ +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " +/** Password change was completed successfully */ +#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " +/** EAP-Request/Notification received */ +#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " +/** EAP authentication started (EAP-Request/Identity received) */ +#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " +/** EAP method proposed by the server */ +#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD " +/** EAP method selected */ +#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " +/** EAP peer certificate from TLS */ +#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT " +/** EAP peer certificate alternative subject name component from TLS */ +#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT " +/** EAP TLS certificate chain validation error */ +#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR " +/** EAP status */ +#define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS " +/** Retransmit the previous request packet */ +#define WPA_EVENT_EAP_RETRANSMIT "CTRL-EVENT-EAP-RETRANSMIT " +#define WPA_EVENT_EAP_RETRANSMIT2 "CTRL-EVENT-EAP-RETRANSMIT2 " +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +#define WPA_EVENT_EAP_SUCCESS2 "CTRL-EVENT-EAP-SUCCESS2 " +/** EAP authentication failed (EAP-Failure received) */ +#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +#define WPA_EVENT_EAP_FAILURE2 "CTRL-EVENT-EAP-FAILURE2 " +/** EAP authentication failed due to no response received */ +#define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE " +#define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 " +#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE " +/** Network block temporarily disabled (e.g., due to authentication failure) */ +#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " +/** Temporarily disabled network block re-enabled */ +#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED " +/** New scan started */ +#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED " +/** New scan results available */ +#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " +/** Scan command failed */ +#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED " +/** wpa_supplicant state change */ +#define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE " +/** A new BSS entry was added (followed by BSS entry id and BSSID) */ +#define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED " +/** A BSS entry was removed (followed by BSS entry id and BSSID) */ +#define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED " +/** No suitable network was found */ +#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND " +/** Change in the signal level was reported by the driver */ +#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE " +/** Beacon loss reported by the driver */ +#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS " +/** Regulatory domain channel */ +#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE " +/** Regulatory beacon hint */ +#define WPA_EVENT_REGDOM_BEACON_HINT "CTRL-EVENT-REGDOM-BEACON-HINT " +/** Channel switch started (followed by freq= and other channel parameters) + */ +#define WPA_EVENT_CHANNEL_SWITCH_STARTED "CTRL-EVENT-STARTED-CHANNEL-SWITCH " +/** Channel switch (followed by freq= and other channel parameters) */ +#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH " +/** MLO link channel switch started (followed by freq= and other channel + * parameters) + */ +#define WPA_EVENT_LINK_CHANNEL_SWITCH_STARTED \ + "CTRL-EVENT-STARTED-LINK-CHANNEL-SWITCH " +/** MLO link channel switch (followed by freq= and other channel + * parameters) + */ +#define WPA_EVENT_LINK_CHANNEL_SWITCH "CTRL-EVENT-LINK-CHANNEL-SWITCH " +/** SAE authentication failed due to unknown password identifier */ +#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \ + "CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER " +/** Unprotected Beacon frame dropped */ +#define WPA_EVENT_UNPROT_BEACON "CTRL-EVENT-UNPROT-BEACON " +/** Decision made to do a within-ESS roam */ +#define WPA_EVENT_DO_ROAM "CTRL-EVENT-DO-ROAM " +/** Decision made to skip a within-ESS roam */ +#define WPA_EVENT_SKIP_ROAM "CTRL-EVENT-SKIP-ROAM " +/** TID-to-link mapping response event */ +#define WPA_EVENT_T2LM_UPDATE "CTRL-EVENT-T2LM-UPDATE " +/** MLO link reconfiguration event */ +#define WPA_EVENT_LINK_RECONFIG "CTRL-EVENT-LINK-RECONFIG " + +/** IP subnet status change notification + * + * When using an offloaded roaming mechanism where driver/firmware takes care + * of roaming and IP subnet validation checks post-roaming, this event can + * indicate whether IP subnet has changed. + * + * The event has a status=<0/1/2> parameter where + * 0 = unknown + * 1 = IP subnet unchanged (can continue to use the old IP address) + * 2 = IP subnet changed (need to get a new IP address) + */ +#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE " + +/** RSN IBSS 4-way handshakes completed with specified peer */ +#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED " + +/** Notification of frequency conflict due to a concurrent operation. + * + * The indicated network is disabled and needs to be re-enabled before it can + * be used again. + */ +#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT " +/** Frequency ranges that the driver recommends to avoid */ +#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ " +/** A new network profile was added (followed by network entry id) */ +#define WPA_EVENT_NETWORK_ADDED "CTRL-EVENT-NETWORK-ADDED " +/** A network profile was removed (followed by prior network entry id) */ +#define WPA_EVENT_NETWORK_REMOVED "CTRL-EVENT-NETWORK-REMOVED " +/** Result of MSCS setup */ +#define WPA_EVENT_MSCS_RESULT "CTRL-EVENT-MSCS-RESULT " +/** WPS overlap detected in PBC mode */ +#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " +/** Available WPS AP with active PBC found in scan results */ +#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC " +/** Available WPS AP with our address as authorized in scan results */ +#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH " +/** Available WPS AP with recently selected PIN registrar found in scan results + */ +#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN " +/** Available WPS AP found in scan results */ +#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE " +/** A new credential received */ +#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED " +/** M2D received */ +#define WPS_EVENT_M2D "WPS-M2D " +/** WPS registration failed after M2/M2D */ +#define WPS_EVENT_FAIL "WPS-FAIL " +/** WPS registration completed successfully */ +#define WPS_EVENT_SUCCESS "WPS-SUCCESS " +/** WPS enrollment attempt timed out and was terminated */ +#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT " +/* PBC mode was activated */ +#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE " +/* PBC mode was disabled */ +#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE " + +#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " + +#define WPS_EVENT_OPEN_NETWORK "WPS-OPEN-NETWORK " +/** Result of SCS setup */ +#define WPA_EVENT_SCS_RESULT "CTRL-EVENT-SCS-RESULT " +/* Event indicating DSCP policy */ +#define WPA_EVENT_DSCP_POLICY "CTRL-EVENT-DSCP-POLICY " + +/* WPS ER events */ +#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD " +#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE " +#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD " +#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE " +#define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " +#define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " + +/* DPP events */ +#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS " +#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED " +#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE " +#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING " +#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE " +#define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION " +#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED " +#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT " +#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED " +#define DPP_EVENT_CONN_STATUS_RESULT "DPP-CONN-STATUS-RESULT " +#define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM " +#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID " +#define DPP_EVENT_CONFOBJ_SSID_CHARSET "DPP-CONFOBJ-SSID-CHARSET " +#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS " +#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK " +#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR " +#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY " +#define DPP_EVENT_PP_KEY "DPP-PP-KEY " +#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY " +#define DPP_EVENT_SERVER_NAME "DPP-SERVER-NAME " +#define DPP_EVENT_CERTBAG "DPP-CERTBAG " +#define DPP_EVENT_CACERT "DPP-CACERT " +#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR " +#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID " +#define DPP_EVENT_CONFIGURATOR_ID "DPP-CONFIGURATOR-ID " +#define DPP_EVENT_RX "DPP-RX " +#define DPP_EVENT_TX "DPP-TX " +#define DPP_EVENT_TX_STATUS "DPP-TX-STATUS " +#define DPP_EVENT_FAIL "DPP-FAIL " +#define DPP_EVENT_PKEX_T_LIMIT "DPP-PKEX-T-LIMIT " +#define DPP_EVENT_INTRO "DPP-INTRO " +#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX " +#define DPP_EVENT_CHIRP_STOPPED "DPP-CHIRP-STOPPED " +#define DPP_EVENT_MUD_URL "DPP-MUD-URL " +#define DPP_EVENT_BAND_SUPPORT "DPP-BAND-SUPPORT " +#define DPP_EVENT_CSR "DPP-CSR " +#define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX " +#define DPP_EVENT_CONF_NEEDED "DPP-CONF-NEEDED " +#define DPP_EVENT_PB_STATUS "DPP-PB-STATUS " +#define DPP_EVENT_PB_RESULT "DPP-PB-RESULT " +#define DPP_EVENT_RELAY_NEEDS_CONTROLLER "DPP-RELAY-NEEDS-CONTROLLER " + +/* Wi-Fi Aware (NAN USD) events */ +#define NAN_DISCOVERY_RESULT "NAN-DISCOVERY-RESULT " +#define NAN_REPLIED "NAN-REPLIED " +#define NAN_PUBLISH_TERMINATED "NAN-PUBLISH-TERMINATED " +#define NAN_SUBSCRIBE_TERMINATED "NAN-SUBSCRIBE-TERMINATED " +#define NAN_RECEIVE "NAN-RECEIVE " + +/* MESH events */ +#define MESH_GROUP_STARTED "MESH-GROUP-STARTED " +#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED " +#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED " +#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED " +/** Mesh SAE authentication failure. Wrong password suspected. */ +#define MESH_SAE_AUTH_FAILURE "MESH-SAE-AUTH-FAILURE " +#define MESH_SAE_AUTH_BLOCKED "MESH-SAE-AUTH-BLOCKED " + +/* WMM AC events */ +#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED " +#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED " +#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED " + +/** P2P device found */ +#define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND " + +/** P2P device lost */ +#define P2P_EVENT_DEVICE_LOST "P2P-DEVICE-LOST " + +/** A P2P device requested GO negotiation, but we were not ready to start the + * negotiation */ +#define P2P_EVENT_GO_NEG_REQUEST "P2P-GO-NEG-REQUEST " +#define P2P_EVENT_GO_NEG_SUCCESS "P2P-GO-NEG-SUCCESS " +#define P2P_EVENT_GO_NEG_FAILURE "P2P-GO-NEG-FAILURE " +#define P2P_EVENT_GROUP_FORMATION_SUCCESS "P2P-GROUP-FORMATION-SUCCESS " +#define P2P_EVENT_GROUP_FORMATION_FAILURE "P2P-GROUP-FORMATION-FAILURE " +#define P2P_EVENT_GROUP_STARTED "P2P-GROUP-STARTED " +#define P2P_EVENT_GROUP_REMOVED "P2P-GROUP-REMOVED " +#define P2P_EVENT_CROSS_CONNECT_ENABLE "P2P-CROSS-CONNECT-ENABLE " +#define P2P_EVENT_CROSS_CONNECT_DISABLE "P2P-CROSS-CONNECT-DISABLE " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_SHOW_PIN "P2P-PROV-DISC-SHOW-PIN " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_ENTER_PIN "P2P-PROV-DISC-ENTER-PIN " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_PBC_REQ "P2P-PROV-DISC-PBC-REQ " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_PBC_RESP "P2P-PROV-DISC-PBC-RESP " +/* parameters: */ +#define P2P_EVENT_PROV_DISC_FAILURE "P2P-PROV-DISC-FAILURE" +/* parameters: */ +#define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ " +/* parameters: */ +#define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP " +#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP " +#define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED " +#define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT " +#define P2P_EVENT_INVITATION_ACCEPTED "P2P-INVITATION-ACCEPTED " +#define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED " +#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id=" +#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE " +#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO " +#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT " +#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT " +#define P2P_EVENT_FALLBACK_TO_GO_NEG "P2P-FALLBACK-TO-GO-NEG " +#define P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED "P2P-FALLBACK-TO-GO-NEG-ENABLED " + +/* parameters: */ +#define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT " +#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP " + +#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START " +#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE " + +#define INTERWORKING_AP "INTERWORKING-AP " +#define INTERWORKING_EXCLUDED "INTERWORKING-BLACKLISTED " +#define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH " +#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED " +#define INTERWORKING_SELECTED "INTERWORKING-SELECTED " + +/* Credential block added; parameters: */ +#define CRED_ADDED "CRED-ADDED " +/* Credential block modified; parameters: */ +#define CRED_MODIFIED "CRED-MODIFIED " +/* Credential block removed; parameters: */ +#define CRED_REMOVED "CRED-REMOVED " + +#define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO " +/* parameters: */ +#define GAS_QUERY_START "GAS-QUERY-START " +/* parameters: */ +#define GAS_QUERY_DONE "GAS-QUERY-DONE " + +/* parameters: */ +#define ANQP_QUERY_DONE "ANQP-QUERY-DONE " + +#define RX_ANQP "RX-ANQP " +#define RX_HS20_ANQP "RX-HS20-ANQP " +#define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON " +#define RX_HS20_ICON "RX-HS20-ICON " +#define RX_MBO_ANQP "RX-MBO-ANQP " + +/* parameters: */ +#define RX_VENUE_URL "RX-VENUE-URL " + +#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION " +#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE " +#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE " + +#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " +#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " + +#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED " +#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED " + +/* hostapd control interface - fixed message prefixes */ +#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " +#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS " +#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS " +#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED " +#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED " +#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED " +#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED " +#define WPS_EVENT_PIN_ACTIVE "WPS-PIN-ACTIVE " +#define WPS_EVENT_CANCEL "WPS-CANCEL " +#define AP_STA_CONNECTED "AP-STA-CONNECTED " +#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " +#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH " +#define AP_STA_POLL_OK "AP-STA-POLL-OK " + +#define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " +#define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " + +#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD " +#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE " + +#define AP_EVENT_ENABLED "AP-ENABLED " +#define AP_EVENT_DISABLED "AP-DISABLED " +#define AP_EVENT_NO_IR "AP-NO_IR" + +#define INTERFACE_ENABLED "INTERFACE-ENABLED " +#define INTERFACE_DISABLED "INTERFACE-DISABLED " + +#define ACS_EVENT_STARTED "ACS-STARTED " +#define ACS_EVENT_COMPLETED "ACS-COMPLETED " +#define ACS_EVENT_FAILED "ACS-FAILED " + +#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED " +#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL " +#define DFS_EVENT_CAC_START "DFS-CAC-START " +#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED " +#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED " +#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED " + +#define AP_CSA_FINISHED "AP-CSA-FINISHED " + +#define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED " +#define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON " + +/* BSS Transition Management Query frame received */ +#define BSS_TM_QUERY "BSS-TM-QUERY " + +/* BSS Transition Management Response frame received */ +#define BSS_TM_RESP "BSS-TM-RESP " + +/* Collocated Interference Request frame received; + * parameters: */ +#define COLOC_INTF_REQ "COLOC-INTF-REQ " +/* Collocated Interference Report frame received; + * parameters: */ +#define COLOC_INTF_REPORT "COLOC-INTF-REPORT " + +/* MBO IE with cellular data connection preference received */ +#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE " + +/* BSS Transition Management Request received with MBO transition reason */ +#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON " + +/* parameters: */ +#define BEACON_REQ_TX_STATUS "BEACON-REQ-TX-STATUS " +/* parameters: */ +#define BEACON_RESP_RX "BEACON-RESP-RX " + +/* parameters: */ +#define LINK_MSR_RESP_RX "LINK-MSR-RESP-RX " + +/* PMKSA cache entry added; parameters: */ +#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED " +/* PMKSA cache entry removed; parameters: */ +#define PMKSA_CACHE_REMOVED "PMKSA-CACHE-REMOVED " + +/* FILS HLP Container receive; parameters: dst= src= frame= + */ +#define FILS_HLP_RX "FILS-HLP-RX " + +/* Event to indicate Probe Request frame; + * parameters: sa= signal= */ +#define RX_PROBE_REQUEST "RX-PROBE-REQUEST " + +/* Event to indicate station's HT/VHT operation mode change information */ +#define STA_OPMODE_MAX_BW_CHANGED "STA-OPMODE-MAX-BW-CHANGED " +#define STA_OPMODE_SMPS_MODE_CHANGED "STA-OPMODE-SMPS-MODE-CHANGED " +#define STA_OPMODE_N_SS_CHANGED "STA-OPMODE-N_SS-CHANGED " + +/* New interface addition or removal for 4addr WDS SDA */ +#define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED " +#define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED " + +/* Transition mode disabled indication - followed by bitmap */ +#define TRANSITION_DISABLE "TRANSITION-DISABLE " + +/* OCV validation failure; parameters: addr= + * frame= error= */ +#define OCV_FAILURE "OCV-FAILURE " + +/* Event triggered for received management frame */ +#define AP_MGMT_FRAME_RECEIVED "AP-MGMT-FRAME-RECEIVED " + +#ifndef BIT +#define BIT(x) (1U << (x)) +#endif + +/* PASN authentication status */ +#define PASN_AUTH_STATUS "PASN-AUTH-STATUS " + +/* BSS command information masks */ + +#define WPA_BSS_MASK_ALL 0xFFFDFFFF +#define WPA_BSS_MASK_ID BIT(0) +#define WPA_BSS_MASK_BSSID BIT(1) +#define WPA_BSS_MASK_FREQ BIT(2) +#define WPA_BSS_MASK_BEACON_INT BIT(3) +#define WPA_BSS_MASK_CAPABILITIES BIT(4) +#define WPA_BSS_MASK_QUAL BIT(5) +#define WPA_BSS_MASK_NOISE BIT(6) +#define WPA_BSS_MASK_LEVEL BIT(7) +#define WPA_BSS_MASK_TSF BIT(8) +#define WPA_BSS_MASK_AGE BIT(9) +#define WPA_BSS_MASK_IE BIT(10) +#define WPA_BSS_MASK_FLAGS BIT(11) +#define WPA_BSS_MASK_SSID BIT(12) +#define WPA_BSS_MASK_WPS_SCAN BIT(13) +#define WPA_BSS_MASK_P2P_SCAN BIT(14) +#define WPA_BSS_MASK_INTERNETW BIT(15) +#define WPA_BSS_MASK_WIFI_DISPLAY BIT(16) +#define WPA_BSS_MASK_DELIM BIT(17) +#define WPA_BSS_MASK_MESH_SCAN BIT(18) +#define WPA_BSS_MASK_SNR BIT(19) +#define WPA_BSS_MASK_EST_THROUGHPUT BIT(20) +#define WPA_BSS_MASK_FST BIT(21) +#define WPA_BSS_MASK_UPDATE_IDX BIT(22) +#define WPA_BSS_MASK_BEACON_IE BIT(23) +#define WPA_BSS_MASK_FILS_INDICATION BIT(24) +#define WPA_BSS_MASK_RNR BIT(25) +#define WPA_BSS_MASK_ML BIT(26) +#define WPA_BSS_MASK_AP_MLD_ADDR BIT(27) + + +/* VENDOR_ELEM_* frame id values */ +enum wpa_vendor_elem_frame { + VENDOR_ELEM_PROBE_REQ_P2P = 0, + VENDOR_ELEM_PROBE_RESP_P2P = 1, + VENDOR_ELEM_PROBE_RESP_P2P_GO = 2, + VENDOR_ELEM_BEACON_P2P_GO = 3, + VENDOR_ELEM_P2P_PD_REQ = 4, + VENDOR_ELEM_P2P_PD_RESP = 5, + VENDOR_ELEM_P2P_GO_NEG_REQ = 6, + VENDOR_ELEM_P2P_GO_NEG_RESP = 7, + VENDOR_ELEM_P2P_GO_NEG_CONF = 8, + VENDOR_ELEM_P2P_INV_REQ = 9, + VENDOR_ELEM_P2P_INV_RESP = 10, + VENDOR_ELEM_P2P_ASSOC_REQ = 11, + VENDOR_ELEM_P2P_ASSOC_RESP = 12, + VENDOR_ELEM_ASSOC_REQ = 13, + VENDOR_ELEM_PROBE_REQ = 14, + NUM_VENDOR_ELEM_FRAMES +}; + + +/* wpa_supplicant/hostapd control interface access */ + +/** + * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd. + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path + * is configured in wpa_supplicant/hostapd and other programs using the control + * interface need to use matching path configuration. + */ +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); + +/** + * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket + * is used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd + * when the socket path for client need to be specified explicitly. Default + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client + * socket path is /tmp. + */ +struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path); + + +/** + * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * + * This function is used to close a control interface. + */ +void wpa_ctrl_close(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * @cmd: Command; usually, ASCII text, e.g., "PING" + * @cmd_len: Length of the cmd in bytes + * @reply: Buffer for the response + * @reply_len: Reply buffer length + * @msg_cb: Callback function for unsolicited messages or %NULL if not used + * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout + * + * This function is used to send commands to wpa_supplicant/hostapd. Received + * response will be written to reply and reply_len is set to the actual length + * of the reply. This function will block for up to 10 seconds while waiting + * for the reply. If unsolicited messages are received, the blocking time may + * be longer. + * + * msg_cb can be used to register a callback function that will be called for + * unsolicited messages received while waiting for the command response. These + * messages may be received if wpa_ctrl_request() is called at the same time as + * wpa_supplicant/hostapd is sending such a message. This can happen only if + * the program has used wpa_ctrl_attach() to register itself as a monitor for + * event messages. Alternatively to msg_cb, programs can register two control + * interface connections and use one of them for commands and the other one for + * receiving event messages, in other words, call wpa_ctrl_attach() only for + * the control interface connection that will be used for event messages. + */ +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)); + + +/** + * wpa_ctrl_attach - Register as an event monitor for the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function registers the control interface connection as a monitor for + * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the + * control interface connection starts receiving event messages that can be + * read with wpa_ctrl_recv(). + */ +int wpa_ctrl_attach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_detach - Unregister event monitor from the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function unregisters the control interface connection as a monitor for + * wpa_supplicant/hostapd events, i.e., cancels the registration done with + * wpa_ctrl_attach(). + */ +int wpa_ctrl_detach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_recv - Receive a pending control interface message + * @ctrl: Control interface data from wpa_ctrl_open() + * @reply: Buffer for the message data + * @reply_len: Length of the reply buffer + * Returns: 0 on success, -1 on failure + * + * This function will receive a pending control interface message. The received + * response will be written to reply and reply_len is set to the actual length + * of the reply. + + * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() + * must have been used to register the control interface as an event monitor. + */ +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); + + +/** + * wpa_ctrl_pending - Check whether there are pending event messages + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 1 if there are pending messages, 0 if no, or -1 on error + * + * This function will check whether there are any pending control interface + * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is + * only used for event messages, i.e., wpa_ctrl_attach() must have been used to + * register the control interface as an event monitor. + */ +int wpa_ctrl_pending(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_get_fd - Get file descriptor used by the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: File descriptor used for the connection + * + * This function can be used to get the file descriptor that is used for the + * control interface connection. The returned value can be used, e.g., with + * select() while waiting for multiple events. + * + * The returned file descriptor must not be used directly for sending or + * receiving packets; instead, the library functions wpa_ctrl_request() and + * wpa_ctrl_recv() must be used for this. + */ +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); + +#ifdef ANDROID +/** + * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that + * may be left over from clients that were previously connected to + * wpa_supplicant. This keeps these files from being orphaned in the + * event of crashes that prevented them from being removed as part + * of the normal orderly shutdown. + */ +void wpa_ctrl_cleanup(void); +#endif /* ANDROID */ + +#ifdef CONFIG_CTRL_IFACE_UDP +/* Port range for multiple wpa_supplicant instances and multiple VIFs */ +#define WPA_CTRL_IFACE_PORT 9877 +#define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */ +#define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */ + +char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl); +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_CTRL_H */ diff --git a/wpa_ctrl.mk b/wpa_ctrl.mk new file mode 100644 index 0000000..0a81dc7 --- /dev/null +++ b/wpa_ctrl.mk @@ -0,0 +1,21 @@ +WPA_CTRL_DIR := $(LVGL_DIR)/src/lib/wpa_ctrl + +# Убираем исходники модуля из автоматического сбора. +# Учитываем пути с "./", без "./" и прежние короткие имена. +CSRCS := $(filter-out \ + src/lib/wpa_ctrl/%.c \ + ./src/lib/wpa_ctrl/%.c \ + $(WPA_CTRL_DIR)/%.c \ + wpa_ctrl.c \ + jl_wpa_os_shim.c \ + jl_wifi_ctrl.c, \ + $(CSRCS)) + +# Реальная реализация нужна только на устройстве. +ifeq ($(JLV_TARGET),jlv135) +CSRCS += $(WPA_CTRL_DIR)/wpa_ctrl.c +CSRCS += $(WPA_CTRL_DIR)/jl_wpa_os_shim.c +CSRCS += $(WPA_CTRL_DIR)/jl_wifi_ctrl.c + +CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX +endif \ No newline at end of file