diff --git a/hostapd/config_file.c b/hostapd/config_file.c index fbaa04216..b26da71a8 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -4108,6 +4108,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, pos); return 1; } + } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) { + bss->coloc_intf_reporting = atoi(pos); #endif /* CONFIG_OWE */ } else { wpa_printf(MSG_ERROR, diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 628278f56..e539a0902 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -992,6 +992,42 @@ fail: return ret; } + +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 */ @@ -2961,6 +2997,9 @@ 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; + } 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, diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 73d2fd832..778366d49 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -684,6 +684,8 @@ struct hostapd_bss_config { char owe_transition_ifname[IFNAMSIZ + 1]; int *owe_groups; #endif /* CONFIG_OWE */ + + int coloc_intf_reporting; }; /** diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index a3f860992..c48139965 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -178,6 +178,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) case 1: /* Bits 8-15 */ if (hapd->conf->proxy_arp) *pos |= 0x10; /* Bit 12 - Proxy ARP */ + if (hapd->conf->coloc_intf_reporting) { + /* Bit 13 - Collocated Interference Reporting */ + *pos |= 0x20; + } break; case 2: /* Bits 16-23 */ if (hapd->conf->wnm_sleep_mode) diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 710fe502b..1e8f58b4d 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -453,6 +453,48 @@ static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd, } +static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd, + const u8 *addr, const u8 *buf, + size_t len) +{ + u8 dialog_token; + char *hex; + size_t hex_len; + + if (!hapd->conf->coloc_intf_reporting) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore unexpected Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + + if (len < 1) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore too short Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + dialog_token = *buf++; + len--; + + wpa_printf(MSG_DEBUG, + "WNM: Received Collocated Interference Report frame from " + MACSTR " (dialog_token=%u)", + MAC2STR(addr), dialog_token); + wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements", + buf, len); + + hex_len = 2 * len + 1; + hex = os_malloc(hex_len); + if (!hex) + return; + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s", + MAC2STR(addr), dialog_token, hex); + os_free(hex); +} + + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -483,6 +525,10 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, plen); return 0; + case WNM_COLLOCATED_INTERFERENCE_REPORT: + ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload, + plen); + return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, @@ -681,3 +727,40 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, return 0; } + + +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout) +{ + u8 buf[100], *pos; + struct ieee80211_mgmt *mgmt; + u8 dialog_token = 1; + + if (auto_report > 3 || timeout > 63) + return -1; + os_memset(buf, 0, sizeof(buf)); + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.coloc_intf_req.action = + WNM_COLLOCATED_INTERFERENCE_REQ; + mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token; + mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2); + pos = &mgmt->u.action.u.coloc_intf_req.req_info; + pos++; + + wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to " + MACSTR " (dialog_token=%u auto_report=%u timeout=%u)", + MAC2STR(sta->addr), dialog_token, auto_report, timeout); + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, + "WNM: Failed to send Collocated Interference Request frame"); + return -1; + } + + return 0; +} diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h index 56d0f881e..1806ba0e0 100644 --- a/src/ap/wnm_ap.h +++ b/src/ap/wnm_ap.h @@ -24,5 +24,7 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, const u8 *nei_rep, size_t nei_rep_len, const u8 *mbo_attrs, size_t mbo_len); void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx); +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout); #endif /* WNM_AP_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 80ea605d8..762e731ab 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -921,6 +921,16 @@ struct ieee80211_mgmt { * Entries (optional) */ u8 variable[]; } STRUCT_PACKED bss_tm_query; + struct { + u8 action; /* 11 */ + u8 dialog_token; + u8 req_info; + } STRUCT_PACKED coloc_intf_req; + struct { + u8 action; /* 12 */ + u8 dialog_token; + u8 variable[]; + } STRUCT_PACKED coloc_intf_report; struct { u8 action; /* 15 */ u8 variable[]; diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 4ee6400da..f65077e04 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -332,6 +332,13 @@ extern "C" { /* 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 " diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 23a657bf0..771e76696 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -2200,6 +2200,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) /* WNM-Sleep Mode Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) ret = -1; +#ifdef CONFIG_WNM + /* WNM - Collocated Interference Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x0b", 2) < 0) + ret = -1; +#endif /* CONFIG_WNM */ #ifdef CONFIG_HS20 /* WNM-Notification */ diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index ced77ebda..c43960697 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -4755,6 +4755,7 @@ static const struct global_parse_data global_fields[] = { { INT(gas_rand_addr_lifetime), 0 }, { INT_RANGE(gas_rand_mac_addr, 0, 2), 0 }, { INT_RANGE(dpp_config_processing, 0, 2), 0 }, + { INT_RANGE(coloc_intf_reporting, 0, 1), 0 }, }; #undef FUNC diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index ad4dd886f..cd7571f59 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -1469,6 +1469,15 @@ struct wpa_config { * profile automatically */ int dpp_config_processing; + + /** + * coloc_intf_reporting - Colocated interference reporting + * + * dot11CoLocIntfReportingActivated + * 0 = disabled (false) + * 1 = enabled (true) + */ + int coloc_intf_reporting; }; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index aa73f9df6..09115e19d 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1511,7 +1511,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->dpp_config_processing) fprintf(f, "dpp_config_processing=%d\n", config->dpp_config_processing); - + if (config->coloc_intf_reporting) + fprintf(f, "coloc_intf_reporting=%d\n", + config->coloc_intf_reporting); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 945ab9c4d..77a3133d8 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -750,6 +750,15 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value); } else if (os_strcasecmp(cmd, "roaming") == 0) { ret = wpa_drv_roaming(wpa_s, atoi(value), NULL); +#ifdef CONFIG_WNM + } else if (os_strcasecmp(cmd, "coloc_intf_elems") == 0) { + struct wpabuf *elems; + + elems = wpabuf_parse_bin(value); + if (!elems) + return -1; + wnm_set_coloc_intf_elems(wpa_s, elems); +#endif /* CONFIG_WNM */ } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -7437,6 +7446,22 @@ static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd list); } + +static int wpas_ctrl_iface_coloc_intf_report(struct wpa_supplicant *wpa_s, + char *cmd) +{ + struct wpabuf *elems; + int ret; + + elems = wpabuf_parse_bin(cmd); + if (!elems) + return -1; + + ret = wnm_send_coloc_intf_report(wpa_s, 0, elems); + wpabuf_free(elems); + return ret; +} + #endif /* CONFIG_WNM */ @@ -10423,6 +10448,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) { if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "COLOC_INTF_REPORT ", 18) == 0) { + if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18)) + reply_len = -1; #endif /* CONFIG_WNM */ } else if (os_strcmp(buf, "FLUSH") == 0) { wpa_supplicant_ctrl_iface_flush(wpa_s); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 860b2792c..dd6dd5267 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -317,6 +317,7 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpas_rrm_reset(wpa_s); wpa_s->wnmsleep_used = 0; + wnm_clear_coloc_intf_reporting(wpa_s); #ifdef CONFIG_TESTING_OPTIONS wpa_s->last_tk_alg = WPA_ALG_NONE; @@ -4339,6 +4340,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_AP */ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS); + wnm_clear_coloc_intf_reporting(wpa_s); break; #ifdef CONFIG_AP #ifdef NEED_AP_MLME diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 7c410e730..6b68fc9e3 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -338,6 +338,9 @@ void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) wpa_s->wnm_num_neighbor_report = 0; os_free(wpa_s->wnm_neighbor_report_elements); wpa_s->wnm_neighbor_report_elements = NULL; + + wpabuf_free(wpa_s->coloc_intf_elems); + wpa_s->coloc_intf_elems = NULL; } @@ -1717,6 +1720,46 @@ static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, } +static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *frm, + int len) +{ + u8 dialog_token, req_info, auto_report, timeout; + + if (!wpa_s->conf->coloc_intf_reporting) + return; + + /* Dialog Token [1] | Request Info [1] */ + + if (len < 2) + return; + dialog_token = frm[0]; + req_info = frm[1]; + auto_report = req_info & 0x03; + timeout = req_info >> 2; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")", + dialog_token, auto_report, timeout, MAC2STR(sa)); + + if (dialog_token == 0) + return; /* only nonzero values are used for request */ + + if (wpa_s->wpa_state != WPA_COMPLETED || + os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Collocated Interference Request frame not from current AP - ignore it"); + return; + } + + wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u", + dialog_token, auto_report, timeout); + wpa_s->coloc_intf_dialog_token = dialog_token; + wpa_s->coloc_intf_auto_report = auto_report; + wpa_s->coloc_intf_timeout = timeout; +} + + void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -1750,8 +1793,75 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, case WNM_NOTIFICATION_REQ: ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); break; + case WNM_COLLOCATED_INTERFERENCE_REQ: + ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos, + end - pos); + break; default: wpa_printf(MSG_ERROR, "WNM: Unknown request"); break; } } + + +int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token, + const struct wpabuf *elems) +{ + struct wpabuf *buf; + int ret; + + if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems) + return -1; + + wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to " + MACSTR " (dialog token %u)", + MAC2STR(wpa_s->bssid), dialog_token); + + buf = wpabuf_alloc(3 + wpabuf_len(elems)); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_buf(buf, elems); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head_u8(buf), wpabuf_len(buf), 0); + wpabuf_free(buf); + return ret; +} + + +void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s, + struct wpabuf *elems) +{ + wpabuf_free(wpa_s->coloc_intf_elems); + if (elems && wpabuf_len(elems) == 0) { + wpabuf_free(elems); + elems = NULL; + } + wpa_s->coloc_intf_elems = elems; + + if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems && + wpa_s->coloc_intf_dialog_token && + (wpa_s->coloc_intf_auto_report == 1 || + wpa_s->coloc_intf_auto_report == 3)) { + /* TODO: Check that there has not been less than + * wpa_s->coloc_intf_timeout * 200 TU from the last report. + */ + wnm_send_coloc_intf_report(wpa_s, + wpa_s->coloc_intf_dialog_token, + wpa_s->coloc_intf_elems); + } +} + + +void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WNM + wpa_s->coloc_intf_dialog_token = 0; + wpa_s->coloc_intf_auto_report = 0; +#endif /* CONFIG_WNM */ +} diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h index 02cd1cde6..29625f8ca 100644 --- a/wpa_supplicant/wnm_sta.h +++ b/wpa_supplicant/wnm_sta.h @@ -65,11 +65,16 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, int cand_list); void wnm_deallocate_memory(struct wpa_supplicant *wpa_s); +int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token, + const struct wpabuf *elems); +void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s, + struct wpabuf *elems); #ifdef CONFIG_WNM int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail); +void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s); #else /* CONFIG_WNM */ @@ -79,6 +84,10 @@ static inline int wnm_scan_process(struct wpa_supplicant *wpa_s, return 0; } +static inline void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) +{ +} + #endif /* CONFIG_WNM */ #endif /* WNM_STA_H */ diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index eed973590..6090e0662 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1645,6 +1645,10 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) case 0: /* Bits 0-7 */ break; case 1: /* Bits 8-15 */ + if (wpa_s->conf->coloc_intf_reporting) { + /* Bit 13 - Collocated Interference Reporting */ + *pos |= 0x20; + } break; case 2: /* Bits 16-23 */ #ifdef CONFIG_WNM diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 6a521f062..8b749f44e 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1059,6 +1059,10 @@ struct wpa_supplicant { struct neighbor_report *wnm_neighbor_report_elements; struct os_reltime wnm_cand_valid_until; u8 wnm_cand_from_bss[ETH_ALEN]; + struct wpabuf *coloc_intf_elems; + u8 coloc_intf_dialog_token; + u8 coloc_intf_auto_report; + u8 coloc_intf_timeout; #ifdef CONFIG_MBO unsigned int wnm_mbo_trans_reason_present:1; u8 wnm_mbo_transition_reason;