diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 80e4dacb8..5ce05cbdf 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -772,235 +772,6 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd, #ifdef CONFIG_WNM_AP -static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, - const char *cmd) -{ - u8 addr[ETH_ALEN]; - int disassoc_timer; - struct sta_info *sta; - - if (hwaddr_aton(cmd, addr)) - return -1; - if (cmd[17] != ' ') - return -1; - disassoc_timer = atoi(cmd + 17); - - sta = ap_get_sta(hapd, addr); - if (sta == NULL) { - wpa_printf(MSG_DEBUG, "Station " MACSTR - " not found for disassociation imminent message", - MAC2STR(addr)); - return -1; - } - - return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer); -} - - -static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, - const char *cmd) -{ - u8 addr[ETH_ALEN]; - const char *url, *timerstr; - int disassoc_timer; - struct sta_info *sta; - - if (hwaddr_aton(cmd, addr)) - return -1; - - sta = ap_get_sta(hapd, addr); - if (sta == NULL) { - wpa_printf(MSG_DEBUG, "Station " MACSTR - " not found for ESS disassociation imminent message", - MAC2STR(addr)); - return -1; - } - - timerstr = cmd + 17; - if (*timerstr != ' ') - return -1; - timerstr++; - disassoc_timer = atoi(timerstr); - if (disassoc_timer < 0 || disassoc_timer > 65535) - return -1; - - url = os_strchr(timerstr, ' '); - if (url == NULL) - return -1; - url++; - - return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer); -} - - -static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, - const char *cmd) -{ - u8 addr[ETH_ALEN]; - const char *pos, *end; - int disassoc_timer = 0; - struct sta_info *sta; - u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01; - u8 bss_term_dur[12]; - char *url = NULL; - int ret; - u8 nei_rep[1000]; - int nei_len; - u8 mbo[10]; - size_t mbo_len = 0; - - if (hwaddr_aton(cmd, addr)) { - wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); - return -1; - } - - sta = ap_get_sta(hapd, addr); - if (sta == NULL) { - wpa_printf(MSG_DEBUG, "Station " MACSTR - " not found for BSS TM Request message", - MAC2STR(addr)); - return -1; - } - - pos = os_strstr(cmd, " disassoc_timer="); - if (pos) { - pos += 16; - disassoc_timer = atoi(pos); - if (disassoc_timer < 0 || disassoc_timer > 65535) { - wpa_printf(MSG_DEBUG, "Invalid disassoc_timer"); - return -1; - } - } - - pos = os_strstr(cmd, " valid_int="); - if (pos) { - pos += 11; - valid_int = atoi(pos); - } - - pos = os_strstr(cmd, " dialog_token="); - if (pos) { - pos += 14; - dialog_token = atoi(pos); - } - - pos = os_strstr(cmd, " bss_term="); - if (pos) { - pos += 10; - req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED; - /* TODO: TSF configurable/learnable */ - bss_term_dur[0] = 4; /* Subelement ID */ - bss_term_dur[1] = 10; /* Length */ - os_memset(&bss_term_dur[2], 0, 8); - end = os_strchr(pos, ','); - if (end == NULL) { - wpa_printf(MSG_DEBUG, "Invalid bss_term data"); - return -1; - } - end++; - WPA_PUT_LE16(&bss_term_dur[10], atoi(end)); - } - - 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) { - size_t len; - pos += 5; - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - url = os_malloc(len + 1); - if (url == NULL) - return -1; - os_memcpy(url, pos, len); - url[len] = '\0'; - req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; - } - - if (os_strstr(cmd, " pref=1")) - req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED; - if (os_strstr(cmd, " abridged=1")) - req_mode |= WNM_BSS_TM_REQ_ABRIDGED; - 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, dialog_token, url, - 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; -} - - static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd, const char *cmd) { diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 9e9eadd2f..ffbc9f0f6 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -24,6 +24,7 @@ #include "ap_drv_ops.h" #include "mbo_ap.h" #include "taxonomy.h" +#include "wnm_ap.h" static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen, @@ -1047,3 +1048,236 @@ void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd) #endif /* CONFIG_MESH */ #endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + +#ifdef CONFIG_WNM_AP + +int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + int disassoc_timer; + struct sta_info *sta; + + if (hwaddr_aton(cmd, addr)) + return -1; + if (cmd[17] != ' ') + return -1; + disassoc_timer = atoi(cmd + 17); + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for disassociation imminent message", + MAC2STR(addr)); + return -1; + } + + return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer); +} + + +int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *url, *timerstr; + int disassoc_timer; + struct sta_info *sta; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for ESS disassociation imminent message", + MAC2STR(addr)); + return -1; + } + + timerstr = cmd + 17; + if (*timerstr != ' ') + return -1; + timerstr++; + disassoc_timer = atoi(timerstr); + if (disassoc_timer < 0 || disassoc_timer > 65535) + return -1; + + url = os_strchr(timerstr, ' '); + if (url == NULL) + return -1; + url++; + + return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer); +} + + +int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *pos, *end; + int disassoc_timer = 0; + struct sta_info *sta; + u8 req_mode = 0, valid_int = 0x01, dialog_token = 0x01; + u8 bss_term_dur[12]; + char *url = NULL; + int ret; + u8 nei_rep[1000]; + int nei_len; + u8 mbo[10]; + size_t mbo_len = 0; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for BSS TM Request message", + MAC2STR(addr)); + return -1; + } + + pos = os_strstr(cmd, " disassoc_timer="); + if (pos) { + pos += 16; + disassoc_timer = atoi(pos); + if (disassoc_timer < 0 || disassoc_timer > 65535) { + wpa_printf(MSG_DEBUG, "Invalid disassoc_timer"); + return -1; + } + } + + pos = os_strstr(cmd, " valid_int="); + if (pos) { + pos += 11; + valid_int = atoi(pos); + } + + pos = os_strstr(cmd, " dialog_token="); + if (pos) { + pos += 14; + dialog_token = atoi(pos); + } + + pos = os_strstr(cmd, " bss_term="); + if (pos) { + pos += 10; + req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED; + /* TODO: TSF configurable/learnable */ + bss_term_dur[0] = 4; /* Subelement ID */ + bss_term_dur[1] = 10; /* Length */ + os_memset(&bss_term_dur[2], 0, 8); + end = os_strchr(pos, ','); + if (end == NULL) { + wpa_printf(MSG_DEBUG, "Invalid bss_term data"); + return -1; + } + end++; + WPA_PUT_LE16(&bss_term_dur[10], atoi(end)); + } + + 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) { + size_t len; + pos += 5; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + url = os_malloc(len + 1); + if (url == NULL) + return -1; + os_memcpy(url, pos, len); + url[len] = '\0'; + req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT; + } + + if (os_strstr(cmd, " pref=1")) + req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED; + if (os_strstr(cmd, " abridged=1")) + req_mode |= WNM_BSS_TM_REQ_ABRIDGED; + 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, dialog_token, url, + 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_AP */ diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h index d1dcebfb9..fb70d9083 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -37,4 +37,11 @@ int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, const u8 *addr, char *buf, size_t len); void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd); +int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, + const char *cmd); +int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd, + const char *cmd); +int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, + const char *cmd); + #endif /* CTRL_IFACE_AP_H */