| /* |
| * |
| * Wireless daemon for Linux |
| * |
| * Copyright (C) 2014-2019 Intel Corporation. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <ell/ell.h> |
| |
| #include "src/ie.h" |
| #include "src/mpdu.h" |
| |
| static bool validate_mgmt_header(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| /* Duration + Address1 + Address 2 + Address 3 + SeqCntrl */ |
| if (len < *offset + 22) |
| return false; |
| |
| *offset += 22; |
| |
| if (!mpdu->fc.order) |
| return true; |
| |
| if (len < *offset + 4) |
| return false; |
| |
| *offset += 4; |
| |
| return true; |
| } |
| |
| /* 802.11-2016 13.11.2 */ |
| static bool skip_resource_req_resp(struct ie_tlv_iter *iter) |
| { |
| struct ie_tlv_iter tmp; |
| |
| /* |
| * This is called when we've seen an RDE so we only need to validate |
| * and skip IEs representing one or more Resource Descriptors up to |
| * the end of this Resource Request or Resource Response. |
| * |
| * Since the Resource Descriptor specification is complex and, |
| * especially in the case of a Vendor Specific descriptor and |
| * in the case of a Resource Response to a failed request (with |
| * the optional information), there seems to be no strict definition |
| * of where one request/response ends and the next begins, allow |
| * any combination of any of the IEs listed in 13.11.2 until an |
| * IE that doesn't seem to be part of this RDE. |
| */ |
| |
| memcpy(&tmp, iter, sizeof(tmp)); |
| |
| while (ie_tlv_iter_next(&tmp)) { |
| switch (ie_tlv_iter_get_tag(&tmp)) { |
| case IE_TYPE_TSPEC: |
| case IE_TYPE_TCLAS: |
| case IE_TYPE_TCLAS_PROCESSING: |
| case IE_TYPE_EXPEDITED_BANDWIDTH_REQUEST: |
| case IE_TYPE_SCHEDULE: |
| case IE_TYPE_TS_DELAY: |
| case IE_TYPE_RIC_DESCRIPTOR: |
| case IE_TYPE_VENDOR_SPECIFIC: |
| memcpy(iter, &tmp, sizeof(tmp)); |
| continue; |
| default: |
| break; |
| } |
| break; |
| } |
| |
| return true; |
| } |
| |
| static bool validate_mgmt_ies(const uint8_t *ies, size_t ies_len, |
| const enum ie_type tag_order[], int tag_count) |
| { |
| struct ie_tlv_iter iter; |
| enum ie_type tag; |
| |
| ie_tlv_iter_init(&iter, ies, ies_len); |
| |
| while (ie_tlv_iter_next(&iter)) { |
| int i = 0; |
| tag = ie_tlv_iter_get_tag(&iter); |
| |
| /* Check that the tag is part of the valid set */ |
| while (i < tag_count && tag_order[i] != tag) |
| i += 1; |
| |
| /* |
| * 802.11-2016 section 9.3.3.2: |
| * "All fields and elements are mandatory unless stated |
| * otherwise and appear in the specified, relative order. |
| * STAs that encounter an element ID they do not recognize |
| * in the frame body of a received Management frame ignore |
| * that element and continue to parse the remainder of the |
| * management frame body (if any) for additional elements |
| * with recognizable element IDs." |
| */ |
| if (i == tag_count) |
| continue; |
| |
| /* Tag found, make sure no duplicates present unless allowed */ |
| if (tag != IE_TYPE_VENDOR_SPECIFIC && |
| tag != IE_TYPE_RIC_DATA && |
| tag != IE_TYPE_TRANSMIT_POWER_ENVELOPE && |
| tag != IE_TYPE_MCCAOP_ADVERTISEMENT && |
| tag != IE_TYPE_EMERGENCY_ALERT_IDENTIFIER && |
| tag != IE_TYPE_MULTIPLE_BSSID && |
| tag != IE_TYPE_NEIGHBOR_REPORT && |
| tag != IE_TYPE_QUIET_CHANNEL) { |
| struct ie_tlv_iter clone; |
| |
| memcpy(&clone, &iter, sizeof(clone)); |
| |
| while (ie_tlv_iter_next(&clone)) { |
| if (ie_tlv_iter_get_tag(&clone) != tag) |
| continue; |
| |
| return false; |
| } |
| } |
| |
| if (tag == IE_TYPE_RIC_DATA && !skip_resource_req_resp(&iter)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* 802.11-2016 section 9.3.3.6 */ |
| static bool validate_association_request_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| const struct mmpdu_association_request *body = |
| (const void *) mpdu + *offset; |
| static const enum ie_type ie_order[] = { |
| IE_TYPE_SSID, |
| IE_TYPE_SUPPORTED_RATES, |
| IE_TYPE_EXTENDED_SUPPORTED_RATES, |
| IE_TYPE_POWER_CAPABILITY, |
| IE_TYPE_SUPPORTED_CHANNELS, |
| IE_TYPE_RSN, |
| IE_TYPE_QOS_CAPABILITY, |
| IE_TYPE_RM_ENABLED_CAPABILITIES, |
| IE_TYPE_MOBILITY_DOMAIN, |
| IE_TYPE_SUPPORTED_OPERATING_CLASSES, |
| IE_TYPE_HT_CAPABILITIES, |
| IE_TYPE_BSS_COEXISTENCE, |
| IE_TYPE_EXTENDED_CAPABILITIES, |
| IE_TYPE_QOS_TRAFFIC_CAPABILITY, |
| IE_TYPE_TIM_BROADCAST_REQUEST, |
| IE_TYPE_INTERWORKING, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| |
| if (len < *offset + (int) sizeof(struct mmpdu_association_request)) |
| return false; |
| |
| *offset += sizeof(struct mmpdu_association_request); |
| |
| return validate_mgmt_ies(body->ies, len - *offset, ie_order, |
| L_ARRAY_SIZE(ie_order)); |
| } |
| |
| /* 802.11-2016 section 9.3.3.7 */ |
| static bool validate_association_response_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| const struct mmpdu_association_response *body = |
| (const void *) mpdu + *offset; |
| static const enum ie_type ie_order[] = { |
| IE_TYPE_SUPPORTED_RATES, |
| IE_TYPE_EXTENDED_SUPPORTED_RATES, |
| IE_TYPE_EDCA_PARAMETER_SET, |
| IE_TYPE_RCPI, |
| IE_TYPE_RSNI, |
| IE_TYPE_RM_ENABLED_CAPABILITIES, |
| IE_TYPE_MOBILITY_DOMAIN, |
| IE_TYPE_FAST_BSS_TRANSITION, |
| IE_TYPE_DSE_REGISTERED_LOCATION, |
| IE_TYPE_TIMEOUT_INTERVAL, |
| IE_TYPE_HT_CAPABILITIES, |
| IE_TYPE_HT_OPERATION, |
| IE_TYPE_BSS_COEXISTENCE, |
| IE_TYPE_OVERLAPPING_BSS_SCAN_PARAMETERS, |
| IE_TYPE_EXTENDED_CAPABILITIES, |
| IE_TYPE_BSS_MAX_IDLE_PERIOD, |
| IE_TYPE_TIM_BROADCAST_RESPONSE, |
| IE_TYPE_QOS_MAP_SET, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| |
| if (len < *offset + (int) sizeof(struct mmpdu_association_response)) |
| return false; |
| |
| *offset += sizeof(struct mmpdu_association_response); |
| |
| return validate_mgmt_ies(body->ies, len - *offset, ie_order, |
| L_ARRAY_SIZE(ie_order)); |
| } |
| |
| /* 802.11-2016 section 9.3.3.8 */ |
| static bool validate_reassociation_request_mmpdu( |
| const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| const struct mmpdu_reassociation_request *body = |
| (const void *) mpdu + *offset; |
| static const enum ie_type ie_order[] = { |
| IE_TYPE_SSID, |
| IE_TYPE_SUPPORTED_RATES, |
| IE_TYPE_EXTENDED_SUPPORTED_RATES, |
| IE_TYPE_POWER_CAPABILITY, |
| IE_TYPE_SUPPORTED_CHANNELS, |
| IE_TYPE_RSN, |
| IE_TYPE_QOS_CAPABILITY, |
| IE_TYPE_RM_ENABLED_CAPABILITIES, |
| IE_TYPE_MOBILITY_DOMAIN, |
| IE_TYPE_FAST_BSS_TRANSITION, |
| IE_TYPE_RIC_DATA, |
| IE_TYPE_SUPPORTED_OPERATING_CLASSES, |
| IE_TYPE_HT_CAPABILITIES, |
| IE_TYPE_BSS_COEXISTENCE, |
| IE_TYPE_EXTENDED_CAPABILITIES, |
| IE_TYPE_QOS_TRAFFIC_CAPABILITY, |
| IE_TYPE_TIM_BROADCAST_REQUEST, |
| IE_TYPE_FMS_REQUEST, |
| IE_TYPE_DMS_REQUEST, |
| IE_TYPE_INTERWORKING, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| |
| if (len < *offset + (int) sizeof(struct mmpdu_reassociation_request)) |
| return false; |
| |
| *offset += sizeof(struct mmpdu_reassociation_request); |
| |
| return validate_mgmt_ies(body->ies, len - *offset, ie_order, |
| L_ARRAY_SIZE(ie_order)); |
| } |
| |
| /* 802.11-2016 section 9.3.3.9 */ |
| static bool validate_reassociation_response_mmpdu( |
| const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| const struct mmpdu_reassociation_response *body = |
| (const void *) mpdu + *offset; |
| static const enum ie_type ie_order[] = { |
| IE_TYPE_SUPPORTED_RATES, |
| IE_TYPE_EXTENDED_SUPPORTED_RATES, |
| IE_TYPE_EDCA_PARAMETER_SET, |
| IE_TYPE_RCPI, |
| IE_TYPE_RSNI, |
| IE_TYPE_RM_ENABLED_CAPABILITIES, |
| IE_TYPE_RSN, |
| IE_TYPE_MOBILITY_DOMAIN, |
| IE_TYPE_FAST_BSS_TRANSITION, |
| IE_TYPE_RIC_DATA, |
| IE_TYPE_DSE_REGISTERED_LOCATION, |
| IE_TYPE_TIMEOUT_INTERVAL, |
| IE_TYPE_HT_CAPABILITIES, |
| IE_TYPE_HT_OPERATION, |
| IE_TYPE_BSS_COEXISTENCE, |
| IE_TYPE_OVERLAPPING_BSS_SCAN_PARAMETERS, |
| IE_TYPE_EXTENDED_CAPABILITIES, |
| IE_TYPE_BSS_MAX_IDLE_PERIOD, |
| IE_TYPE_TIM_BROADCAST_RESPONSE, |
| IE_TYPE_FMS_RESPONSE, |
| IE_TYPE_DMS_RESPONSE, |
| IE_TYPE_QOS_MAP_SET, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| |
| if (len < *offset + (int) sizeof(struct mmpdu_reassociation_response)) |
| return false; |
| |
| *offset += sizeof(struct mmpdu_reassociation_response); |
| |
| return validate_mgmt_ies(body->ies, len - *offset, ie_order, |
| L_ARRAY_SIZE(ie_order)); |
| } |
| |
| /* 802.11-2016 section 9.3.3.10 */ |
| static bool validate_probe_request_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| const struct mmpdu_probe_request *body = (const void *) mpdu + *offset; |
| static const enum ie_type ie_order[] = { |
| IE_TYPE_SSID, |
| IE_TYPE_SUPPORTED_RATES, |
| IE_TYPE_REQUEST, |
| IE_TYPE_EXTENDED_SUPPORTED_RATES, |
| IE_TYPE_DSSS_PARAMETER_SET, |
| IE_TYPE_SUPPORTED_OPERATING_CLASSES, |
| IE_TYPE_HT_CAPABILITIES, |
| IE_TYPE_BSS_COEXISTENCE, |
| IE_TYPE_EXTENDED_CAPABILITIES, |
| IE_TYPE_SSID_LIST, |
| IE_TYPE_CHANNEL_USAGE, |
| IE_TYPE_INTERWORKING, |
| IE_TYPE_MESH_ID, |
| IE_TYPE_MULTIBAND, |
| IE_TYPE_DMG_CAPABILITIES, |
| IE_TYPE_MULTIPLE_MAC_SUBLAYERS, |
| IE_TYPE_VHT_CAPABILITIES, |
| IE_TYPE_ESTIMATED_SERVICE_PARAMETERS, |
| IE_TYPE_EXTENDED_REQUEST, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| |
| if (len < *offset + (int) sizeof(struct mmpdu_probe_request)) |
| return false; |
| |
| *offset += sizeof(struct mmpdu_probe_request); |
| |
| return validate_mgmt_ies(body->ies, len - *offset, ie_order, |
| L_ARRAY_SIZE(ie_order)); |
| } |
| |
| /* 802.11-2016 section 9.3.3.11 */ |
| static bool validate_probe_response_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| /* |
| * If this is a response to a frame that could have contained a |
| * Request or an Extended Request element, then, after all of the |
| * "Elements that would have been included even in the absence of |
| * the Request element or Extended Request element" (802.11-2016 |
| * section 11.1.4.3.5) basically any Element ID may appear with the |
| * only requirement being an ascending order of the numerical values |
| * of the IDs. |
| * |
| * Given the above, and the fact that nobody on the planet seems |
| * to order IEs properly inside the Management frames, we simply skip |
| * any checking here and return true. |
| */ |
| |
| return true; |
| } |
| |
| /* 802.11-2016 section 9.3.3.16 */ |
| static bool validate_timing_advertisement_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| const struct mmpdu_timing_advertisement *body = |
| (const void *) mpdu + *offset; |
| static const enum ie_type ie_order[] = { |
| IE_TYPE_COUNTRY, |
| IE_TYPE_POWER_CONSTRAINT, |
| IE_TYPE_TIME_ADVERTISEMENT, |
| IE_TYPE_EXTENDED_CAPABILITIES, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| |
| if (len < *offset + (int) sizeof(struct mmpdu_timing_advertisement)) |
| return false; |
| |
| *offset += sizeof(struct mmpdu_timing_advertisement); |
| |
| return validate_mgmt_ies(body->ies, len - *offset, ie_order, |
| L_ARRAY_SIZE(ie_order)); |
| } |
| |
| /* 802.11-2016 section 9.3.3.3 */ |
| static bool validate_beacon_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| const struct mmpdu_beacon *body = (const void *) mpdu + *offset; |
| static const enum ie_type ie_order[] = { |
| IE_TYPE_SSID, |
| IE_TYPE_SUPPORTED_RATES, |
| IE_TYPE_DSSS_PARAMETER_SET, |
| IE_TYPE_CF_PARAMETER_SET, |
| IE_TYPE_IBSS_PARAMETER_SET, |
| IE_TYPE_TIM, |
| IE_TYPE_COUNTRY, |
| IE_TYPE_POWER_CONSTRAINT, |
| IE_TYPE_CHANNEL_SWITCH_ANNOUNCEMENT, |
| IE_TYPE_QUIET, |
| IE_TYPE_IBSS_DFS, |
| IE_TYPE_TPC_REPORT, |
| IE_TYPE_ERP, |
| IE_TYPE_EXTENDED_SUPPORTED_RATES, |
| IE_TYPE_RSN, |
| IE_TYPE_BSS_LOAD, |
| IE_TYPE_EDCA_PARAMETER_SET, |
| IE_TYPE_QOS_CAPABILITY, |
| IE_TYPE_AP_CHANNEL_REPORT, |
| IE_TYPE_BSS_AVERAGE_ACCESS_DELAY, |
| IE_TYPE_ANTENNA, |
| IE_TYPE_BSS_AVAILABLE_ADMISSION_CAPACITY, |
| IE_TYPE_BSS_AC_ACCESS_DELAY, |
| IE_TYPE_MEASUREMENT_PILOT_TRANSMISSION, |
| IE_TYPE_MULTIPLE_BSSID, |
| IE_TYPE_RM_ENABLED_CAPABILITIES, |
| IE_TYPE_MOBILITY_DOMAIN, |
| IE_TYPE_DSE_REGISTERED_LOCATION, |
| IE_TYPE_EXTENDED_CHANNEL_SWITCH_ANNOUNCEMENT, |
| IE_TYPE_SUPPORTED_OPERATING_CLASSES, |
| IE_TYPE_HT_CAPABILITIES, |
| IE_TYPE_HT_OPERATION, |
| IE_TYPE_BSS_COEXISTENCE, |
| IE_TYPE_OVERLAPPING_BSS_SCAN_PARAMETERS, |
| IE_TYPE_EXTENDED_CAPABILITIES, |
| IE_TYPE_FMS_DESCRIPTOR, |
| IE_TYPE_QOS_TRAFFIC_CAPABILITY, |
| IE_TYPE_TIME_ADVERTISEMENT, |
| IE_TYPE_INTERWORKING, |
| IE_TYPE_ADVERTISEMENT_PROTOCOL, |
| IE_TYPE_ROAMING_CONSORTIUM, |
| IE_TYPE_EMERGENCY_ALERT_IDENTIFIER, |
| IE_TYPE_MESH_ID, |
| IE_TYPE_MESH_CONFIGURATION, |
| IE_TYPE_MESH_AWAKE_WINDOW, |
| IE_TYPE_BEACON_TIMING, |
| IE_TYPE_MCCAOP_ADVERTISEMENT_OVERVIEW, |
| IE_TYPE_MCCAOP_ADVERTISEMENT, |
| IE_TYPE_MESH_CHANNEL_SWITCH_PARAMETERS, |
| IE_TYPE_QMF_POLICY, |
| IE_TYPE_QLOAD_REPORT, |
| IE_TYPE_HCCA_TXOP_UPDATE_COUNT, |
| IE_TYPE_MULTIBAND, |
| IE_TYPE_VHT_CAPABILITIES, |
| IE_TYPE_VHT_OPERATION, |
| IE_TYPE_TRANSMIT_POWER_ENVELOPE, |
| IE_TYPE_CHANNEL_SWITCH_WRAPPER, |
| IE_TYPE_EXTENDED_BSS_LOAD, |
| IE_TYPE_QUIET_CHANNEL, |
| IE_TYPE_OPERATING_MODE_NOTIFICATION, |
| IE_TYPE_REDUCED_NEIGHBOR_REPORT, |
| IE_TYPE_TVHT_OPERATION, |
| IE_TYPE_ESTIMATED_SERVICE_PARAMETERS, |
| IE_TYPE_FUTURE_CHANNEL_GUIDANCE, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| |
| if (len < *offset + (int) sizeof(struct mmpdu_beacon)) |
| return false; |
| |
| *offset += sizeof(struct mmpdu_beacon); |
| |
| return validate_mgmt_ies(body->ies, len - *offset, ie_order, |
| L_ARRAY_SIZE(ie_order)); |
| } |
| |
| static bool validate_atim_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| return *offset == len; |
| } |
| |
| static bool validate_disassociation_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| *offset += 2; |
| return *offset <= len; |
| } |
| |
| static bool validate_authentication_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| uint16_t transaction_sequence; |
| const struct mmpdu_authentication *body = (const void *) mpdu + *offset; |
| static const enum ie_type ie_order_shared_key[] = { |
| IE_TYPE_CHALLENGE_TEXT, |
| IE_TYPE_MULTIBAND, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| static const enum ie_type ie_order_ft[] = { |
| IE_TYPE_RSN, |
| IE_TYPE_MOBILITY_DOMAIN, |
| IE_TYPE_FAST_BSS_TRANSITION, |
| IE_TYPE_TIMEOUT_INTERVAL, |
| IE_TYPE_RIC_DATA, |
| IE_TYPE_FAST_BSS_TRANSITION, |
| IE_TYPE_MULTIBAND, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| static const enum ie_type ie_order_error[] = { |
| IE_TYPE_NEIGHBOR_REPORT, |
| IE_TYPE_VENDOR_SPECIFIC, |
| }; |
| static const enum ie_type ie_order_fils[] = { |
| IE_TYPE_FILS_SESSION, |
| IE_TYPE_FILS_WRAPPED_DATA, |
| }; |
| |
| if (len < *offset + 6) |
| return false; |
| |
| *offset += 6; |
| |
| if (L_LE16_TO_CPU(L_LE16_TO_CPU(body->status)) != 0) |
| return validate_mgmt_ies(body->ies, len - *offset, |
| ie_order_error, |
| L_ARRAY_SIZE(ie_order_error)); |
| |
| switch (L_LE16_TO_CPU(body->algorithm)) { |
| case MMPDU_AUTH_ALGO_OPEN_SYSTEM: |
| return *offset <= len; |
| case MMPDU_AUTH_ALGO_SHARED_KEY: |
| transaction_sequence = |
| L_LE16_TO_CPU(body->transaction_sequence); |
| |
| if (transaction_sequence < 2 || transaction_sequence > 3) |
| return *offset <= len; |
| |
| return validate_mgmt_ies(body->ies, len - *offset, |
| ie_order_shared_key, |
| L_ARRAY_SIZE(ie_order_shared_key)); |
| case MMPDU_AUTH_ALGO_FT: |
| return validate_mgmt_ies(body->ies, len - *offset, ie_order_ft, |
| L_ARRAY_SIZE(ie_order_ft)); |
| case MMPDU_AUTH_ALGO_SAE: |
| return *offset <= len; |
| case MMPDU_AUTH_ALGO_FILS_SK: |
| case MMPDU_AUTH_ALGO_FILS_SK_PFS: |
| return validate_mgmt_ies(body->ies, len - *offset, |
| ie_order_fils, |
| L_ARRAY_SIZE(ie_order_fils)); |
| default: |
| return false; |
| } |
| |
| return false; |
| } |
| |
| static bool validate_deauthentication_mmpdu(const struct mmpdu_header *mpdu, |
| int len, int *offset) |
| { |
| *offset += 2; |
| return *offset <= len; |
| } |
| |
| static bool validate_mgmt_mpdu(const struct mmpdu_header *mpdu, int len, |
| int *offset) |
| { |
| if (!validate_mgmt_header(mpdu, len, offset)) |
| return false; |
| |
| switch (mpdu->fc.subtype) { |
| case MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST: |
| return validate_association_request_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE: |
| return validate_association_response_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST: |
| return validate_reassociation_request_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE: |
| return validate_reassociation_response_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST: |
| return validate_probe_request_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE: |
| return validate_probe_response_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_TIMING_ADVERTISEMENT: |
| return validate_timing_advertisement_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_BEACON: |
| return validate_beacon_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_ATIM: |
| return validate_atim_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION: |
| return validate_disassociation_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION: |
| return validate_authentication_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_DEAUTHENTICATION: |
| return validate_deauthentication_mmpdu(mpdu, len, offset); |
| case MPDU_MANAGEMENT_SUBTYPE_ACTION: |
| case MPDU_MANAGEMENT_SUBTYPE_ACTION_NO_ACK: |
| return *offset + 1 <= len; |
| default: |
| return false; |
| } |
| |
| return true; |
| } |
| |
| const struct mmpdu_header *mpdu_validate(const uint8_t *frame, int len) |
| { |
| const struct mpdu_fc *fc; |
| const struct mmpdu_header *mmpdu; |
| int offset; |
| |
| if (!frame) |
| return NULL; |
| |
| if (len < 2) |
| return NULL; |
| |
| offset = 2; |
| fc = (const struct mpdu_fc *) frame; |
| |
| switch (fc->type) { |
| case MPDU_TYPE_MANAGEMENT: |
| mmpdu = (const struct mmpdu_header *) frame; |
| |
| if (validate_mgmt_mpdu(mmpdu, len, &offset)) |
| return mmpdu; |
| |
| return NULL; |
| default: |
| return NULL; |
| } |
| } |
| |
| size_t mmpdu_header_len(const struct mmpdu_header *mmpdu) |
| { |
| return mmpdu->fc.order == 0 ? 24 : 28; |
| } |
| |
| const void *mmpdu_body(const struct mmpdu_header *mmpdu) |
| { |
| return ((const uint8_t *) mmpdu + mmpdu_header_len(mmpdu)); |
| } |