Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91c25d06 authored by Ajay Panicker's avatar Ajay Panicker Committed by Android (Google) Code Review
Browse files

Merge "Add Filtering for snoop logs based on L2CAP and RFCOMM Channels (1/4)"

parents f55de723 76d59dd1
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -29,6 +29,28 @@ typedef struct btsnoop_t {
  // true, the packet is marked as incoming. Otherwise, the packet is marked
  // as outgoing.
  void (*capture)(const BT_HDR* packet, bool is_received);

  // Set a L2CAP channel as whitelisted, allowing packets with that L2CAP CID
  // to show up in the snoop logs.
  void (*whitelist_l2c_channel)(uint16_t conn_handle, uint16_t local_cid,
                                uint16_t remote_cid);

  // Set a RFCOMM dlci as whitelisted, allowing packets with that RFCOMM CID
  // to show up in the snoop logs. The local_cid is used to associate it with
  // its corrisponding ACL connection. The dlci is the channel with direction
  // so there is no chance of a collision if two services are using the same
  // channel but in different directions.
  void (*whitelist_rfc_dlci)(uint16_t local_cid, uint8_t dlci);

  // Indicate that the provided L2CAP channel is being used for RFCOMM.
  // If packets with the provided L2CAP CID are encountered, they will be
  // filtered on RFCOMM based on channels provided to |filter_rfc_channel|.
  void (*add_rfc_l2c_channel)(uint16_t conn_handle, uint16_t local_cid,
                              uint16_t remote_cid);

  // Clear an L2CAP channel from being filtered.
  void (*clear_l2cap_whitelist)(uint16_t conn_handle, uint16_t local_cid,
                                uint16_t remote_cid);
} btsnoop_t;

const btsnoop_t* btsnoop_get_interface(void);
+246 −46
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <mutex>

#include <arpa/inet.h>
#include <base/logging.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -34,14 +35,21 @@
#include <sys/time.h>
#include <sys/uio.h>
#include <unistd.h>
#include <mutex>
#include <unordered_map>
#include <unordered_set>

#include "bt_types.h"
#include "common/time_util.h"
#include "hci/include/btsnoop.h"
#include "hci/include/btsnoop_mem.h"
#include "hci_layer.h"
#include "internal_include/bt_trace.h"
#include "osi/include/log.h"
#include "osi/include/properties.h"
#include "stack/include/hcimsgs.h"
#include "stack/include/rfcdefs.h"
#include "stack/l2cap/l2c_int.h"
#include "stack_config.h"

// The number of of packets per btsnoop file before we rotate to the next
@@ -50,7 +58,14 @@
// property
#define DEFAULT_BTSNOOP_SIZE 0xffff

#define BTSNOOP_ENABLE_PROPERTY "persist.bluetooth.btsnoopenable"
#define IS_DEBUGGABLE_PROPERTY "ro.debuggable"

#define BTSNOOP_LOG_MODE_PROPERTY "persist.bluetooth.btsnooplogmode"
#define BTSNOOP_DEFAULT_MODE_PROPERTY "persist.bluetooth.btsnoopdefaultmode"
#define BTSNOOP_MODE_DISABLED "disabled"
#define BTSNOOP_MODE_FILTERED "filtered"
#define BTSNOOP_MODE_FULL "full"

