1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 10:45:57 +01:00
Files
libtins/src/utils/radiotap_parser.cpp
2017-05-23 19:07:49 -07:00

301 lines
8.6 KiB
C++

/*
* Copyright (c) 2017, Matias Fontanini
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "utils/radiotap_parser.h"
#include "exceptions.h"
using std::vector;
namespace Tins {
namespace Utils {
const RadioTapParser::FieldMetadata RadioTapParser::RADIOTAP_METADATA[] = {
{ 8, 8 }, /// TSFT
{ 1, 1 }, // FLAGS
{ 1, 1 }, // RATE
{ 4, 2 }, // CHANNEL
{ 2, 2 }, // FHSS
{ 1, 1 }, // DBM_SIGNAL
{ 1, 1 }, // DBM_NOISE
{ 2, 2 }, // LOCK_QUALITY
{ 2, 2 }, // TX_ATTENUATION
{ 2, 2 }, // DB_TX_ATTENUATION
{ 1, 1 }, // DBM_TX_ATTENUATION
{ 1, 1 }, // ANTENNA
{ 1, 1 }, // DB_SIGNAL
{ 1, 1 }, // DB_NOISE
{ 2, 2 }, // RX_FLAGS
{ 2, 2 }, // TX_FLAGS
{ 1, 1 }, // RTS_RETRIES
{ 1, 1 }, // DATA_RETRIES
{ 4, 4 }, // CHANNEL_PLUS
{ 3, 1 }, // MCS
};
const uint32_t RadioTapParser::MAX_RADIOTAP_FIELD = sizeof(RADIOTAP_METADATA) /
sizeof(FieldMetadata) + 1;
#if TINS_IS_LITTLE_ENDIAN
TINS_BEGIN_PACK
struct RadioTapFlags {
uint32_t
tsft:1,
flags:1,
rate:1,
channel:1,
fhss:1,
dbm_signal:1,
dbm_noise:1,
lock_quality:1,
tx_attenuation:1,
db_tx_attenuation:1,
dbm_tx_power:1,
antenna:1,
db_signal:1,
db_noise:1,
rx_flags:1,
tx_flags:1,
reserved1:1,
data_retries:1,
channel_plus:1,
mcs:1,
reserved2:4,
reserved3:7,
ext:1;
} TINS_END_PACK;
#else
TINS_BEGIN_PACK
struct RadioTapFlags {
uint32_t
lock_quality:1,
dbm_noise:1,
dbm_signal:1,
fhss:1,
channel:1,
rate:1,
flags:1,
tsft:1,
tx_flags:1,
rx_flags:1,
db_noise:1,
db_signal:1,
antenna:1,
dbm_tx_power:1,
db_tx_attenuation:1,
tx_attenuation:1,
reserved2:4,
mcs:1,
channel_plus:1,
data_retries:1,
reserved1:1,
ext:1,
reserved3:7;
} TINS_END_PACK;
#endif
void align_buffer(const uint8_t* buffer_start, const uint8_t*& buffer, uint32_t size, size_t n) {
uint32_t offset = ((buffer - buffer_start) % n);
if (offset) {
offset = n - offset;
if (offset > size) {
throw malformed_packet();
}
buffer += offset;
}
}
RadioTapParser::RadioTapParser(const vector<uint8_t>& buffer)
: current_namespace_(RADIOTAP_NS), current_bit_(MAX_RADIOTAP_FIELD), namespace_index_(0) {
if (buffer.empty()) {
start_ = 0;
end_ = 0;
current_ptr_ = start_;
}
else {
start_ = &*buffer.begin();
end_ = start_ + buffer.size();
current_ptr_ = find_options_start();
// Skip all fields and make this point to the first flags one
advance_to_next_field(true /* start from bit zero */);
}
}
RadioTapParser::NamespaceType RadioTapParser::current_namespace() const {
return current_namespace_;
}
uint32_t RadioTapParser::current_namespace_index() const {
return namespace_index_;
}
RadioTap::PresentFlags RadioTapParser::current_field() const {
return static_cast<RadioTap::PresentFlags>(1 << current_bit_);
}
RadioTapParser::option RadioTapParser::current_option() {
const uint32_t size = RADIOTAP_METADATA[current_bit_].size;
if (TINS_UNLIKELY(current_ptr_ + size > end_)) {
throw malformed_packet();
}
return option(current_field(), size, current_ptr_);
}
const uint8_t* RadioTapParser::current_option_ptr() const {
return current_ptr_;
}
bool RadioTapParser::advance_field() {
// If we have no buffer to parse, then we can't advance
if (start_ == 0 || current_bit_ == MAX_RADIOTAP_FIELD) {
return false;
}
// If we manage to advance the field, return true
if (advance_to_next_field(false /* keep going from current */)) {
return true;
}
// We finished iterating the current namespace (if any). Reset our bit
current_bit_ = MAX_RADIOTAP_FIELD;
// Otherwise, let's try advancing the namespace. If we fail, then we failed
if (!advance_to_next_namespace()) {
return false;
}
// Try to find the first field in this new namespace
return advance_to_next_field(true /* start from 0*/);
}
bool RadioTapParser::skip_to_field(RadioTap::PresentFlags flag) {
while (has_fields() && current_field() != flag) {
advance_field();
}
return has_fields();
}
bool RadioTapParser::has_fields() const {
return current_bit_ != MAX_RADIOTAP_FIELD;
}
bool RadioTapParser::has_field(RadioTap::PresentFlags flag) const {
const uint8_t* ptr = start_;
while (ptr + sizeof(uint32_t) < end_) {
const RadioTapFlags* flags = (const RadioTapFlags*)ptr;
if (is_field_set(flag, flags)) {
return true;
}
if (!flags->ext) {
break;
}
// Jump to the next flags field
ptr += sizeof(uint32_t);
}
return false;
}
const uint8_t* RadioTapParser::find_options_start() const {
uint32_t total_sz = end_ - start_;
if (TINS_UNLIKELY(total_sz < sizeof(RadioTapFlags))) {
throw malformed_packet();
}
// Skip fields before the flags one
const RadioTapFlags* flags = get_flags_ptr();
while (flags->ext == 1) {
if (TINS_UNLIKELY(total_sz < sizeof(RadioTapFlags))) {
throw malformed_packet();
}
++flags;
total_sz -= sizeof(RadioTapFlags);
}
return reinterpret_cast<const uint8_t*>(flags) + sizeof(RadioTapFlags);
}
bool RadioTapParser::advance_to_next_field(bool start_from_zero) {
const RadioTapFlags* flags = get_flags_ptr();
uint32_t bit;
if (start_from_zero) {
bit = 0;
}
else {
// Skip the payload
current_ptr_ += RADIOTAP_METADATA[current_bit_].size;
bit = current_bit_ + 1;
}
while (!is_field_set(1 << bit, flags) && bit < MAX_RADIOTAP_FIELD) {
bit++;
}
if (bit < MAX_RADIOTAP_FIELD) {
const uint8_t* radiotap_start = start_ - sizeof(uint32_t);
// Skip and align the buffer
align_buffer(radiotap_start, current_ptr_, end_ - radiotap_start,
RADIOTAP_METADATA[bit].alignment);
current_bit_ = bit;
return true;
}
return false;
}
bool RadioTapParser::advance_to_next_namespace() {
const uint32_t initial_index = namespace_index_;
while (get_flags_ptr()->ext == 1) {
const RadioTapFlags* flags = get_flags_ptr();
if (is_field_set(29, flags)) {
current_namespace_ = RADIOTAP_NS;
}
else if (is_field_set(30, flags)) {
current_namespace_ = VENDOR_NS;
}
else {
current_namespace_ = UNKNOWN_NS;
}
namespace_index_++;
}
return initial_index != namespace_index_;
}
bool RadioTapParser::is_field_set(uint32_t bit, const RadioTapFlags* flags) const {
TINS_BEGIN_PACK
union FlagsUnion {
RadioTapFlags* flags;
uint32_t flags_32;
} TINS_END_PACK;
const FlagsUnion* flags_union = reinterpret_cast<const FlagsUnion*>(flags);
return (Endian::le_to_host(flags_union->flags_32) & bit) != 0;
}
const RadioTapFlags* RadioTapParser::get_flags_ptr() const {
return (const RadioTapFlags*)(start_ + sizeof(uint32_t) * namespace_index_);
}
} // Utils
} // Tins