diff options
Diffstat (limited to 'src/common/ieee802_11_common.c')
-rw-r--r-- | src/common/ieee802_11_common.c | 247 |
1 files changed, 156 insertions, 91 deletions
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index e1ef27795b99..e42a327449eb 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -126,6 +126,10 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->roaming_cons_sel = pos; elems->roaming_cons_sel_len = elen; break; + case MULTI_AP_OUI_TYPE: + elems->multi_ap = pos; + elems->multi_ap_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -266,6 +270,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen, elems->password_id = pos; elems->password_id_len = elen; break; + case WLAN_EID_EXT_HE_CAPABILITIES: + elems->he_capabilities = pos; + elems->he_capabilities_len = elen; + break; + case WLAN_EID_EXT_OCV_OCI: + elems->oci = pos; + elems->oci_len = elen; + break; default: if (show_errors) { wpa_printf(MSG_MSGDUMP, @@ -291,29 +303,17 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors) { - size_t left = len; - const u8 *pos = start; + const struct element *elem; int unknown = 0; os_memset(elems, 0, sizeof(*elems)); - while (left >= 2) { - u8 id, elen; + if (!start) + return ParseOK; - id = *pos++; - elen = *pos++; - left -= 2; - - if (elen > left) { - if (show_errors) { - wpa_printf(MSG_DEBUG, "IEEE 802.11 element " - "parse failed (id=%d elen=%d " - "left=%lu)", - id, elen, (unsigned long) left); - wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); - } - return ParseFailed; - } + for_each_element(elem, start, len) { + u8 id = elem->id, elen = elem->datalen; + const u8 *pos = elem->data; switch (id) { case WLAN_EID_SSID: @@ -461,8 +461,7 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->mic = pos; elems->mic_len = elen; /* after mic everything is encrypted, so stop. */ - left = elen; - break; + goto done; case WLAN_EID_MULTI_BAND: if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) { wpa_printf(MSG_MSGDUMP, @@ -521,35 +520,33 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, id, elen); break; } - - left -= elen; - pos += elen; } - if (left) + if (!for_each_element_completed(elem, start, len)) { + if (show_errors) { + wpa_printf(MSG_DEBUG, + "IEEE 802.11 element parse failed @%d", + (int) (start + len - (const u8 *) elem)); + wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); + } return ParseFailed; + } +done: return unknown ? ParseUnknown : ParseOK; } int ieee802_11_ie_count(const u8 *ies, size_t ies_len) { + const struct element *elem; int count = 0; - const u8 *pos, *end; if (ies == NULL) return 0; - pos = ies; - end = ies + ies_len; - - while (end - pos >= 2) { - if (2 + pos[1] > end - pos) - break; + for_each_element(elem, ies, ies_len) count++; - pos += 2 + pos[1]; - } return count; } @@ -559,24 +556,17 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, u32 oui_type) { struct wpabuf *buf; - const u8 *end, *pos, *ie; - - pos = ies; - end = ies + ies_len; - ie = NULL; + const struct element *elem, *found = NULL; - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - return NULL; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == oui_type) { - ie = pos; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && + WPA_GET_BE32(elem->data) == oui_type) { + found = elem; break; } - pos += 2 + pos[1]; } - if (ie == NULL) + if (!found) return NULL; /* No specified vendor IE found */ buf = wpabuf_alloc(ies_len); @@ -587,13 +577,9 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, * There may be multiple vendor IEs in the message, so need to * concatenate their data fields. */ - while (end - pos > 1) { - if (2 + pos[1] > end - pos) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - WPA_GET_BE32(&pos[2]) == oui_type) - wpabuf_put_data(buf, pos + 6, pos[1] - 4); - pos += 2 + pos[1]; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) { + if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type) + wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4); } return buf; @@ -896,6 +882,41 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, } +int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth, + int sec_channel, u8 *op_class, u8 *channel) +{ + int vht = CHAN_WIDTH_UNKNOWN; + + switch (chanwidth) { + case CHAN_WIDTH_UNKNOWN: + case CHAN_WIDTH_20_NOHT: + case CHAN_WIDTH_20: + case CHAN_WIDTH_40: + vht = VHT_CHANWIDTH_USE_HT; + break; + case CHAN_WIDTH_80: + vht = VHT_CHANWIDTH_80MHZ; + break; + case CHAN_WIDTH_80P80: + vht = VHT_CHANWIDTH_80P80MHZ; + break; + case CHAN_WIDTH_160: + vht = VHT_CHANWIDTH_160MHZ; + break; + } + + if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class, + channel) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_WARNING, + "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)", + freq, chanwidth, sec_channel); + return -1; + } + + return 0; +} + + static const char *const us_op_class_cc[] = { "US", "CA", NULL }; @@ -1297,27 +1318,27 @@ const char * fc2str(u16 fc) int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf, size_t ies_len) { + const struct element *elem; + os_memset(info, 0, sizeof(*info)); - while (ies_buf && ies_len >= 2 && - info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) { - size_t len = 2 + ies_buf[1]; + if (!ies_buf) + return 0; - if (len > ies_len) { - wpa_hexdump(MSG_DEBUG, "Truncated IEs", - ies_buf, ies_len); - return -1; - } + for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) { + if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED) + return 0; - if (ies_buf[0] == WLAN_EID_MULTI_BAND) { - wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len); - info->ies[info->nof_ies].ie = ies_buf + 2; - info->ies[info->nof_ies].ie_len = ies_buf[1]; - info->nof_ies++; - } + wpa_printf(MSG_DEBUG, "MB IE of %u bytes found", + elem->datalen + 2); + info->ies[info->nof_ies].ie = elem->data; + info->ies[info->nof_ies].ie_len = elem->datalen; + info->nof_ies++; + } - ies_len -= len; - ies_buf += len; + if (!for_each_element_completed(elem, ies_buf, ies_len)) { + wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len); + return -1; } return 0; @@ -1440,22 +1461,13 @@ size_t global_op_class_size = ARRAY_SIZE(global_op_class); */ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) { - const u8 *end; + const struct element *elem; if (!ies) return NULL; - end = ies + len; - - while (end - ies > 1) { - if (2 + ies[1] > end - ies) - break; - - if (ies[0] == eid) - return ies; - - ies += 2 + ies[1]; - } + for_each_element_id(elem, eid, ies, len) + return &elem->id; return NULL; } @@ -1473,22 +1485,26 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) */ const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext) { - const u8 *end; + const struct element *elem; if (!ies) return NULL; - end = ies + len; + for_each_element_extid(elem, ext, ies, len) + return &elem->id; + + return NULL; +} - while (end - ies > 1) { - if (2 + ies[1] > end - ies) - break; - if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 && - ies[2] == ext) - return ies; +const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type) +{ + const struct element *elem; - ies += 2 + ies[1]; + for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) { + if (elem->datalen >= 4 && + vendor_type == WPA_GET_BE32(elem->data)) + return &elem->id; } return NULL; @@ -1519,6 +1535,26 @@ size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) } +size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value) +{ + u8 *pos = buf; + + if (len < 9) + return 0; + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = 7; /* len */ + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = MULTI_AP_OUI_TYPE; + *pos++ = MULTI_AP_SUB_ELEM_TYPE; + *pos++ = 1; /* len */ + *pos++ = value; + + return pos - buf; +} + + static const struct country_op_class us_op_class[] = { { 1, 115 }, { 2, 118 }, @@ -1664,6 +1700,27 @@ const struct oper_class_map * get_oper_class(const char *country, u8 op_class) } +int oper_class_bw_to_int(const struct oper_class_map *map) +{ + switch (map->bw) { + case BW20: + return 20; + case BW40PLUS: + case BW40MINUS: + return 40; + case BW80: + return 80; + case BW80P80: + case BW160: + return 160; + case BW2160: + return 2160; + default: + return 0; + } +} + + int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, size_t nei_rep_len) { @@ -1764,3 +1821,11 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, return nei_pos - nei_rep; } + + +int ieee802_11_ext_capab(const u8 *ie, unsigned int capab) +{ + if (!ie || ie[1] <= capab / 8) + return 0; + return !!(ie[2 + capab / 8] & BIT(capab % 8)); +} |