blob: 4a05da0fdddb1d74410b9887b790d5f271fa0066 [file] [log] [blame]
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "hci/pcap_filter.h"
#include <packet_runtime.h>
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <memory>
#include <utility>
#include <vector>
#include "log.h"
#include "packets/hci_packets.h"
using namespace bluetooth::hci;
namespace rootcanal {
static pdl::packet::slice create_packet_view(
std::vector<uint8_t> const& packet) {
// Wrap the reference to the packet in a shared_ptr with created
// a no-op deleter. The packet view will be short lived so there is no
// risk of the reference leaking.
return pdl::packet::slice(std::shared_ptr<std::vector<uint8_t> const>(
&packet, [](std::vector<uint8_t> const* /* ptr */) {}));
}
static std::vector<uint8_t> FilterHciAcl(std::vector<uint8_t> const& packet);
static std::vector<uint8_t> FilterHciSco(std::vector<uint8_t> const& packet);
static std::vector<uint8_t> FilterHciIso(std::vector<uint8_t> const& packet);
std::vector<uint8_t> PcapFilter::FilterHciPacket(
std::vector<uint8_t> const& packet, uint8_t idc) {
switch (idc) {
case 0x1:
return FilterHciCommand(packet);
case 0x2:
return FilterHciAcl(packet);
case 0x3:
return FilterHciSco(packet);
case 0x4:
return FilterHciEvent(packet);
case 0x5:
return FilterHciIso(packet);
default:
break;
}
return std::vector<uint8_t>(packet);
}
std::vector<uint8_t> PcapFilter::FilterHciCommand(
std::vector<uint8_t> const& packet) {
auto command = CommandView::Create(create_packet_view(packet));
ASSERT(command.IsValid());
switch (command.GetOpCode()) {
case OpCode::WRITE_LOCAL_NAME:
return FilterWriteLocalName(command);
case OpCode::WRITE_EXTENDED_INQUIRY_RESPONSE:
return FilterWriteExtendedInquiryResponse(command);
case OpCode::LE_SET_ADVERTISING_DATA:
return FilterLeSetAdvertisingData(command);
case OpCode::LE_SET_SCAN_RESPONSE_DATA:
return FilterLeSetScanResponseData(command);
case OpCode::LE_SET_EXTENDED_ADVERTISING_DATA:
return FilterLeSetExtendedAdvertisingData(command);
case OpCode::LE_SET_EXTENDED_SCAN_RESPONSE_DATA:
return FilterLeSetExtendedScanResponseData(command);
case OpCode::LE_SET_PERIODIC_ADVERTISING_DATA:
return FilterLeSetPeriodicAdvertisingData(command);
default:
break;
}
return std::vector<uint8_t>(packet);
}
std::vector<uint8_t> PcapFilter::FilterHciEvent(
std::vector<uint8_t> const& packet) {
auto event = EventView::Create(create_packet_view(packet));
ASSERT(event.IsValid());
switch (event.GetEventCode()) {
case EventCode::LE_META_EVENT: {
auto le_meta_event = LeMetaEventView::Create(event);
ASSERT(le_meta_event.IsValid());
switch (le_meta_event.GetSubeventCode()) {
case SubeventCode::ADVERTISING_REPORT:
return FilterLeAdvertisingReport(le_meta_event);
case SubeventCode::EXTENDED_ADVERTISING_REPORT:
return FilterLeExtendedAdvertisingReport(le_meta_event);
default:
break;
}
break;
}
case EventCode::COMMAND_COMPLETE: {
auto command_complete = CommandCompleteView::Create(event);
ASSERT(command_complete.IsValid());
switch (command_complete.GetCommandOpCode()) {
case OpCode::READ_LOCAL_NAME:
return FilterReadLocalNameComplete(command_complete);
case OpCode::READ_EXTENDED_INQUIRY_RESPONSE:
return FilterReadExtendedInquiryResponseComplete(command_complete);
default:
break;
}
break;
}
case EventCode::REMOTE_NAME_REQUEST_COMPLETE:
return FilterRemoteNameRequestComplete(event);
case EventCode::EXTENDED_INQUIRY_RESULT:
return FilterExtendedInquiryResult(event);
default:
break;
}
return std::vector<uint8_t>(packet);
}
static std::vector<uint8_t> FilterHciAcl(std::vector<uint8_t> const& packet) {
auto acl = AclView::Create(create_packet_view(packet));
std::vector<uint8_t> payload;
payload.resize(acl.GetPayload().size());
ASSERT(acl.IsValid());
return AclBuilder::Create(acl.GetHandle(), acl.GetPacketBoundaryFlag(),
acl.GetBroadcastFlag(), std::move(payload))
->SerializeToBytes();
}
static std::vector<uint8_t> FilterHciSco(std::vector<uint8_t> const& packet) {
auto sco = ScoView::Create(create_packet_view(packet));
std::vector<uint8_t> data;
data.resize(sco.GetData().size());
ASSERT(sco.IsValid());
return ScoBuilder::Create(sco.GetHandle(), sco.GetPacketStatusFlag(), data)
->SerializeToBytes();
}
static std::vector<uint8_t> FilterHciIso(std::vector<uint8_t> const& packet) {
auto iso = IsoView::Create(create_packet_view(packet));
std::vector<uint8_t> payload;
payload.resize(iso.GetPayload().size());
ASSERT(iso.IsValid());
return IsoBuilder::Create(iso.GetConnectionHandle(), iso.GetPbFlag(),
iso.GetTsFlag(), std::move(payload))
->SerializeToBytes();
}
// Replace device names in GAP entries.
// TODO: extended advertising reports can be chunked across multiple
// events, and a single GAP data entry can be segmented in two.
// The filter should account for that and keep a state for partial
// GAP entries.
void PcapFilter::FilterGapData(uint8_t* gap_data, size_t gap_data_len) {
size_t offset = 0;
while ((offset + 2) <= gap_data_len) {
size_t length = gap_data[offset];
GapDataType data_type = static_cast<GapDataType>(gap_data[offset + 1]);
// Truncated entry.
if ((offset + length + 1) > gap_data_len) {
break;
}
// Empty entry.
if (length == 0) {
offset += 1;
continue;
}
// Apply the filter to entries that contain user data.
switch (data_type) {
case GapDataType::COMPLETE_LOCAL_NAME:
case GapDataType::SHORTENED_LOCAL_NAME: {
auto start_pos = gap_data + offset + 1;
auto end_pos = gap_data + offset + length;
std::vector<uint8_t> new_name =
ChangeDeviceName(std::vector<uint8_t>{start_pos, end_pos});
std::copy(new_name.begin(), new_name.end(), start_pos);
break;
}
default:
break;
}
offset += length + 1;
}
}
void PcapFilter::FilterGapData(std::vector<uint8_t>& gap_data) {
FilterGapData(gap_data.data(), gap_data.size());
}
// Replace the local device name.
std::vector<uint8_t> PcapFilter::FilterWriteLocalName(CommandView& command) {
auto parameters = WriteLocalNameView::Create(command);
ASSERT(parameters.IsValid());
std::array<uint8_t, 248> local_name =
ChangeDeviceName(parameters.GetLocalName());
return WriteLocalNameBuilder::Create(local_name)->SerializeToBytes();
}
// Replace the device names in the GAP entries of the extended inquiry response.
std::vector<uint8_t> PcapFilter::FilterWriteExtendedInquiryResponse(
CommandView& command) {
auto parameters = WriteExtendedInquiryResponseView::Create(command);
ASSERT(parameters.IsValid());
std::array<uint8_t, 240> extended_inquiry_response =
parameters.GetExtendedInquiryResponse();
FilterGapData(extended_inquiry_response.data(),
extended_inquiry_response.size());
return WriteExtendedInquiryResponseBuilder::Create(
parameters.GetFecRequired(), extended_inquiry_response)
->SerializeToBytes();
}
// Replace the device names in the GAP entries of the advertising data.
std::vector<uint8_t> PcapFilter::FilterLeSetAdvertisingData(
CommandView& command) {
auto parameters = LeSetAdvertisingDataView::Create(command);
ASSERT(parameters.IsValid());
std::vector<uint8_t> advertising_data = parameters.GetAdvertisingData();
FilterGapData(advertising_data);
return LeSetAdvertisingDataBuilder::Create(advertising_data)
->SerializeToBytes();
}
// Replace the device names in the GAP entries of the scan response data.
std::vector<uint8_t> PcapFilter::FilterLeSetScanResponseData(
CommandView& command) {
auto parameters = LeSetScanResponseDataView::Create(command);
ASSERT(parameters.IsValid());
std::vector<uint8_t> advertising_data = parameters.GetAdvertisingData();
FilterGapData(advertising_data);
return LeSetScanResponseDataBuilder::Create(advertising_data)
->SerializeToBytes();
}
// Replace the device names in the GAP entries of the extended advertising data.
std::vector<uint8_t> PcapFilter::FilterLeSetExtendedAdvertisingData(
CommandView& command) {
auto parameters = LeSetExtendedAdvertisingDataView::Create(command);
ASSERT(parameters.IsValid());
std::vector<uint8_t> advertising_data = parameters.GetAdvertisingData();
FilterGapData(advertising_data);
return LeSetExtendedAdvertisingDataBuilder::Create(
parameters.GetAdvertisingHandle(), parameters.GetOperation(),
parameters.GetFragmentPreference(), advertising_data)
->SerializeToBytes();
}
// Replace the device names in the GAP entries of the extended scan response
// data.
std::vector<uint8_t> PcapFilter::FilterLeSetExtendedScanResponseData(
CommandView& command) {
auto parameters = LeSetExtendedScanResponseDataView::Create(command);
ASSERT(parameters.IsValid());
std::vector<uint8_t> advertising_data = parameters.GetScanResponseData();
FilterGapData(advertising_data);
return LeSetExtendedScanResponseDataBuilder::Create(
parameters.GetAdvertisingHandle(), parameters.GetOperation(),
parameters.GetFragmentPreference(), advertising_data)
->SerializeToBytes();
}
// Replace the device names in the GAP entries of the periodic advertising
// data.
std::vector<uint8_t> PcapFilter::FilterLeSetPeriodicAdvertisingData(
bluetooth::hci::CommandView& command) {
auto parameters = LeSetPeriodicAdvertisingDataView::Create(command);
ASSERT(parameters.IsValid());
std::vector<uint8_t> advertising_data = parameters.GetAdvertisingData();
FilterGapData(advertising_data);
return LeSetPeriodicAdvertisingDataBuilder::Create(
parameters.GetAdvertisingHandle(), parameters.GetOperation(),
advertising_data)
->SerializeToBytes();
}
// Replace the local device name in the read local name complete event.
std::vector<uint8_t> PcapFilter::FilterReadLocalNameComplete(
bluetooth::hci::CommandCompleteView& command_complete) {
auto parameters = ReadLocalNameCompleteView::Create(command_complete);
ASSERT(parameters.IsValid());
std::array<uint8_t, 248> local_name = parameters.GetLocalName();
if (parameters.GetStatus() == ErrorCode::SUCCESS) {
local_name = ChangeDeviceName(local_name);
}
return ReadLocalNameCompleteBuilder::Create(
parameters.GetNumHciCommandPackets(), parameters.GetStatus(),
local_name)
->SerializeToBytes();
}
// Replace the device names in the GAP entries of the extended inquiry response.
std::vector<uint8_t> PcapFilter::FilterReadExtendedInquiryResponseComplete(
bluetooth::hci::CommandCompleteView& command_complete) {
auto parameters =
ReadExtendedInquiryResponseCompleteView::Create(command_complete);
ASSERT(parameters.IsValid());
std::array<uint8_t, 240> extended_inquiry_response =
parameters.GetExtendedInquiryResponse();
if (parameters.GetStatus() == ErrorCode::SUCCESS) {
FilterGapData(extended_inquiry_response.data(),
extended_inquiry_response.size());
}
return ReadExtendedInquiryResponseCompleteBuilder::Create(
parameters.GetNumHciCommandPackets(), parameters.GetStatus(),
parameters.GetFecRequired(), extended_inquiry_response)
->SerializeToBytes();
}
// Replace the remote device name in the remote name request complete event.
std::vector<uint8_t> PcapFilter::FilterRemoteNameRequestComplete(
bluetooth::hci::EventView& event) {
auto parameters = RemoteNameRequestCompleteView::Create(event);
ASSERT(parameters.IsValid());
std::array<uint8_t, 248> remote_name = parameters.GetRemoteName();
if (parameters.GetStatus() == ErrorCode::SUCCESS) {
remote_name = ChangeDeviceName(remote_name);
}
return RemoteNameRequestCompleteBuilder::Create(
parameters.GetStatus(), parameters.GetBdAddr(), remote_name)
->SerializeToBytes();
}
// Replace the device names in the GAP entries in the extended inquiry result.
std::vector<uint8_t> PcapFilter::FilterExtendedInquiryResult(
bluetooth::hci::EventView& event) {
auto parameters = ExtendedInquiryResultView::Create(event);
ASSERT(parameters.IsValid());
std::array<uint8_t, 240> extended_inquiry_response =
parameters.GetExtendedInquiryResponse();
FilterGapData(extended_inquiry_response.data(),
extended_inquiry_response.size());
return ExtendedInquiryResultBuilder::Create(
parameters.GetAddress(), parameters.GetPageScanRepetitionMode(),
parameters.GetClassOfDevice(), parameters.GetClockOffset(),
parameters.GetRssi(), extended_inquiry_response)
->SerializeToBytes();
}
// Replace the device names in the GAP entries in the advertising report.
std::vector<uint8_t> PcapFilter::FilterLeAdvertisingReport(
bluetooth::hci::LeMetaEventView& event) {
auto parameters = LeAdvertisingReportView::Create(event);
ASSERT(parameters.IsValid());
std::vector<LeAdvertisingResponse> responses = parameters.GetResponses();
for (auto& response : responses) {
FilterGapData(response.advertising_data_);
}
return LeAdvertisingReportBuilder::Create(responses)->SerializeToBytes();
}
// Replace the device names in the GAP entries in the extended advertising
// report.
std::vector<uint8_t> PcapFilter::FilterLeExtendedAdvertisingReport(
bluetooth::hci::LeMetaEventView& event) {
auto parameters = LeExtendedAdvertisingReportView::Create(event);
ASSERT(parameters.IsValid());
std::vector<LeExtendedAdvertisingResponse> responses =
parameters.GetResponses();
for (auto& response : responses) {
FilterGapData(response.advertising_data_);
}
return LeExtendedAdvertisingReportBuilder::Create(responses)
->SerializeToBytes();
}
// Generate a device name of the specified length.
// device_nr is a unique identifier used for the generation.
// padded indicates if the name should be padded to length with
// spaces.
static std::vector<uint8_t> generate_device_name(size_t device_nr,
size_t device_name_len,
bool padded) {
std::vector<uint8_t> output;
output.resize(device_name_len + 1);
int written_len = std::snprintf(reinterpret_cast<char*>(output.data()),
output.size(), "#%02zu device", device_nr);
// Remove the null terminator, not used for the device name
// since it is framed in most cases.
output.resize(device_name_len);
// Pad the device name with spaces.
if (padded && written_len >= 0 && written_len < (int)output.size()) {
std::memset(&output[written_len], ' ', output.size() - written_len);
}
return output;
}
std::vector<uint8_t> PcapFilter::ChangeDeviceName(
std::vector<uint8_t> const& device_name) {
for (auto const& [old_device_name, new_device_name] : device_name_map) {
if (old_device_name == device_name) {
return std::vector<uint8_t>(new_device_name);
}
}
std::vector<uint8_t> new_device_name =
generate_device_name(device_name_map.size(), device_name.size(), true);
device_name_map.push_back(std::pair{
std::vector<uint8_t>(device_name),
new_device_name,
});
return new_device_name;
}
std::array<uint8_t, 248> PcapFilter::ChangeDeviceName(
std::array<uint8_t, 248> const& device_name) {
for (auto const& [old_device_name, new_device_name] : device_name_map) {
if (std::equal(old_device_name.begin(), old_device_name.end(),
device_name.begin(), device_name.end())) {
std::array<uint8_t, 248> out_device_name{};
std::copy(new_device_name.begin(), new_device_name.end(),
out_device_name.begin());
return out_device_name;
}
}
std::vector<uint8_t> new_device_name =
generate_device_name(device_name_map.size(), device_name.size(), false);
std::array<uint8_t, 248> out_device_name{};
std::copy(new_device_name.begin(), new_device_name.end(),
out_device_name.begin());
device_name_map.push_back(std::pair{
std::vector<uint8_t>(device_name.begin(), device_name.end()),
std::move(new_device_name),
});
return out_device_name;
}
} // namespace rootcanal