Loading system/hci/include/btsnoop.h +22 −0 Original line number Diff line number Diff line Loading @@ -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); system/hci/src/btsnoop.cc +246 −46 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <mutex> #include <arpa/inet.h> #include <base/logging.h> #include <errno.h> #include <fcntl.h> #include <inttypes.h> Loading @@ -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 Loading @@ -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" Loading @@ -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); Loading @@ -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; } Loading @@ -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; Loading @@ -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() { Loading @@ -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; } Loading @@ -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; Loading @@ -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); Loading system/stack/avct/avct_api.cc +4 −2 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 */ Loading system/stack/avdt/avdt_api.cc +2 −1 Original line number Diff line number Diff line Loading @@ -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, Loading system/stack/bnep/bnep_main.cc +2 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
system/hci/include/btsnoop.h +22 −0 Original line number Diff line number Diff line Loading @@ -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);
system/hci/src/btsnoop.cc +246 −46 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <mutex> #include <arpa/inet.h> #include <base/logging.h> #include <errno.h> #include <fcntl.h> #include <inttypes.h> Loading @@ -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 Loading @@ -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" Loading @@ -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); Loading @@ -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; } Loading @@ -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; Loading @@ -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() { Loading @@ -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; } Loading @@ -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; Loading @@ -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); Loading
system/stack/avct/avct_api.cc +4 −2 Original line number Diff line number Diff line Loading @@ -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, Loading @@ -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 */ Loading
system/stack/avdt/avdt_api.cc +2 −1 Original line number Diff line number Diff line Loading @@ -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, Loading
system/stack/bnep/bnep_main.cc +2 −1 Original line number Diff line number Diff line Loading @@ -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