X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=hostapd%2Fctrl_iface.c;h=8d9d1a3beea76b29f8174431b425100a309f991b;hb=6bf6c6feca41e12f4f90f917e00feff0f93ddd33;hp=6be6c2ab5d3bdf98bb1b4059f0d937390c5a4b23;hpb=63e169e149fce48827ba8482e6ea32b4bc8edc8b;p=thirdparty%2Fhostap.git diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 6be6c2ab5..8d9d1a3be 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -1,6 +1,6 @@ /* * hostapd / UNIX domain socket -based control interface - * Copyright (c) 2004-2015, Jouni Malinen + * Copyright (c) 2004-2018, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -19,10 +19,20 @@ #include #include +#ifdef CONFIG_CTRL_IFACE_UDP +#include +#endif /* CONFIG_CTRL_IFACE_UDP */ + #include "utils/common.h" #include "utils/eloop.h" +#include "utils/module_tests.h" #include "common/version.h" #include "common/ieee802_11_defs.h" +#include "common/ctrl_iface_common.h" +#ifdef CONFIG_DPP +#include "common/dpp.h" +#endif /* CONFIG_DPP */ +#include "common/wpa_ctrl.h" #include "crypto/tls.h" #include "drivers/driver.h" #include "eapol_auth/eapol_auth_sm.h" @@ -42,6 +52,9 @@ #include "ap/wnm_ap.h" #include "ap/wpa_auth.h" #include "ap/beacon.h" +#include "ap/neighbor_db.h" +#include "ap/rrm.h" +#include "ap/dpp_hostapd.h" #include "wps/wps_defs.h" #include "wps/wps.h" #include "fst/fst_ctrl_iface.h" @@ -51,14 +64,15 @@ #define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256 -struct wpa_ctrl_dst { - struct wpa_ctrl_dst *next; - struct sockaddr_un addr; - socklen_t addrlen; - int debug_level; - int errors; -}; - +#ifdef CONFIG_CTRL_IFACE_UDP +#define COOKIE_LEN 8 +static unsigned char cookie[COOKIE_LEN]; +static unsigned char gcookie[COOKIE_LEN]; +#define HOSTAPD_CTRL_IFACE_PORT 8877 +#define HOSTAPD_CTRL_IFACE_PORT_LIMIT 50 +#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT 8878 +#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT 50 +#endif /* CONFIG_CTRL_IFACE_UDP */ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, enum wpa_msg_type type, @@ -66,81 +80,27 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, - struct sockaddr_un *from, - socklen_t fromlen) + struct sockaddr_storage *from, + socklen_t fromlen, const char *input) { - struct wpa_ctrl_dst *dst; - - dst = os_zalloc(sizeof(*dst)); - if (dst == NULL) - return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dst->next = hapd->ctrl_dst; - hapd->ctrl_dst = dst; - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", - (u8 *) from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); - return 0; + return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input); } static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst, *prev = NULL; - - dst = hapd->ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); - if (prev == NULL) - hapd->ctrl_dst = dst->next; - else - prev->next = dst->next; - os_free(dst); - return 0; - } - prev = dst; - dst = dst->next; - } - return -1; + return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen); } static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen, char *level) { - struct wpa_ctrl_dst *dst; - - wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); - - dst = hapd->ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " - "level", (u8 *) from->sun_path, fromlen - - offsetof(struct sockaddr_un, sun_path)); - dst->debug_level = atoi(level); - return 0; - } - dst = dst->next; - } - - return -1; + return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level); } @@ -170,7 +130,6 @@ static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd, } -#ifdef CONFIG_IEEE80211W #ifdef NEED_AP_MLME static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, const char *txtaddr) @@ -189,7 +148,6 @@ static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, return 0; } #endif /* NEED_AP_MLME */ -#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS @@ -808,7 +766,7 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd, #endif /* CONFIG_INTERWORKING */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, const char *cmd) @@ -883,7 +841,9 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, char *url = NULL; int ret; u8 nei_rep[1000]; - u8 *nei_pos = nei_rep; + int nei_len; + u8 mbo[10]; + size_t mbo_len = 0; if (hwaddr_aton(cmd, addr)) { wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); @@ -921,7 +881,7 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, /* TODO: TSF configurable/learnable */ bss_term_dur[0] = 4; /* Subelement ID */ bss_term_dur[1] = 10; /* Length */ - os_memset(bss_term_dur, 2, 8); + os_memset(&bss_term_dur[2], 0, 8); end = os_strchr(pos, ','); if (end == NULL) { wpa_printf(MSG_DEBUG, "Invalid bss_term data"); @@ -931,99 +891,10 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, WPA_PUT_LE16(&bss_term_dur[10], atoi(end)); } - - /* - * BSS Transition Candidate List Entries - Neighbor Report elements - * neighbor=,,, - * ,[,] - */ - pos = cmd; - while (pos) { - u8 *nei_start; - long int val; - char *endptr, *tmp; - - pos = os_strstr(pos, " neighbor="); - if (!pos) - break; - if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) { - wpa_printf(MSG_DEBUG, - "Not enough room for additional neighbor"); - return -1; - } - pos += 10; - - nei_start = nei_pos; - *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT; - nei_pos++; /* length to be filled in */ - - if (hwaddr_aton(pos, nei_pos)) { - wpa_printf(MSG_DEBUG, "Invalid BSSID"); - return -1; - } - nei_pos += ETH_ALEN; - pos += 17; - if (*pos != ',') { - wpa_printf(MSG_DEBUG, "Missing BSSID Information"); - return -1; - } - pos++; - - val = strtol(pos, &endptr, 0); - WPA_PUT_LE32(nei_pos, val); - nei_pos += 4; - if (*endptr != ',') { - wpa_printf(MSG_DEBUG, "Missing Operating Class"); - return -1; - } - pos = endptr + 1; - - *nei_pos++ = atoi(pos); /* Operating Class */ - pos = os_strchr(pos, ','); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, "Missing Channel Number"); - return -1; - } - pos++; - - *nei_pos++ = atoi(pos); /* Channel Number */ - pos = os_strchr(pos, ','); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, "Missing PHY Type"); - return -1; - } - pos++; - - *nei_pos++ = atoi(pos); /* PHY Type */ - end = os_strchr(pos, ' '); - tmp = os_strchr(pos, ','); - if (tmp && (!end || tmp < end)) { - /* Optional Subelements (hexdump) */ - size_t len; - - pos = tmp + 1; - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) { - wpa_printf(MSG_DEBUG, - "Not enough room for neighbor subelements"); - return -1; - } - if (len & 0x01 || - hexstr2bin(pos, nei_pos, len / 2) < 0) { - wpa_printf(MSG_DEBUG, - "Invalid neighbor subelement info"); - return -1; - } - nei_pos += len / 2; - pos = end; - } - - nei_start[1] = nei_pos - nei_start - 2; - } + nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep, + sizeof(nei_rep)); + if (nei_len < 0) + return -1; pos = os_strstr(cmd, " url="); if (pos) { @@ -1049,15 +920,113 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, if (os_strstr(cmd, " disassoc_imminent=1")) req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT; +#ifdef CONFIG_MBO + pos = os_strstr(cmd, "mbo="); + if (pos) { + unsigned int mbo_reason, cell_pref, reassoc_delay; + u8 *mbo_pos = mbo; + + ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason, + &reassoc_delay, &cell_pref); + if (ret != 3) { + wpa_printf(MSG_DEBUG, + "MBO requires three arguments: mbo=::"); + ret = -1; + goto fail; + } + + if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) { + wpa_printf(MSG_DEBUG, + "Invalid MBO transition reason code %u", + mbo_reason); + ret = -1; + goto fail; + } + + /* Valid values for Cellular preference are: 0, 1, 255 */ + if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) { + wpa_printf(MSG_DEBUG, + "Invalid MBO cellular capability %u", + cell_pref); + ret = -1; + goto fail; + } + + if (reassoc_delay > 65535 || + (reassoc_delay && + !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) { + wpa_printf(MSG_DEBUG, + "MBO: Assoc retry delay is only valid in disassoc imminent mode"); + ret = -1; + goto fail; + } + + *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON; + *mbo_pos++ = 1; + *mbo_pos++ = mbo_reason; + *mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF; + *mbo_pos++ = 1; + *mbo_pos++ = cell_pref; + + if (reassoc_delay) { + *mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY; + *mbo_pos++ = 2; + WPA_PUT_LE16(mbo_pos, reassoc_delay); + mbo_pos += 2; + } + + mbo_len = mbo_pos - mbo; + } +#endif /* CONFIG_MBO */ + ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, valid_int, bss_term_dur, url, - nei_pos > nei_rep ? nei_rep : NULL, - nei_pos - nei_rep); + nei_len ? nei_rep : NULL, nei_len, + mbo_len ? mbo : NULL, mbo_len); +#ifdef CONFIG_MBO +fail: +#endif /* CONFIG_MBO */ os_free(url); return ret; } -#endif /* CONFIG_WNM */ + +static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + unsigned int auto_report, timeout; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for Collocated Interference Request", + MAC2STR(addr)); + return -1; + } + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + auto_report = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + timeout = atoi(pos); + + return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout); +} + +#endif /* CONFIG_WNM_AP */ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, @@ -1083,7 +1052,7 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, return pos - buf; pos += ret; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "FT-PSK "); if (os_snprintf_error(end - pos, ret)) @@ -1096,6 +1065,14 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, return pos - buf; pos += ret; } +#ifdef CONFIG_SHA384 + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SHA384 */ #ifdef CONFIG_SAE if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { ret = os_snprintf(pos, end - pos, "FT-SAE "); @@ -1104,8 +1081,21 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, pos += ret; } #endif /* CONFIG_SAE */ -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W +#ifdef CONFIG_FILS + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_FILS */ +#endif /* CONFIG_IEEE80211R_AP */ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); if (os_snprintf_error(end - pos, ret)) @@ -1118,7 +1108,6 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, return pos - buf; pos += ret; } -#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_SAE if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { ret = os_snprintf(pos, end - pos, "SAE "); @@ -1141,6 +1130,38 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, return pos - buf; pos += ret; } +#ifdef CONFIG_FILS + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "FILS-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "FILS-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { + ret = os_snprintf(pos, end - pos, "OWE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { + ret = os_snprintf(pos, end - pos, "DPP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_DPP */ if (pos > buf && *(pos - 1) == ' ') { *(pos - 1) = '\0'; @@ -1269,6 +1290,42 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, } +static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd) +{ + struct sta_info *sta; + struct vlan_description vlan_id; + + if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED) + return; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, + sta->addr, &vlan_id) || + (vlan_id.notempty && + vlan_compare(&vlan_id, sta->vlan_desc))) + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + } +} + + +static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd) +{ + struct sta_info *sta; + struct vlan_description vlan_id; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, sta->addr, + &vlan_id) && + (!vlan_id.notempty || + !vlan_compare(&vlan_id, sta->vlan_desc))) + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + } +} + static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) { char *value; @@ -1306,51 +1363,67 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); #endif /* CONFIG_WPS_TESTING */ -#ifdef CONFIG_INTERWORKING - } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) { - int val = atoi(value); - if (val <= 0) - ret = -1; - else - hapd->gas_frag_limit = val; -#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { hapd->ext_mgmt_frame_handling = atoi(value); } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { hapd->ext_eapol_frame_io = atoi(value); +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { + os_free(hapd->dpp_config_obj_override); + hapd->dpp_config_obj_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) { + os_free(hapd->dpp_discovery_override); + hapd->dpp_discovery_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) { + os_free(hapd->dpp_groups_override); + hapd->dpp_groups_override = os_strdup(value); + } else if (os_strcasecmp(cmd, + "dpp_ignore_netaccesskey_mismatch") == 0) { + hapd->dpp_ignore_netaccesskey_mismatch = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_test") == 0) { + dpp_test = atoi(value); +#endif /* CONFIG_DPP */ #endif /* CONFIG_TESTING_OPTIONS */ - } else { - struct sta_info *sta; - int vlan_id; +#ifdef CONFIG_MBO + } else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) { + int val; + + if (!hapd->conf->mbo_enabled) + return -1; + + val = atoi(value); + if (val < 0 || val > 1) + return -1; + hapd->mbo_assoc_disallow = val; + ieee802_11_update_beacons(hapd->iface); + + /* + * TODO: Need to configure drivers that do AP MLME offload with + * disallowing station logic. + */ +#endif /* CONFIG_MBO */ +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { + os_free(hapd->dpp_configurator_params); + hapd->dpp_configurator_params = os_strdup(value); +#endif /* CONFIG_DPP */ + } else { ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); if (ret) return ret; if (os_strcasecmp(cmd, "deny_mac_file") == 0) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (hostapd_maclist_found( - hapd->conf->deny_mac, - hapd->conf->num_deny_mac, sta->addr, - &vlan_id) && - (!vlan_id || vlan_id == sta->vlan_id)) - ap_sta_disconnect( - hapd, sta, sta->addr, - WLAN_REASON_UNSPECIFIED); - } - } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED && - os_strcasecmp(cmd, "accept_mac_file") == 0) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!hostapd_maclist_found( - hapd->conf->accept_mac, - hapd->conf->num_accept_mac, - sta->addr, &vlan_id) || - (vlan_id && vlan_id != sta->vlan_id)) - ap_sta_disconnect( - hapd, sta, sta->addr, - WLAN_REASON_UNSPECIFIED); - } + hostapd_disassoc_deny_mac(hapd); + } else if (os_strcasecmp(cmd, "accept_mac_file") == 0) { + hostapd_disassoc_accept_mac(hapd); + } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 || + os_strncmp(cmd, "wmm_ac_", 7) == 0) { + hapd->parameter_set_count++; + if (ieee802_11_update_beacons(hapd->iface)) + wpa_printf(MSG_DEBUG, + "Failed to update beacons with WMM parameters"); } } @@ -1411,6 +1484,63 @@ static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface) } +static int +hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + struct hostapd_wpa_psk *psk; + const u8 *pmk; + int pmk_len; + int pmk_match; + int sta_match; + int bss_match; + int reason; + + pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); + + for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) { + pmk_match = PMK_LEN == pmk_len && + os_memcmp(psk->psk, pmk, pmk_len) == 0; + sta_match = psk->group == 0 && + os_memcmp(sta->addr, psk->addr, ETH_ALEN) == 0; + bss_match = psk->group == 1; + + if (pmk_match && (sta_match || bss_match)) + return 0; + } + + wpa_printf(MSG_INFO, "STA " MACSTR + " PSK/passphrase no longer valid - disconnect", + MAC2STR(sta->addr)); + reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + hostapd_drv_sta_deauth(hapd, sta->addr, reason); + ap_sta_deauthenticate(hapd, sta, reason); + + return 0; +} + + +static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + int err; + + hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk); + + err = hostapd_setup_wpa_psk(conf); + if (err < 0) { + wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d", + err); + return -1; + } + + ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter, + NULL); + + return 0; +} + + #ifdef CONFIG_TESTING_OPTIONS static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd) @@ -1500,91 +1630,236 @@ static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd) } -static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd) +static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd, + char *cmd) { - char *pos; - u8 src[ETH_ALEN], *buf; - int used; + char *pos, *param; size_t len; + u8 *buf; + int stype = 0, ok = 0; + union wpa_event_data event; - wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd); + if (!hapd->ext_mgmt_frame_handling) + return -1; + + /* stype= ok=<0/1> buf= */ + + wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd); pos = cmd; - used = hwaddr_aton2(pos, src); - if (used < 0) + param = os_strstr(pos, "stype="); + if (param) { + param += 6; + stype = atoi(param); + } + + param = os_strstr(pos, " ok="); + if (param) { + param += 4; + ok = atoi(param); + } + + param = os_strstr(pos, " buf="); + if (!param) return -1; - pos += used; - while (*pos == ' ') - pos++; + param += 5; - len = os_strlen(pos); + len = os_strlen(param); if (len & 1) return -1; len /= 2; buf = os_malloc(len); - if (buf == NULL) - return -1; - - if (hexstr2bin(pos, buf, len) < 0) { + if (!buf || hexstr2bin(param, buf, len) < 0) { os_free(buf); return -1; } - ieee802_1x_receive(hapd, src, buf, len); + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_TYPE_MGMT; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.stype = stype; + event.tx_status.ack = ok; + hapd->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event); + hapd->ext_mgmt_frame_handling = 1; + os_free(buf); return 0; } -static u16 ipv4_hdr_checksum(const void *buf, size_t len) +static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd, + char *cmd) { - size_t i; - u32 sum = 0; - const u16 *pos = buf; - - for (i = 0; i < len / 2; i++) - sum += *pos++; + char *pos, *param; + size_t len; + u8 *buf; + int freq = 0, datarate = 0, ssi_signal = 0; + union wpa_event_data event; - while (sum >> 16) - sum = (sum & 0xffff) + (sum >> 16); + if (!hapd->ext_mgmt_frame_handling) + return -1; - return sum ^ 0xffff; -} + /* freq= datarate= ssi_signal= frame= */ + wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd); -#define HWSIM_PACKETLEN 1500 -#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) + pos = cmd; + param = os_strstr(pos, "freq="); + if (param) { + param += 5; + freq = atoi(param); + } -void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, - size_t len) -{ - struct hostapd_data *hapd = ctx; - const struct ether_header *eth; - struct iphdr ip; - const u8 *pos; - unsigned int i; + param = os_strstr(pos, " datarate="); + if (param) { + param += 10; + datarate = atoi(param); + } - if (len != HWSIM_PACKETLEN) - return; + param = os_strstr(pos, " ssi_signal="); + if (param) { + param += 12; + ssi_signal = atoi(param); + } - eth = (const struct ether_header *) buf; - os_memcpy(&ip, eth + 1, sizeof(ip)); - pos = &buf[sizeof(*eth) + sizeof(ip)]; + param = os_strstr(pos, " frame="); + if (param == NULL) + return -1; + param += 7; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.freq = freq; + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + event.rx_mgmt.datarate = datarate; + hapd->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event); + hapd->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + +static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd) +{ + char *pos; + u8 src[ETH_ALEN], *buf; + int used; + size_t len; + + wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd); + + pos = cmd; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(pos, buf, len) < 0) { + os_free(buf); + return -1; + } + + ieee802_1x_receive(hapd, src, buf, len); + os_free(buf); + + return 0; +} + + +static u16 ipv4_hdr_checksum(const void *buf, size_t len) +{ + size_t i; + u32 sum = 0; + const u16 *pos = buf; + + for (i = 0; i < len / 2; i++) + sum += *pos++; + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return sum ^ 0xffff; +} + + +#define HWSIM_PACKETLEN 1500 +#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) + +static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + const struct ether_header *eth; + struct iphdr ip; + const u8 *pos; + unsigned int i; + char extra[30]; + + if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore unexpected length %d", + (int) len); + return; + } + + eth = (const struct ether_header *) buf; + os_memcpy(&ip, eth + 1, sizeof(ip)); + pos = &buf[sizeof(*eth) + sizeof(ip)]; if (ip.ihl != 5 || ip.version != 4 || - ntohs(ip.tot_len) != HWSIM_IP_LEN) + ntohs(ip.tot_len) > HWSIM_IP_LEN) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore unexpect IP header"); return; + } - for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) { - if (*pos != (u8) i) + for (i = 0; i < ntohs(ip.tot_len) - sizeof(ip); i++) { + if (*pos != (u8) i) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore mismatching payload"); return; + } pos++; } - wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR, - MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost)); + extra[0] = '\0'; + if (ntohs(ip.tot_len) != HWSIM_IP_LEN) + os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.tot_len)); + wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s", + MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra); } @@ -1629,7 +1904,7 @@ static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd, static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) { u8 dst[ETH_ALEN], src[ETH_ALEN]; - char *pos; + char *pos, *pos2; int used; long int val; u8 tos; @@ -1638,11 +1913,12 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) struct iphdr *ip; u8 *dpos; unsigned int i; + size_t send_len = HWSIM_IP_LEN; if (hapd->l2_test == NULL) return -1; - /* format: */ + /* format: [len=] */ pos = cmd; used = hwaddr_aton2(pos, dst); @@ -1656,11 +1932,19 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) return -1; pos += used; - val = strtol(pos, NULL, 0); + val = strtol(pos, &pos2, 0); if (val < 0 || val > 0xff) return -1; tos = val; + pos = os_strstr(pos2, " len="); + if (pos) { + i = atoi(pos + 5); + if (i < sizeof(*ip) || i > HWSIM_IP_LEN) + return -1; + send_len = i; + } + eth = (struct ether_header *) &buf[2]; os_memcpy(eth->ether_dhost, dst, ETH_ALEN); os_memcpy(eth->ether_shost, src, ETH_ALEN); @@ -1671,17 +1955,17 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) ip->version = 4; ip->ttl = 64; ip->tos = tos; - ip->tot_len = htons(HWSIM_IP_LEN); + ip->tot_len = htons(send_len); ip->protocol = 1; ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1); ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2); ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); dpos = (u8 *) (ip + 1); - for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) + for (i = 0; i < send_len - sizeof(*ip); i++) *dpos++ = i; if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2], - HWSIM_PACKETLEN) < 0) + sizeof(struct ether_header) + send_len) < 0) return -1; wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR @@ -1745,8 +2029,6 @@ done: static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; char *pos; wpa_trace_fail_after = atoi(cmd); @@ -1770,9 +2052,6 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_fail_func[256]; - extern unsigned int wpa_trace_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, wpa_trace_fail_func); #else /* WPA_TRACE_BFD */ @@ -1784,8 +2063,6 @@ static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd, static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; char *pos; wpa_trace_test_fail_after = atoi(cmd); @@ -1809,9 +2086,6 @@ static int hostapd_ctrl_get_fail(struct hostapd_data *hapd, char *buf, size_t buflen) { #ifdef WPA_TRACE_BFD - extern char wpa_trace_test_fail_func[256]; - extern unsigned int wpa_trace_test_fail_after; - return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after, wpa_trace_test_fail_func); #else /* WPA_TRACE_BFD */ @@ -1819,6 +2093,243 @@ static int hostapd_ctrl_get_fail(struct hostapd_data *hapd, #endif /* WPA_TRACE_BFD */ } + +static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + u8 zero[WPA_TK_MAX_LEN]; + + os_memset(zero, 0, sizeof(zero)); + + if (hwaddr_aton(cmd, addr)) + return -1; + + if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) { + if (hapd->last_igtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 1, NULL, 0, + zero, hapd->last_igtk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 1, NULL, 0, + hapd->last_igtk, + hapd->last_igtk_len); + } + + if (is_broadcast_ether_addr(addr)) { + if (hapd->last_gtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 1, NULL, 0, + zero, hapd->last_gtk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 1, NULL, 0, + hapd->last_gtk, hapd->last_gtk_len); + } + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + if (sta->last_tk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR, + MAC2STR(sta->addr)); + + /* First, use a zero key to avoid any possible duplicate key avoidance + * in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + zero, sta->last_tk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC/RSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + sta->last_tk, sta->last_tk_len); +} + + +static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *pos = cmd; + enum wpa_alg alg; + int idx, set_tx; + u8 seq[6], key[WPA_TK_MAX_LEN]; + size_t key_len; + + /* parameters: alg addr idx set_tx seq key */ + + alg = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + if (hwaddr_aton(pos, addr)) + return -1; + pos += 17; + if (*pos != ' ') + return -1; + pos++; + idx = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + set_tx = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + if (hexstr2bin(pos, seq, sizeof(seq)) < 0) + return -1; + pos += 2 * 6; + if (*pos != ' ') + return -1; + pos++; + key_len = os_strlen(pos) / 2; + if (hexstr2bin(pos, key, key_len) < 0) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Set key"); + return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx, + set_tx, seq, 6, key, key_len); +} + + +static void restore_tk(void *ctx1, void *ctx2) +{ + struct hostapd_data *hapd = ctx1; + struct sta_info *sta = ctx2; + + wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR, + MAC2STR(sta->addr)); + /* This does not really restore the TSC properly, so this will result + * in replay protection issues for now since there is no clean way of + * preventing encryption of a single EAPOL frame. */ + hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + sta->last_tk, sta->last_tk_len); +} + + +static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m1(sta->wpa_sm, + os_strstr(cmd, "change-anonce") != NULL, + plain ? restore_tk : NULL, hapd, sta); +} + + +static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m3(sta->wpa_sm, + plain ? restore_tk : NULL, hapd, sta); +} + + +static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd, + const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, + "TESTING: Send group M1 for the same GTK and zero RSC to " + MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_group_m1(sta->wpa_sm, + plain ? restore_tk : NULL, hapd, sta); +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -1835,6 +2346,11 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, return ret; for (i = 0; i < iface->num_bss; i++) { + + /* Save CHAN_SWITCH VHT config */ + hostapd_chan_switch_vht_config( + iface->bss[i], settings.freq_params.vht_enabled); + ret = hostapd_switch_channel(iface->bss[i], &settings); if (ret) { /* FIX: What do we do if CSA fails in the middle of @@ -1875,13 +2391,13 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd, /* cmd: [] */ vendor_id = strtoul(cmd, &pos, 16); - if (!isblank(*pos)) + if (!isblank((unsigned char) *pos)) return -EINVAL; subcmd = strtoul(pos, &pos, 10); if (*pos != '\0') { - if (!isblank(*pos++)) + if (!isblank((unsigned char) *pos++)) return -EINVAL; data_len = os_strlen(pos); } @@ -1900,71 +2416,522 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd, } } - reply = wpabuf_alloc((buflen - 1) / 2); - if (!reply) { - os_free(data); - return -ENOBUFS; - } + reply = wpabuf_alloc((buflen - 1) / 2); + if (!reply) { + os_free(data); + return -ENOBUFS; + } + + ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len, + reply); + + if (ret == 0) + ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply), + wpabuf_len(reply)); + + wpabuf_free(reply); + os_free(data); + + return ret; +} + + +static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return -1; + + eapol_auth_reauthenticate(sta->eapol_sm); + return 0; +} + + +static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + char *pos = cmd, *param; + + if (hwaddr_aton(pos, addr) || pos[17] != ' ') + return -1; + pos += 18; + param = pos; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return -1; + + return eapol_auth_set_conf(sta->eapol_sm, param, pos); +} + + +static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + char *pos, *end, *stamp; + int ret; + + /* cmd: "LOG_LEVEL []" */ + if (*cmd == '\0') { + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, "Current level: %s\n" + "Timestamp: %d\n", + debug_level_str(wpa_debug_level), + wpa_debug_timestamp); + if (os_snprintf_error(end - pos, ret)) + ret = 0; + + return ret; + } + + while (*cmd == ' ') + cmd++; + + stamp = os_strchr(cmd, ' '); + if (stamp) { + *stamp++ = '\0'; + while (*stamp == ' ') { + stamp++; + } + } + + if (os_strlen(cmd)) { + int level = str_to_debug_level(cmd); + if (level < 0) + return -1; + wpa_debug_level = level; + } + + if (stamp && os_strlen(stamp)) + wpa_debug_timestamp = atoi(stamp); + + os_memcpy(buf, "OK\n", 3); + return 3; +} + + +#ifdef NEED_AP_MLME +static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + struct hostapd_iface *iface = hapd->iface; + char *pos, *end; + struct hostapd_sta_info *info; + struct os_reltime now; + + if (!iface->num_sta_seen) + return 0; + + sta_track_expire(iface, 0); + + pos = buf; + end = buf + buflen; + + os_get_reltime(&now); + dl_list_for_each_reverse(info, &iface->sta_seen, + struct hostapd_sta_info, list) { + struct os_reltime age; + int ret; + + os_reltime_sub(&now, &info->last_seen, &age); + ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n", + MAC2STR(info->addr), (unsigned int) age.sec, + info->ssi_signal); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + } + + return pos - buf; +} +#endif /* NEED_AP_MLME */ + + +static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address"); + return -1; + } + + return hostapd_send_lci_req(hapd, addr); +} + + +static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd) +{ + u8 addr[ETH_ALEN]; + char *token, *context = NULL; + int random_interval, min_ap; + u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS]; + unsigned int n_responders; + + token = str_token(cmd, " ", &context); + if (!token || hwaddr_aton(token, addr)) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE - Bad destination address"); + return -1; + } + + token = str_token(cmd, " ", &context); + if (!token) + return -1; + + random_interval = atoi(token); + if (random_interval < 0 || random_interval > 0xffff) + return -1; + + token = str_token(cmd, " ", &context); + if (!token) + return -1; + + min_ap = atoi(token); + if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP) + return -1; + + n_responders = 0; + while ((token = str_token(cmd, " ", &context))) { + if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE: Too many responders"); + return -1; + } + + if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE: Bad responder address"); + return -1; + } + + n_responders++; + } + + if (!n_responders) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE - No FTM responder address"); + return -1; + } + + return hostapd_send_range_req(hapd, addr, random_interval, min_ap, + responders, n_responders); +} + + +static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd, + const char *cmd, char *reply, + size_t reply_size) +{ + u8 addr[ETH_ALEN]; + const char *pos; + struct wpabuf *req; + int ret; + u8 req_mode = 0; + + if (hwaddr_aton(cmd, addr)) + return -1; + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + if (os_strncmp(pos, "req_mode=", 9) == 0) { + int val = hex2byte(pos + 9); + + if (val < 0) + return -1; + req_mode = val; + pos += 11; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + } + req = wpabuf_parse_bin(pos); + if (!req) + return -1; + + ret = hostapd_send_beacon_req(hapd, addr, req_mode, req); + wpabuf_free(req); + if (ret >= 0) + ret = os_snprintf(reply, reply_size, "%d", ret); + return ret; +} + + +static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) +{ + struct wpa_ssid_value ssid; + u8 bssid[ETH_ALEN]; + struct wpabuf *nr, *lci = NULL, *civic = NULL; + int stationary = 0; + char *tmp; + int ret; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Neighbor report is not enabled"); + return -1; + } + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad or missing SSID"); + return -1; + } + buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' '); + if (!buf) + return -1; + + tmp = os_strstr(buf, "nr="); + if (!tmp) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Missing Neighbor Report element"); + return -1; + } + + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + + nr = wpabuf_parse_bin(tmp + 3); + if (!nr) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad Neighbor Report element"); + return -1; + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "lci="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + lci = wpabuf_parse_bin(tmp + 4); + if (!lci) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad LCI subelement"); + wpabuf_free(nr); + return -1; + } + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "civic="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + civic = wpabuf_parse_bin(tmp + 6); + if (!civic) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad civic subelement"); + wpabuf_free(nr); + wpabuf_free(lci); + return -1; + } + } + + if (!buf) + goto set; + + if (os_strstr(buf, "stat")) + stationary = 1; + +set: + ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic, + stationary); + + wpabuf_free(nr); + wpabuf_free(lci); + wpabuf_free(civic); + + return ret; +} + + +static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd, + char *buf) +{ + struct wpa_ssid_value ssid; + u8 bssid[ETH_ALEN]; + char *tmp; + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID"); + return -1; + } + + return hostapd_neighbor_remove(hapd, bssid, &ssid); +} + + +static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf, + size_t buflen) +{ + int ret, i; + char *pos, *end; + + ret = os_snprintf(buf, buflen, "%016llX:\n", + (long long unsigned) iface->drv_flags); + if (os_snprintf_error(buflen, ret)) + return -1; + + pos = buf + ret; + end = buf + buflen; + + for (i = 0; i < 64; i++) { + if (iface->drv_flags & (1LLU << i)) { + ret = os_snprintf(pos, end - pos, "%s\n", + driver_flag_to_string(1LLU << i)); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + } + + return pos - buf; +} + + +static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct vlan_description vlan_id; + + if (!(*num)) + return 0; + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + if (hostapd_maclist_found(*acl, *num, addr, &vlan_id)) + hostapd_remove_acl_mac(acl, num, addr); + + return 0; +} + + +static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl, + int *num) +{ + while (*num) + hostapd_remove_acl_mac(acl, num, (*acl)[0].addr); +} - ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len, - reply); - if (ret == 0) - ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply), - wpabuf_len(reply)); +static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num, + char *buf, size_t buflen) +{ + int i = 0, len = 0, ret = 0; - wpabuf_free(reply); - os_free(data); + if (!acl) + return 0; - return ret; + while (i < num) { + ret = os_snprintf(buf + len, buflen - len, + MACSTR " VLAN_ID=%d\n", + MAC2STR(acl[i].addr), + acl[i].vlan_id.untagged); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + i++; + len += ret; + } + return len; } -static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd, - const char *cmd) +static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num, + const char *cmd) { u8 addr[ETH_ALEN]; - struct sta_info *sta; + struct vlan_description vlan_id; + int ret = 0, vlanid = 0; + const char *pos; if (hwaddr_aton(cmd, addr)) return -1; - sta = ap_get_sta(hapd, addr); - if (!sta || !sta->eapol_sm) - return -1; + pos = os_strstr(cmd, "VLAN_ID="); + if (pos) + vlanid = atoi(pos + 8); - eapol_auth_reauthenticate(sta->eapol_sm); - return 0; + if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) { + ret = hostapd_add_acl_maclist(acl, num, vlanid, addr); + if (ret != -1 && *acl) + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + } + + return ret < 0 ? -1 : 0; } -static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd) +static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd, + const char *field, char *buf, + size_t buflen) { - u8 addr[ETH_ALEN]; - struct sta_info *sta; - char *pos = cmd, *param; + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field); - if (hwaddr_aton(pos, addr) || pos[17] != ' ') - return -1; - pos += 18; - param = pos; - pos = os_strchr(pos, ' '); - if (!pos) - return -1; - *pos++ = '\0'; +#ifdef CONFIG_DPP + if (os_strcmp(field, "dpp") == 0) { + int res; - sta = ap_get_sta(hapd, addr); - if (!sta || !sta->eapol_sm) - return -1; +#ifdef CONFIG_DPP2 + res = os_snprintf(buf, buflen, "DPP=2"); +#else /* CONFIG_DPP2 */ + res = os_snprintf(buf, buflen, "DPP=1"); +#endif /* CONFIG_DPP2 */ + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_DPP */ - return eapol_auth_set_conf(sta->eapol_sm, param, pos); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", + field); + + return -1; } static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, char *buf, char *reply, int reply_size, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { int reply_len, res; @@ -1978,6 +2945,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; + } else if (os_strncmp(buf, "NOTE ", 5) == 0) { + wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); } else if (os_strcmp(buf, "STATUS") == 0) { reply_len = hostapd_ctrl_iface_status(hapd, reply, reply_size); @@ -2025,7 +2994,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, reply_size); } else if (os_strcmp(buf, "ATTACH") == 0) { - if (hostapd_ctrl_iface_attach(hapd, from, fromlen)) + if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7)) reply_len = -1; } else if (os_strcmp(buf, "DETACH") == 0) { if (hostapd_ctrl_iface_detach(hapd, from, fromlen)) @@ -2043,16 +3015,22 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { if (hostapd_ctrl_iface_disassociate(hapd, buf + 13)) reply_len = -1; +#ifdef CONFIG_TAXONOMY + } else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) { + reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10, + reply, reply_size); +#endif /* CONFIG_TAXONOMY */ + } else if (os_strncmp(buf, "POLL_STA ", 9) == 0) { + if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9)) + reply_len = -1; } else if (os_strcmp(buf, "STOP_AP") == 0) { if (hostapd_ctrl_iface_stop_ap(hapd)) reply_len = -1; -#ifdef CONFIG_IEEE80211W #ifdef NEED_AP_MLME } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) { if (hostapd_ctrl_iface_sa_query(hapd, buf + 9)) reply_len = -1; #endif /* NEED_AP_MLME */ -#endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8)) @@ -2109,7 +3087,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16)) reply_len = -1; #endif /* CONFIG_HS20 */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) reply_len = -1; @@ -2119,7 +3097,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) { if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11)) reply_len = -1; -#endif /* CONFIG_WNM */ + } else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) { + if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15)) + reply_len = -1; +#endif /* CONFIG_WNM_AP */ } else if (os_strcmp(buf, "GET_CONFIG") == 0) { reply_len = hostapd_ctrl_iface_get_config(hapd, reply, reply_size); @@ -2132,6 +3113,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "ENABLE", 6) == 0) { if (hostapd_ctrl_iface_enable(hapd->iface)) reply_len = -1; + } else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) { + if (hostapd_ctrl_iface_reload_wpa_psk(hapd)) + reply_len = -1; } else if (os_strncmp(buf, "RELOAD", 6) == 0) { if (hostapd_ctrl_iface_reload(hapd->iface)) reply_len = -1; @@ -2148,6 +3132,13 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8)) reply_len = -1; + } else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) { + if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd, + buf + 23) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) { + if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0) + reply_len = -1; } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0) reply_len = -1; @@ -2171,6 +3162,24 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } else if (os_strcmp(buf, "GET_FAIL") == 0) { reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size); + } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) { + if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "SET_KEY ", 8) == 0) { + if (hostapd_ctrl_set_key(hapd, buf + 8) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) { + if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) { + if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) { + if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "REKEY_GTK") == 0) { + if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) @@ -2189,6 +3198,173 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) { if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10)) reply_len = -1; + } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) { + reply_len = hostapd_ctrl_iface_log_level( + hapd, buf + 9, reply, reply_size); +#ifdef NEED_AP_MLME + } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) { + reply_len = hostapd_ctrl_iface_track_sta_list( + hapd, reply, reply_size); +#endif /* NEED_AP_MLME */ + } else if (os_strcmp(buf, "PMKSA") == 0) { + reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply, + reply_size); + } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { + hostapd_ctrl_iface_pmksa_flush(hapd); + } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) { + if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) { + if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) { + if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) { + if (hostapd_ctrl_iface_req_lci(hapd, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) { + if (hostapd_ctrl_iface_req_range(hapd, buf + 10)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) { + reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11, + reply, reply_size); + } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { + reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, + reply_size); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); + } else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) { + if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) { + if (!hostapd_ctrl_iface_acl_add_mac( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac, buf + 19)) + hostapd_disassoc_accept_mac(hapd); + else + reply_len = -1; + } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) { + hostapd_ctrl_iface_acl_del_mac( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac, buf + 19); + } else if (os_strcmp(buf + 11, "SHOW") == 0) { + reply_len = hostapd_ctrl_iface_acl_show_mac( + hapd->conf->accept_mac, + hapd->conf->num_accept_mac, reply, reply_size); + } else if (os_strcmp(buf + 11, "CLEAR") == 0) { + hostapd_ctrl_iface_acl_clear_list( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac); + } + } else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) { + if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) { + if (!hostapd_ctrl_iface_acl_add_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17)) + hostapd_disassoc_deny_mac(hapd); + } else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) { + hostapd_ctrl_iface_acl_del_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17); + } else if (os_strcmp(buf + 9, "SHOW") == 0) { + reply_len = hostapd_ctrl_iface_acl_show_mac( + hapd->conf->deny_mac, + hapd->conf->num_deny_mac, reply, reply_size); + } else if (os_strcmp(buf + 9, "CLEAR") == 0) { + hostapd_ctrl_iface_acl_clear_list( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac); + } +#ifdef CONFIG_DPP + } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) { + res = hostapd_dpp_qr_code(hapd, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { + res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { + if (dpp_bootstrap_remove(hapd->iface->interfaces->dpp, + buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { + const char *uri; + + uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp, + atoi(buf + 22)); + if (!uri) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%s", uri); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { + reply_len = dpp_bootstrap_info(hapd->iface->interfaces->dpp, + atoi(buf + 19), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { + if (hostapd_dpp_auth_init(hapd, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) { + if (hostapd_dpp_listen(hapd, buf + 11) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) { + hostapd_dpp_stop(hapd); + hostapd_dpp_listen_stop(hapd); + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { + res = dpp_configurator_add(hapd->iface->interfaces->dpp, + buf + 20); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { + if (dpp_configurator_remove(hapd->iface->interfaces->dpp, + buf + 24) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { + if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { + reply_len = dpp_configurator_get_key_id( + hapd->iface->interfaces->dpp, + atoi(buf + 25), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { + res = hostapd_dpp_pkex_add(hapd, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) { + if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0) + reply_len = -1; +#endif /* CONFIG_DPP */ +#ifdef RADIUS_SERVER + } else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) { + if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0) + reply_len = -1; +#endif /* RADIUS_SERVER */ + } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) { + reply_len = hostapd_ctrl_iface_get_capability( + hapd, buf + 15, reply, reply_size); } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -2209,12 +3385,15 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, struct hostapd_data *hapd = eloop_ctx; char buf[4096]; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); - char *reply; + char *reply, *pos = buf; const int reply_size = 4096; int reply_len; int level = MSG_DEBUG; +#ifdef CONFIG_CTRL_IFACE_UDP + unsigned char lcookie[COOKIE_LEN]; +#endif /* CONFIG_CTRL_IFACE_UDP */ res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); @@ -2224,9 +3403,6 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, return; } buf[res] = '\0'; - if (os_strcmp(buf, "PING") == 0) - level = MSG_EXCESSIVE; - wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res); reply = os_malloc(reply_size); if (reply == NULL) { @@ -2238,10 +3414,46 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, return; } - reply_len = hostapd_ctrl_iface_receive_process(hapd, buf, +#ifdef CONFIG_CTRL_IFACE_UDP + if (os_strcmp(buf, "GET_COOKIE") == 0) { + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + cookie, COOKIE_LEN); + reply_len = 7 + 2 * COOKIE_LEN; + goto done; + } + + if (os_strncmp(buf, "COOKIE=", 7) != 0 || + hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "CTRL: No cookie in the request - drop request"); + os_free(reply); + return; + } + + if (os_memcmp(cookie, lcookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid cookie in the request - drop request"); + os_free(reply); + return; + } + + pos = buf + 7 + 2 * COOKIE_LEN; + while (*pos == ' ') + pos++; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + if (os_strcmp(pos, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res); + + reply_len = hostapd_ctrl_iface_receive_process(hapd, pos, reply, reply_size, &from, fromlen); +#ifdef CONFIG_CTRL_IFACE_UDP +done: +#endif /* CONFIG_CTRL_IFACE_UDP */ if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen) < 0) { wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", @@ -2251,6 +3463,7 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } +#ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) { char *buf; @@ -2270,6 +3483,7 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) buf[len - 1] = '\0'; return buf; } +#endif /* CONFIG_CTRL_IFACE_UDP */ static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, @@ -2285,6 +3499,99 @@ static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int hostapd_ctrl_iface_init(struct hostapd_data *hapd) { +#ifdef CONFIG_CTRL_IFACE_UDP + int port = HOSTAPD_CTRL_IFACE_PORT; + char p[32] = { 0 }; + char port_str[40], *tmp; + char *pos; + struct addrinfo hints = { 0 }, *res, *saveres; + int n; + + if (hapd->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + if (hapd->conf->ctrl_interface == NULL) + return 0; + + pos = os_strstr(hapd->conf->ctrl_interface, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port"); + goto fail; + } + } + + dl_list_init(&hapd->ctrl_dst); + hapd->ctrl_sock = -1; + os_get_random(cookie, COOKIE_LEN); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + hints.ai_flags = AI_PASSIVE; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + hints.ai_family = AF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_family = AF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_socktype = SOCK_DGRAM; + +try_again: + os_snprintf(p, sizeof(p), "%d", port); + n = getaddrinfo(NULL, p, &hints, &res); + if (n) { + wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); + goto fail; + } + + saveres = res; + hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (hapd->ctrl_sock < 0) { + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); + goto fail; + } + + if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) { + port--; + if ((HOSTAPD_CTRL_IFACE_PORT - port) < + HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos) + goto try_again; + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); + goto fail; + } + + freeaddrinfo(saveres); + + os_snprintf(port_str, sizeof(port_str), "udp:%d", port); + tmp = os_strdup(port_str); + if (tmp) { + os_free(hapd->conf->ctrl_interface); + hapd->conf->ctrl_interface = tmp; + } + wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); + + if (eloop_register_read_sock(hapd->ctrl_sock, + hostapd_ctrl_iface_receive, hapd, NULL) < + 0) { + hostapd_ctrl_iface_deinit(hapd); + return -1; + } + + hapd->msg_ctx = hapd; + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); + + return 0; + +fail: + if (hapd->ctrl_sock >= 0) + close(hapd->ctrl_sock); + return -1; +#else /* CONFIG_CTRL_IFACE_UDP */ struct sockaddr_un addr; int s = -1; char *fname = NULL; @@ -2294,6 +3601,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) return 0; } + dl_list_init(&hapd->ctrl_dst); + if (hapd->conf->ctrl_interface == NULL) return 0; @@ -2309,18 +3618,18 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } if (hapd->conf->ctrl_interface_gid_set && - chown(hapd->conf->ctrl_interface, -1, - hapd->conf->ctrl_interface_gid) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(hapd->conf->ctrl_interface, -1, + hapd->conf->ctrl_interface_gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); return -1; } if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && - chown(hapd->conf->ctrl_interface, -1, - hapd->iface->interfaces->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(hapd->conf->ctrl_interface, -1, + hapd->iface->interfaces->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); return -1; } @@ -2393,16 +3702,16 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } if (hapd->conf->ctrl_interface_gid_set && - chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", + lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", strerror(errno)); goto fail; } if (!hapd->conf->ctrl_interface_gid_set && hapd->iface->interfaces->ctrl_iface_group && - chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s", + lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", strerror(errno)); goto fail; } @@ -2433,6 +3742,7 @@ fail: os_free(fname); } return -1; +#endif /* CONFIG_CTRL_IFACE_UDP */ } @@ -2441,10 +3751,14 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) struct wpa_ctrl_dst *dst, *prev; if (hapd->ctrl_sock > -1) { +#ifndef CONFIG_CTRL_IFACE_UDP char *fname; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + eloop_unregister_read_sock(hapd->ctrl_sock); close(hapd->ctrl_sock); hapd->ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP fname = hostapd_ctrl_iface_path(hapd); if (fname) unlink(fname); @@ -2463,15 +3777,12 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) strerror(errno)); } } +#endif /* !CONFIG_CTRL_IFACE_UDP */ } - dst = hapd->ctrl_dst; - hapd->ctrl_dst = NULL; - while (dst) { - prev = dst; - dst = dst->next; - os_free(prev); - } + dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); #ifdef CONFIG_TESTING_OPTIONS l2_packet_deinit(hapd->l2_test); @@ -2503,54 +3814,19 @@ static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces, static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces, - struct sockaddr_un *from, - socklen_t fromlen) + struct sockaddr_storage *from, + socklen_t fromlen, char *input) { - struct wpa_ctrl_dst *dst; - - dst = os_zalloc(sizeof(*dst)); - if (dst == NULL) - return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); - dst->addrlen = fromlen; - dst->debug_level = MSG_INFO; - dst->next = interfaces->global_ctrl_dst; - interfaces->global_ctrl_dst = dst; - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached (global)", - from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); - return 0; + return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen, + input); } static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { - struct wpa_ctrl_dst *dst, *prev = NULL; - - dst = interfaces->global_ctrl_dst; - while (dst) { - if (fromlen == dst->addrlen && - os_memcmp(from->sun_path, dst->addr.sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)) - == 0) { - wpa_hexdump(MSG_DEBUG, - "CTRL_IFACE monitor detached (global)", - from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); - if (prev == NULL) - interfaces->global_ctrl_dst = dst->next; - else - prev->next = dst->next; - os_free(dst); - return 0; - } - prev = dst; - dst = dst->next; - } - return -1; + return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen); } @@ -2561,6 +3837,16 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) wps_testing_dummy_cred = 0; wps_corrupt_pkhash = 0; #endif /* CONFIG_WPS_TESTING */ + +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_DPP + dpp_test = DPP_TEST_DISABLED; +#endif /* CONFIG_DPP */ +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_DPP + dpp_global_clear(interfaces->dpp); +#endif /* CONFIG_DPP */ } @@ -2578,6 +3864,10 @@ hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces, if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) { hapd = hostapd_get_iface(interfaces, ifname); if (hapd) { + if (hapd->iface->fst) { + wpa_printf(MSG_INFO, "FST: Already attached"); + return -1; + } fst_hostapd_fill_iface_obj(hapd, &iface_obj); hapd->iface->fst = fst_attach(ifname, hapd->own_addr, &iface_obj, &cfg); @@ -2586,7 +3876,7 @@ hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces, } } - return EINVAL; + return -EINVAL; } @@ -2608,7 +3898,7 @@ hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces, } } - return EINVAL; + return -EINVAL; } #endif /* CONFIG_FST */ @@ -2699,6 +3989,51 @@ error_return: } +static int +hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces, + const char *input, + char *reply, int reply_size) +{ + size_t i, j; + int res; + char *pos, *end; + struct hostapd_iface *iface; + int show_ctrl = 0; + + if (input) + show_ctrl = !!os_strstr(input, "ctrl"); + + pos = reply; + end = reply + reply_size; + + for (i = 0; i < interfaces->count; i++) { + iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_bss_config *conf; + + conf = iface->conf->bss[j]; + if (show_ctrl) + res = os_snprintf(pos, end - pos, + "%s ctrl_iface=%s\n", + conf->iface, + conf->ctrl_interface ? + conf->ctrl_interface : "N/A"); + else + res = os_snprintf(pos, end - pos, "%s\n", + conf->iface); + if (os_snprintf_error(end - pos, res)) { + *pos = '\0'; + return pos - reply; + } + pos += res; + } + } + + return pos - reply; +} + + static int hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces, char *cmd) @@ -2748,7 +4083,7 @@ static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces, const char *ifname, char *buf, char *reply, int reply_size, - struct sockaddr_un *from, + struct sockaddr_storage *from, socklen_t fromlen) { struct hostapd_data *hapd; @@ -2772,15 +4107,18 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { void *interfaces = eloop_ctx; - char buf[256]; + char buffer[256], *buf = buffer; int res; - struct sockaddr_un from; + struct sockaddr_storage from; socklen_t fromlen = sizeof(from); char *reply; int reply_len; const int reply_size = 4096; +#ifdef CONFIG_CTRL_IFACE_UDP + unsigned char lcookie[COOKIE_LEN]; +#endif /* CONFIG_CTRL_IFACE_UDP */ - res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", @@ -2803,6 +4141,35 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, os_memcpy(reply, "OK\n", 3); reply_len = 3; +#ifdef CONFIG_CTRL_IFACE_UDP + if (os_strcmp(buf, "GET_COOKIE") == 0) { + os_memcpy(reply, "COOKIE=", 7); + wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1, + gcookie, COOKIE_LEN); + reply_len = 7 + 2 * COOKIE_LEN; + goto send_reply; + } + + if (os_strncmp(buf, "COOKIE=", 7) != 0 || + hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "CTRL: No cookie in the request - drop request"); + os_free(reply); + return; + } + + if (os_memcmp(gcookie, lcookie, COOKIE_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid cookie in the request - drop request"); + os_free(reply); + return; + } + + buf += 7 + 2 * COOKIE_LEN; + while (*buf == ' ') + buf++; +#endif /* CONFIG_CTRL_IFACE_UDP */ + if (os_strncmp(buf, "IFNAME=", 7) == 0) { char *pos = os_strchr(buf + 7, ' '); @@ -2831,7 +4198,11 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = -1; } else if (os_strcmp(buf, "ATTACH") == 0) { if (hostapd_global_ctrl_iface_attach(interfaces, &from, - fromlen)) + fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_global_ctrl_iface_attach(interfaces, &from, + fromlen, buf + 7)) reply_len = -1; } else if (os_strcmp(buf, "DETACH") == 0) { if (hostapd_global_ctrl_iface_detach(interfaces, &from, @@ -2839,7 +4210,6 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = -1; #ifdef CONFIG_MODULE_TESTS } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { - int hapd_module_tests(void); if (hapd_module_tests() < 0) reply_len = -1; #endif /* CONFIG_MODULE_TESTS */ @@ -2863,6 +4233,11 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = os_snprintf(reply, reply_size, "OK\n"); else reply_len = -1; + } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { + reply_len = hostapd_global_ctrl_iface_interfaces( + interfaces, buf + 10, reply, sizeof(buffer)); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); } else { wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " "ignored"); @@ -2884,6 +4259,7 @@ send_reply: } +#ifndef CONFIG_CTRL_IFACE_UDP static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) { char *buf; @@ -2903,10 +4279,93 @@ static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) buf[len - 1] = '\0'; return buf; } +#endif /* CONFIG_CTRL_IFACE_UDP */ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) { +#ifdef CONFIG_CTRL_IFACE_UDP + int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT; + char p[32] = { 0 }; + char *pos; + struct addrinfo hints = { 0 }, *res, *saveres; + int n; + + if (interface->global_ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + if (interface->global_iface_path == NULL) + return 0; + + pos = os_strstr(interface->global_iface_path, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port"); + goto fail; + } + } + + os_get_random(gcookie, COOKIE_LEN); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + hints.ai_flags = AI_PASSIVE; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + hints.ai_family = AF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_family = AF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_socktype = SOCK_DGRAM; + +try_again: + os_snprintf(p, sizeof(p), "%d", port); + n = getaddrinfo(NULL, p, &hints, &res); + if (n) { + wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); + goto fail; + } + + saveres = res; + interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (interface->global_ctrl_sock < 0) { + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); + goto fail; + } + + if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) < + 0) { + port++; + if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) < + HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos) + goto try_again; + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); + goto fail; + } + + freeaddrinfo(saveres); + + wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port); + + if (eloop_register_read_sock(interface->global_ctrl_sock, + hostapd_global_ctrl_iface_receive, + interface, NULL) < 0) { + hostapd_global_ctrl_iface_deinit(interface); + return -1; + } + + return 0; + +fail: + if (interface->global_ctrl_sock >= 0) + close(interface->global_ctrl_sock); + return -1; +#else /* CONFIG_CTRL_IFACE_UDP */ struct sockaddr_un addr; int s = -1; char *fname = NULL; @@ -2926,9 +4385,9 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) goto fail; } } else if (interface->ctrl_iface_group && - chown(interface->global_iface_path, -1, - interface->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(interface->global_iface_path, -1, + interface->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); goto fail; } @@ -2985,8 +4444,8 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) } if (interface->ctrl_iface_group && - chown(fname, -1, interface->ctrl_iface_group) < 0) { - wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s", + lchown(fname, -1, interface->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", strerror(errno)); goto fail; } @@ -3012,18 +4471,22 @@ fail: os_free(fname); } return -1; +#endif /* CONFIG_CTRL_IFACE_UDP */ } void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) { +#ifndef CONFIG_CTRL_IFACE_UDP char *fname = NULL; +#endif /* CONFIG_CTRL_IFACE_UDP */ struct wpa_ctrl_dst *dst, *prev; if (interfaces->global_ctrl_sock > -1) { eloop_unregister_read_sock(interfaces->global_ctrl_sock); close(interfaces->global_ctrl_sock); interfaces->global_ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP fname = hostapd_global_ctrl_iface_path(interfaces); if (fname) { unlink(fname); @@ -3043,18 +4506,27 @@ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) strerror(errno)); } } +#endif /* CONFIG_CTRL_IFACE_UDP */ } os_free(interfaces->global_iface_path); interfaces->global_iface_path = NULL; - dst = interfaces->global_ctrl_dst; - interfaces->global_ctrl_dst = NULL; - while (dst) { - prev = dst; - dst = dst->next; - os_free(prev); - } + dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst, + struct wpa_ctrl_dst, list) + os_free(dst); +} + + +static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst, + const char *buf) +{ + /* Enable Probe Request events based on explicit request. + * Other events are enabled by default. + */ + if (str_starts(buf, RX_PROBE_REQUEST)) + return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST); + return 1; } @@ -3063,6 +4535,7 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, const char *buf, size_t len) { struct wpa_ctrl_dst *dst, *next; + struct dl_list *ctrl_dst; struct msghdr msg; int idx; struct iovec io[2]; @@ -3071,13 +4544,13 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, if (type != WPA_MSG_ONLY_GLOBAL) { s = hapd->ctrl_sock; - dst = hapd->ctrl_dst; + ctrl_dst = &hapd->ctrl_dst; } else { s = hapd->iface->interfaces->global_ctrl_sock; - dst = hapd->iface->interfaces->global_ctrl_dst; + ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst; } - if (s < 0 || dst == NULL) + if (s < 0 || dl_list_empty(ctrl_dst)) return; os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); @@ -3090,12 +4563,11 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, msg.msg_iovlen = 2; idx = 0; - while (dst) { - next = dst->next; - if (level >= dst->debug_level) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", - (u8 *) dst->addr.sun_path, dst->addrlen - - offsetof(struct sockaddr_un, sun_path)); + dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { + if ((level >= dst->debug_level) && + hostapd_ctrl_check_event_enabled(dst, buf)) { + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send", + &dst->addr, dst->addrlen); msg.msg_name = &dst->addr; msg.msg_namelen = dst->addrlen; if (sendmsg(s, &msg, 0) < 0) { @@ -3119,7 +4591,6 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, dst->errors = 0; } idx++; - dst = next; } }