#include #include #include #include #include #include #define CCMP_HEADER_SIZE 8 #define CCMP_MIC_SIZE 8 #define CCMP_EXT_IV 0x20 #define TKIP_HEADER_SIZE 8 #define TKIP_MIC_SIZE 8 #define TKIP_ICV_SIZE 4 #define TKIP_EXT_IV 0x20 #define SEQUENCE_SIZE 6 static bool deserialize_cipher_key_index(const void *data, size_t data_size, uint8_t *index) { // all currently supported 802.11 cipher headers store the key id in bits 6-7 of byte 3 if (data_size < 4) return false; *index = ((const uint8_t *)data)[3] >> 6; return true; } static bool data_packet_for_device(u80211_device_t *device, const u80211_header_description_t *header) { return u80211_mac_address_equal(&header->addresses[0], &device->metadata.mac_address) || (header->addresses[0].bytes[0] & 1); } static void data_packet_addresses(const u80211_header_description_t *header, const u80211_mac_address_t **destination, const u80211_mac_address_t **source) { bool to_ds = header->frame_control & U80211_HEADER_FRAME_CONTROL_TO_DS; bool from_ds = header->frame_control & U80211_HEADER_FRAME_CONTROL_FROM_DS; if (to_ds) { *destination = &header->addresses[2]; *source = from_ds ? &header->addresses[3] : &header->addresses[1]; } else { *destination = &header->addresses[0]; *source = from_ds ? &header->addresses[2] : &header->addresses[1]; } } static bool process_ccmp_rx(u80211_device_t *device, const u80211_header_description_t *header, uint8_t key_index, void **data_start, size_t *data_size) { if (*data_size < CCMP_HEADER_SIZE + CCMP_MIC_SIZE) return false; const uint8_t *ccmp_header = *data_start; if (!(ccmp_header[3] & CCMP_EXT_IV)) return false; const uint8_t sequence[SEQUENCE_SIZE] = { ccmp_header[0], ccmp_header[1], ccmp_header[4], ccmp_header[5], ccmp_header[6], ccmp_header[7], }; if (!u80211_key_update_rx_sequence(device, header, key_index, U80211_CIPHER_CCMP, sequence, sizeof(sequence))) return false; *data_start = (uint8_t *)*data_start + CCMP_HEADER_SIZE; *data_size -= CCMP_HEADER_SIZE + CCMP_MIC_SIZE; return true; } static bool process_tkip_rx(u80211_device_t *device, const u80211_header_description_t *header, uint8_t key_index, void **data_start, size_t *data_size) { // TODO: support qos data frames and use their tid as the michael priority if (U80211_HEADER_FRAME_CONTROL_GET_TYPE(header->frame_control) != U80211_HEADER_FRAME_CONTROL_TYPE_DATA) return false; if (U80211_HEADER_FRAME_CONTROL_GET_SUBTYPE(header->frame_control) != U80211_HEADER_FRAME_CONTROL_SUBTYPE_DATA) return false; if (*data_size < TKIP_HEADER_SIZE + TKIP_MIC_SIZE + TKIP_ICV_SIZE) return false; const uint8_t *tkip_header = *data_start; if (!(tkip_header[3] & TKIP_EXT_IV)) return false; if (tkip_header[1] != ((tkip_header[0] | 0x20) & 0x7f)) return false; const uint8_t *payload = tkip_header + TKIP_HEADER_SIZE; size_t payload_size = *data_size - TKIP_HEADER_SIZE - TKIP_MIC_SIZE - TKIP_ICV_SIZE; const uint8_t *mic = payload + payload_size; u80211_tkip_data_t tkip_data = { .header = header, .key_index = key_index, .priority = 0, .data = payload, .data_size = payload_size, .sequence = { tkip_header[2], tkip_header[0], tkip_header[4], tkip_header[5], tkip_header[6], tkip_header[7], }, }; u80211_memcpy(tkip_data.mic, mic, sizeof(tkip_data.mic)); data_packet_addresses(header, &tkip_data.destination, &tkip_data.source); if (!u80211_key_validate_tkip_rx(device, &tkip_data)) return false; *data_start = (void *)payload; *data_size = payload_size; return true; } void u80211_process_packet(u80211_device_t *device, void *packet, size_t packet_size) { __atomic_add_fetch(&device->packet_count, 1, __ATOMIC_RELAXED); void *data_start; size_t data_size; u80211_header_description_t header; if (u80211_deserialize_header(packet, packet_size, &header, &data_start, &data_size) != U80211_STATUS_SUCCESS) return; // encrypted packets contain an additional header that we need to handle // TODO: this might be handled by future hardware, so this needs to be a flag in the device metadata if (header.frame_control & U80211_HEADER_FRAME_CONTROL_PROTECTED_FRAME) { uint8_t key_index; if (!deserialize_cipher_key_index(data_start, data_size, &key_index)) return; switch (u80211_select_cipher_by_index(device, &header, key_index)) { case U80211_CIPHER_CCMP: if (!process_ccmp_rx(device, &header, key_index, &data_start, &data_size)) return; break; case U80211_CIPHER_TKIP: if (!process_tkip_rx(device, &header, key_index, &data_start, &data_size)) return; break; default: return; } } switch (U80211_HEADER_FRAME_CONTROL_GET_TYPE(header.frame_control)) { case U80211_HEADER_FRAME_CONTROL_TYPE_MANAGEMENT: u80211_process_management_packet(device, &header, data_start, data_size); break; case U80211_HEADER_FRAME_CONTROL_TYPE_DATA: if (!data_packet_for_device(device, &header)) return; if (u80211_association_is_duplicate(device, &header, U80211_DEVICE_STATE_ASSOCIATED)) return; u80211_process_data_packet(device, &header, data_start, data_size); break; } } size_t u80211_get_packet_count(u80211_device_t *device) { return __atomic_load_n(&device->packet_count, __ATOMIC_RELAXED); } void u80211_reset_packet_count(u80211_device_t *device) { __atomic_store_n(&device->packet_count, 0, __ATOMIC_RELAXED); } static int deserialize_management_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size) { if (source_size < 14) return U80211_STATUS_NOT_ENOUGH_SPACE; u80211_memcpy(&header->addresses[1], source, 6); u80211_memcpy(&header->addresses[2], (const void *)((uintptr_t)source + 6), 6); header->sequence_control = deserialize_le16((const void *)((uintptr_t)source + 12)); *data_start = (void *)((uintptr_t)source + 14); *data_size = source_size - 14; return U80211_STATUS_SUCCESS; } static int deserialize_control_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size) { (void)source; (void)source_size; (void)header; (void)data_start; (void)data_size; return U80211_STATUS_UNSUPPORTED; } static int deserialize_data_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size) { int subtype = U80211_HEADER_FRAME_CONTROL_GET_SUBTYPE(header->frame_control); if (subtype != U80211_HEADER_FRAME_CONTROL_SUBTYPE_DATA && subtype != U80211_HEADER_FRAME_CONTROL_SUBTYPE_NULL_DATA) return U80211_STATUS_UNSUPPORTED; size_t header_size = (header->frame_control & U80211_HEADER_FRAME_CONTROL_TO_DS) && (header->frame_control & U80211_HEADER_FRAME_CONTROL_FROM_DS) ? 20 : 14; if (source_size < header_size) return U80211_STATUS_NOT_ENOUGH_SPACE; u80211_memcpy(&header->addresses[1], source, 6); u80211_memcpy(&header->addresses[2], (const void *)((uintptr_t)source + 6), 6); if (header_size == 20) { header->sequence_control = deserialize_le16((const void *)((uintptr_t)source + 12)); u80211_memcpy(&header->addresses[3], (const void *)((uintptr_t)source + 14), 6); } else { header->sequence_control = deserialize_le16((const void *)((uintptr_t)source + 12)); } *data_start = (void *)((uintptr_t)source + header_size); *data_size = source_size - header_size; return U80211_STATUS_SUCCESS; } int u80211_deserialize_header(void *source, size_t source_size, u80211_header_description_t *header, void **data_start, size_t *data_size) { if (source_size < 10) return U80211_STATUS_NOT_ENOUGH_SPACE; header->frame_control = deserialize_le16(source); header->duration_id = deserialize_le16((const void *)((uintptr_t)source + 2)); u80211_memcpy(&header->addresses[0], (const void *)((uintptr_t)source + 4), 6); void *next_part = (void *)((uintptr_t)source + 10); size_t next_part_size = source_size - 10; switch (U80211_HEADER_FRAME_CONTROL_GET_TYPE(header->frame_control)) { case U80211_HEADER_FRAME_CONTROL_TYPE_MANAGEMENT: return deserialize_management_header(next_part, next_part_size, header, data_start, data_size); case U80211_HEADER_FRAME_CONTROL_TYPE_CONTROL: return deserialize_control_header(next_part, next_part_size, header, data_start, data_size); case U80211_HEADER_FRAME_CONTROL_TYPE_DATA: return deserialize_data_header(next_part, next_part_size, header, data_start, data_size); default: return U80211_STATUS_UNSUPPORTED; } } static void serialize_common_header(u80211_header_description_t *header, uint8_t *destination) { serialize_le16(destination, header->frame_control); serialize_le16(destination + 2, header->duration_id); u80211_memcpy(destination + 4, &header->addresses[0], 6); } static int serialize_management_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor) { uint8_t *destination = u80211_descriptor_allocate_space(descriptor, 24); if (destination == NULL) return U80211_STATUS_NOT_ENOUGH_SPACE; serialize_common_header(header, destination); u80211_memcpy(destination + 10, &header->addresses[1], 6); u80211_memcpy(destination + 16, &header->addresses[2], 6); serialize_le16(destination + 22, header->sequence_control); return U80211_STATUS_SUCCESS; } static int serialize_control_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor) { (void)header; (void)descriptor; return U80211_STATUS_UNSUPPORTED; } static int serialize_data_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor) { int subtype = U80211_HEADER_FRAME_CONTROL_GET_SUBTYPE(header->frame_control); if (subtype != U80211_HEADER_FRAME_CONTROL_SUBTYPE_DATA && subtype != U80211_HEADER_FRAME_CONTROL_SUBTYPE_NULL_DATA) return U80211_STATUS_UNSUPPORTED; size_t header_size = (header->frame_control & U80211_HEADER_FRAME_CONTROL_TO_DS) && (header->frame_control & U80211_HEADER_FRAME_CONTROL_FROM_DS) ? 30 : 24; uint8_t *destination = u80211_descriptor_allocate_space(descriptor, header_size); if (destination == NULL) return U80211_STATUS_NOT_ENOUGH_SPACE; serialize_common_header(header, destination); u80211_memcpy(destination + 10, &header->addresses[1], 6); u80211_memcpy(destination + 16, &header->addresses[2], 6); if (header_size == 30) { serialize_le16(destination + 22, header->sequence_control); u80211_memcpy(destination + 24, &header->addresses[3], 6); } else { serialize_le16(destination + 22, header->sequence_control); } return U80211_STATUS_SUCCESS; } int u80211_serialize_header(u80211_header_description_t *header, u80211_tx_buffer_descriptor_t *descriptor) { switch (U80211_HEADER_FRAME_CONTROL_GET_TYPE(header->frame_control)) { case U80211_HEADER_FRAME_CONTROL_TYPE_MANAGEMENT: return serialize_management_header(header, descriptor); case U80211_HEADER_FRAME_CONTROL_TYPE_CONTROL: return serialize_control_header(header, descriptor); case U80211_HEADER_FRAME_CONTROL_TYPE_DATA: return serialize_data_header(header, descriptor); default: return U80211_STATUS_UNSUPPORTED; } }