mirror of
https://github.com/mfontanini/libtins
synced 2026-01-23 10:45:57 +01:00
301 lines
8.6 KiB
C++
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
|