#define BTSNOOP_PATH_PROPERTY "persist.bluetooth.btsnooppath"
#define DEFAULT_BTSNOOP_PATH "/data/misc/bluetooth/logs/btsnoop_hci.log"
#define BTSNOOP_MAX_PACKETS_PROPERTY "persist.bluetooth.btsnoopsize"
@@ -65,33 +80,138 @@ typedef enum {
// Epoch in microseconds since 01/01/0000.
static const uint64_t BTSNOOP_EPOCH_DELTA = 0x00dcddb30f2f8000ULL;

// Number of bytes into a packet where you can find the value for a channel.
static const size_t ACL_CHANNEL_OFFSET = 0;
static const size_t L2C_CHANNEL_OFFSET = 6;
static const size_t RFC_CHANNEL_OFFSET = 8;
static const size_t RFC_EVENT_OFFSET = 9;

// The size of the L2CAP header. All information past this point is removed from
// a filtered packet.
static const uint32_t L2C_HEADER_SIZE = 9;

static int logfile_fd = INVALID_FD;
static std::mutex btsnoop_mutex;

static int32_t packets_per_file;
static int32_t packet_counter;

// Channel tracking variables for filtering.

// Keeps track of L2CAP channels that need to be filtered out of the snoop
// logs.
class FilterTracker {
 public:
  // NOTE: 1 is used as a static CID for L2CAP signaling
  std::unordered_set<uint16_t> l2c_local_cid = {1};
  std::unordered_set<uint16_t> l2c_remote_cid = {1};
  uint16_t rfc_local_cid = 0;
  uint16_t rfc_remote_cid = 0;
  std::unordered_set<uint16_t> rfc_channels = {0};

  // Adds L2C channel to whitelist.
  void addL2cCid(uint16_t local_cid, uint16_t remote_cid) {
    l2c_local_cid.insert(local_cid);
    l2c_remote_cid.insert(remote_cid);
  }

  // Sets L2CAP channel that RFCOMM uses.
  void setRfcCid(uint16_t local_cid, uint16_t remote_cid) {
    rfc_local_cid = local_cid;
    rfc_remote_cid = remote_cid;
  }

  // Remove L2C channel from whitelist.
  void removeL2cCid(uint16_t local_cid, uint16_t remote_cid) {
    if (rfc_local_cid == local_cid) {
      rfc_channels.clear();
      rfc_channels.insert(0);
      rfc_local_cid = 0;
      rfc_remote_cid = 0;
    }

    l2c_local_cid.erase(local_cid);
    l2c_remote_cid.erase(remote_cid);
  }

  void addRfcDlci(uint8_t channel) { rfc_channels.insert(channel); }

  bool isWhitelistedL2c(bool local, uint16_t cid) {
    const auto& set = local ? l2c_local_cid : l2c_remote_cid;
    return (set.find(cid) != set.end());
  }

  bool isRfcChannel(bool local, uint16_t cid) {
    const auto& channel = local ? rfc_local_cid : rfc_remote_cid;
    return cid == channel;
  }

  bool isWhitelistedDlci(uint8_t dlci) {
    return rfc_channels.find(dlci) != rfc_channels.end();
  }
};

std::mutex filter_list_mutex;
std::unordered_map<uint16_t, FilterTracker> filter_list;
std::unordered_map<uint16_t, uint16_t> local_cid_to_acl;

// Cached value for whether full snoop logs are enabled. So the property isn't
// checked for every packet.
static bool is_btsnoop_enabled;
static bool is_btsnoop_filtered;

// TODO(zachoverflow): merge btsnoop and btsnoop_net together
void btsnoop_net_open();
void btsnoop_net_close();
void btsnoop_net_write(const void* data, size_t length);

static void delete_btsnoop_files();
static bool is_btsnoop_enabled();
static char* get_btsnoop_log_path(char* log_path);
static char* get_btsnoop_last_log_path(char* last_log_path, char* log_path);
static void delete_btsnoop_files(bool filtered);
static std::string get_btsnoop_log_path(bool filtered);
static std::string get_btsnoop_last_log_path(std::string log_path);
static void open_next_snoop_file();
static void btsnoop_write_packet(packet_type_t type, uint8_t* packet,
                                 bool is_received, uint64_t timestamp_us);

// Module lifecycle functions

static future_t* start_up(void) {
static future_t* start_up() {
  std::array<char, PROPERTY_VALUE_MAX> property = {};
  std::lock_guard<std::mutex> lock(btsnoop_mutex);

  if (!is_btsnoop_enabled()) {
    delete_btsnoop_files();
  // Default mode is FILTERED on userdebug/eng build, DISABLED on user build.
  // It can also be overwritten by modifying the global setting.
  int is_debuggable = osi_property_get_int32(IS_DEBUGGABLE_PROPERTY, 0);
  std::string default_mode = BTSNOOP_MODE_DISABLED;
  if (is_debuggable) {
    int len = osi_property_get(BTSNOOP_DEFAULT_MODE_PROPERTY, property.data(),
                               BTSNOOP_MODE_FILTERED);
    default_mode = std::string(property.data(), len);
  }

  // Get the actual mode
  int len = osi_property_get(BTSNOOP_LOG_MODE_PROPERTY, property.data(),
                             default_mode.c_str());
  std::string btsnoop_mode(property.data(), len);

  if (btsnoop_mode == BTSNOOP_MODE_FILTERED) {
    LOG(INFO) << __func__ << ": Filtered Snoop Logs enabled";
    is_btsnoop_enabled = true;
    is_btsnoop_filtered = true;
    delete_btsnoop_files(false);
  } else if (btsnoop_mode == BTSNOOP_MODE_FULL) {
    LOG(INFO) << __func__ << ": Snoop Logs fully enabled";
    is_btsnoop_enabled = true;
    is_btsnoop_filtered = false;
    delete_btsnoop_files(true);
  } else {
    LOG(INFO) << __func__ << ": Snoop Logs disabled";
    is_btsnoop_enabled = false;
    is_btsnoop_filtered = false;
    delete_btsnoop_files(true);
    delete_btsnoop_files(false);
  }

  if (is_btsnoop_enabled) {
    open_next_snoop_file();
    packets_per_file = osi_property_get_int32(BTSNOOP_MAX_PACKETS_PROPERTY,
                                              DEFAULT_BTSNOOP_SIZE);
@@ -104,14 +224,21 @@ static future_t* start_up(void) {
static future_t* shut_down(void) {
  std::lock_guard<std::mutex> lock(btsnoop_mutex);

  if (!is_btsnoop_enabled()) {
    delete_btsnoop_files();
  if (is_btsnoop_enabled) {
    if (is_btsnoop_filtered) {
      delete_btsnoop_files(false);
    } else {
      delete_btsnoop_files(true);
    }
  } else {
    delete_btsnoop_files(true);
    delete_btsnoop_files(false);
  }

  if (logfile_fd != INVALID_FD) close(logfile_fd);
  logfile_fd = INVALID_FD;

  btsnoop_net_close();
  if (is_btsnoop_enabled) btsnoop_net_close();

  return NULL;
}
@@ -129,7 +256,12 @@ static void capture(const BT_HDR* buffer, bool is_received) {
  uint8_t* p = const_cast<uint8_t*>(buffer->data + buffer->offset);

  std::lock_guard<std::mutex> lock(btsnoop_mutex);
  uint64_t timestamp_us = bluetooth::common::time_gettimeofday_us();

  struct timespec ts_now = {};
  clock_gettime(CLOCK_REALTIME, &ts_now);
  uint64_t timestamp_us =
      ((uint64_t)ts_now.tv_sec * 1000000L) + ((uint64_t)ts_now.tv_nsec / 1000);

  btsnoop_mem_capture(buffer, timestamp_us);

  if (logfile_fd == INVALID_FD) return;
@@ -152,39 +284,75 @@ static void capture(const BT_HDR* buffer, bool is_received) {
  }
}

static const btsnoop_t interface = {capture};
static void whitelist_l2c_channel(uint16_t conn_handle, uint16_t local_cid,
                                  uint16_t remote_cid) {
  LOG(INFO) << __func__
            << ": Whitelisting l2cap channel. conn_handle=" << conn_handle
            << " cid=" << loghex(local_cid) << ":" << loghex(remote_cid);
  std::lock_guard lock(filter_list_mutex);

const btsnoop_t* btsnoop_get_interface() {
  return &interface;
  // This will create the entry if there is no associated filter with the
  // connection.
  filter_list[conn_handle].addL2cCid(local_cid, remote_cid);
}

// Internal functions
static void delete_btsnoop_files() {
  LOG_VERBOSE(LOG_TAG, "Deleting snoop log if it exists");
  char log_path[PROPERTY_VALUE_MAX];
  char last_log_path[PROPERTY_VALUE_MAX + sizeof(".last")];
  get_btsnoop_log_path(log_path);
  get_btsnoop_last_log_path(last_log_path, log_path);
  remove(log_path);
  remove(last_log_path);
static void whitelist_rfc_dlci(uint16_t local_cid, uint8_t dlci) {
  LOG(INFO) << __func__
            << ": Whitelisting rfcomm channel. L2CAP CID=" << loghex(local_cid)
            << " DLCI=" << loghex(dlci);
  std::lock_guard lock(filter_list_mutex);

  tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(nullptr, local_cid);
  filter_list[p_ccb->p_lcb->handle].addRfcDlci(dlci);
}

static bool is_btsnoop_enabled() {
  char btsnoop_enabled[PROPERTY_VALUE_MAX] = {0};
  osi_property_get(BTSNOOP_ENABLE_PROPERTY, btsnoop_enabled, "false");
  return strncmp(btsnoop_enabled, "true", 4) == 0;
static void add_rfc_l2c_channel(uint16_t conn_handle, uint16_t local_cid,
                                uint16_t remote_cid) {
  LOG(INFO) << __func__
            << ": rfcomm data going over l2cap channel. conn_handle="
            << conn_handle << " cid=" << loghex(local_cid) << ":"
            << loghex(remote_cid);
  std::lock_guard lock(filter_list_mutex);

  filter_list[conn_handle].setRfcCid(local_cid, remote_cid);
  local_cid_to_acl.insert({local_cid, conn_handle});
}

static char* get_btsnoop_log_path(char* btsnoop_path) {
static void clear_l2cap_whitelist(uint16_t conn_handle, uint16_t local_cid,
                                  uint16_t remote_cid) {
  LOG(INFO) << __func__
            << ": Clearing whitelist from l2cap channel. conn_handle="
            << conn_handle << " cid=" << local_cid << ":" << remote_cid;

  std::lock_guard lock(filter_list_mutex);
  filter_list[conn_handle].removeL2cCid(local_cid, remote_cid);
}

static const btsnoop_t interface = {capture, whitelist_l2c_channel,
                                    whitelist_rfc_dlci, add_rfc_l2c_channel,
                                    clear_l2cap_whitelist};

const btsnoop_t* btsnoop_get_interface() { return &interface; }

static void delete_btsnoop_files(bool filtered) {
  LOG(INFO) << __func__
            << ": Deleting snoop logs if they exist. filtered = " << filtered;
  auto log_path = get_btsnoop_log_path(filtered);
  remove(log_path.c_str());
  remove(get_btsnoop_last_log_path(log_path).c_str());
}

std::string get_btsnoop_log_path(bool filtered) {
  char btsnoop_path[PROPERTY_VALUE_MAX];
  osi_property_get(BTSNOOP_PATH_PROPERTY, btsnoop_path, DEFAULT_BTSNOOP_PATH);
  return btsnoop_path;
  std::string result(btsnoop_path);
  if (filtered) result = result.append(".filtered");

  return result;
}

static char* get_btsnoop_last_log_path(char* last_log_path,
                                       char* btsnoop_path) {
  snprintf(last_log_path, PROPERTY_VALUE_MAX + sizeof(".last"), "%s.last",
           btsnoop_path);
  return last_log_path;
std::string get_btsnoop_last_log_path(std::string btsnoop_path) {
  return btsnoop_path.append(".last");
}

static void open_next_snoop_file() {
@@ -195,22 +363,20 @@ static void open_next_snoop_file() {
    logfile_fd = INVALID_FD;
  }

  char log_path[PROPERTY_VALUE_MAX];
  char last_log_path[PROPERTY_VALUE_MAX + sizeof(".last")];
  get_btsnoop_log_path(log_path);
  get_btsnoop_last_log_path(last_log_path, log_path);
  auto log_path = get_btsnoop_log_path(is_btsnoop_filtered);
  auto last_log_path = get_btsnoop_last_log_path(log_path);

  if (rename(log_path, last_log_path) != 0 && errno != ENOENT)
    LOG_ERROR(LOG_TAG, "%s unable to rename '%s' to '%s': %s", __func__,
              log_path, last_log_path, strerror(errno));
  if (rename(log_path.c_str(), last_log_path.c_str()) != 0 && errno != ENOENT)
    LOG(ERROR) << __func__ << ": unable to rename '" << log_path << "' to '"
               << last_log_path << "' : " << strerror(errno);

  mode_t prevmask = umask(0);
  logfile_fd = open(log_path, O_WRONLY | O_CREAT | O_TRUNC,
  logfile_fd = open(log_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC,
                    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
  umask(prevmask);
  if (logfile_fd == INVALID_FD) {
    LOG_ERROR(LOG_TAG, "%s unable to open '%s': %s", __func__, log_path,
              strerror(errno));
    LOG(ERROR) << __func__ << ": unable to open '" << log_path
               << "' : " << strerror(errno);
    return;
  }

@@ -235,6 +401,32 @@ static uint64_t htonll(uint64_t ll) {
  return ll;
}

static bool should_filter_log(bool is_received, uint8_t* packet) {
  uint16_t acl_handle =
      HCID_GET_HANDLE((((uint16_t)packet[ACL_CHANNEL_OFFSET + 1]) << 8) +
                      packet[ACL_CHANNEL_OFFSET]);

  std::lock_guard lock(filter_list_mutex);
  auto& filters = filter_list[acl_handle];
  uint16_t l2c_channel =
      (packet[L2C_CHANNEL_OFFSET + 1] << 8) + packet[L2C_CHANNEL_OFFSET];
  if (filters.isRfcChannel(is_received, l2c_channel)) {
    uint8_t rfc_event = packet[RFC_EVENT_OFFSET] & 0b11101111;
    if (rfc_event == RFCOMM_SABME || rfc_event == RFCOMM_UA) {
      return false;
    }

    uint8_t rfc_dlci = packet[RFC_CHANNEL_OFFSET] >> 2;
    if (!filters.isWhitelistedDlci(rfc_dlci)) {
      return true;
    }
  } else if (!filters.isWhitelistedL2c(is_received, l2c_channel)) {
    return true;
  }

  return false;
}

static void btsnoop_write_packet(packet_type_t type, uint8_t* packet,
                                 bool is_received, uint64_t timestamp_us) {
  uint32_t length_he = 0;
@@ -261,7 +453,15 @@ static void btsnoop_write_packet(packet_type_t type, uint8_t* packet,

  btsnoop_header_t header;
  header.length_original = htonl(length_he);
  header.length_captured = header.length_original;

  bool blacklisted = false;
  if (is_btsnoop_filtered && type == kAclPacket) {
    blacklisted = should_filter_log(is_received, packet);
  }

  header.length_captured =
      blacklisted ? htonl(L2C_HEADER_SIZE) : header.length_original;
  if (blacklisted) length_he = L2C_HEADER_SIZE;
  header.flags = htonl(flags);
  header.dropped_packets = 0;
  header.timestamp = htonll(timestamp_us + BTSNOOP_EPOCH_DELTA);
+4 −2
Original line number Diff line number Diff line
@@ -56,7 +56,8 @@ void AVCT_Register(uint16_t mtu, UNUSED_ATTR uint16_t mtu_br,
  AVCT_TRACE_API("AVCT_Register");

  /* register PSM with L2CAP */
  L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_appl);
  L2CA_Register(AVCT_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_appl,
                true /* enable_snoop */);

  /* set security level */
  BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVCTP, sec_mask, AVCT_PSM, 0,
@@ -68,7 +69,8 @@ void AVCT_Register(uint16_t mtu, UNUSED_ATTR uint16_t mtu_br,
  memset(&avct_cb, 0, sizeof(tAVCT_CB));

  /* Include the browsing channel which uses eFCR */
  L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_br_appl);
  L2CA_Register(AVCT_BR_PSM, (tL2CAP_APPL_INFO*)&avct_l2c_br_appl,
                true /*enable_snoop*/);

  /* AVCTP browsing channel uses the same security service as AVCTP control
   * channel */
+2 −1
Original line number Diff line number Diff line
@@ -90,7 +90,8 @@ void avdt_scb_transport_channel_timer_timeout(void* data) {
 ******************************************************************************/
void AVDT_Register(AvdtpRcb* p_reg, tAVDT_CTRL_CBACK* p_cback) {
  /* register PSM with L2CAP */
  L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO*)&avdt_l2c_appl);
  L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO*)&avdt_l2c_appl,
                true /* enable_snoop */);

  /* set security level */
  BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
+2 −1
Original line number Diff line number Diff line
@@ -94,7 +94,8 @@ tBNEP_RESULT bnep_register_with_l2cap(void) {
  bnep_cb.reg_info.pL2CA_CongestionStatus_Cb = bnep_congestion_ind;

  /* Now, register with L2CAP */
  if (!L2CA_Register(BT_PSM_BNEP, &bnep_cb.reg_info)) {
  if (!L2CA_Register(BT_PSM_BNEP, &bnep_cb.reg_info,
                     false /* enable_snoop */)) {
    BNEP_TRACE_ERROR("BNEP - Registration failed");
    return BNEP_SECURITY_FAIL;
  }
Loading