1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "hal/snoop_logger.h" 18 19 #include <arpa/inet.h> 20 #include <netinet/in.h> 21 #include <bitset> 22 #include <chrono> 23 24 #include "os/log.h" 25 26 namespace bluetooth { 27 namespace hal { 28 29 namespace { 30 typedef struct { 31 uint32_t length_original; 32 uint32_t length_captured; 33 uint32_t flags; 34 uint32_t dropped_packets; 35 uint64_t timestamp; 36 uint8_t type; 37 } __attribute__((__packed__)) btsnoop_packet_header_t; 38 39 typedef struct { 40 uint8_t identification_pattern[8]; 41 uint32_t version_number; 42 uint32_t datalink_type; 43 } __attribute__((__packed__)) btsnoop_file_header_t; 44 45 constexpr uint64_t BTSNOOP_EPOCH_DELTA = 0x00dcddb30f2f8000ULL; 46 47 constexpr uint32_t kBytesToTest = 0x12345678; 48 constexpr uint8_t kFirstByte = (const uint8_t&)kBytesToTest; 49 constexpr bool isLittleEndian = kFirstByte == 0x78; 50 constexpr bool isBigEndian = kFirstByte == 0x12; 51 static_assert(isLittleEndian || isBigEndian && isLittleEndian != isBigEndian); 52 53 constexpr uint32_t BTSNOOP_VERSION_NUMBER = isLittleEndian ? 0x01000000 : 1; 54 constexpr uint32_t BTSNOOP_DATALINK_TYPE = 55 isLittleEndian ? 0xea030000 : 0x03ea; // Datalink Type code for HCI UART (H4) is 1002 56 uint64_t htonll(uint64_t ll) { 57 if constexpr (isLittleEndian) { 58 return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32); 59 } else { 60 return ll; 61 } 62 } 63 64 constexpr btsnoop_file_header_t BTSNOOP_FILE_HEADER = { 65 .identification_pattern = {'b', 't', 's', 'n', 'o', 'o', 'p', 0x00}, 66 .version_number = BTSNOOP_VERSION_NUMBER, 67 .datalink_type = BTSNOOP_DATALINK_TYPE}; 68 } // namespace 69 70 SnoopLogger::SnoopLogger() { 71 bool file_exists; 72 { 73 std::ifstream btsnoop_istream(file_path); 74 file_exists = btsnoop_istream.is_open(); 75 } 76 btsnoop_ostream_.open(file_path, std::ios::binary | std::ios::app | std::ios::out); 77 if (!file_exists) { 78 LOG_INFO("Creating new BTSNOOP"); 79 btsnoop_ostream_.write(reinterpret_cast<const char*>(&BTSNOOP_FILE_HEADER), sizeof(btsnoop_file_header_t)); 80 } else { 81 LOG_INFO("Appending to old BTSNOOP"); 82 } 83 } 84 85 void SnoopLogger::SetFilePath(const std::string& filename) { 86 file_path = filename; 87 } 88 89 void SnoopLogger::capture(const HciPacket& packet, Direction direction, PacketType type) { 90 uint64_t timestamp_us = 91 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()) 92 .count(); 93 std::lock_guard<std::mutex> lock(file_mutex_); 94 std::bitset<32> flags = 0; 95 switch (type) { 96 case PacketType::CMD: 97 flags.set(0, false); 98 flags.set(1, true); 99 break; 100 case PacketType::ACL: 101 flags.set(0, direction == Direction::INCOMING); 102 flags.set(1, false); 103 break; 104 case PacketType::SCO: 105 flags.set(0, direction == Direction::INCOMING); 106 flags.set(1, false); 107 break; 108 case PacketType::EVT: 109 flags.set(0, true); 110 flags.set(1, true); 111 break; 112 } 113 uint32_t length = packet.size() + /* type byte */ 1; 114 btsnoop_packet_header_t header = {.length_original = htonl(length), 115 .length_captured = htonl(length), 116 .flags = htonl(static_cast<uint32_t>(flags.to_ulong())), 117 .dropped_packets = 0, 118 .timestamp = htonll(timestamp_us + BTSNOOP_EPOCH_DELTA), 119 .type = static_cast<uint8_t>(type)}; 120 btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(btsnoop_packet_header_t)); 121 btsnoop_ostream_.write(reinterpret_cast<const char*>(packet.data()), packet.size()); 122 if (AlwaysFlush) btsnoop_ostream_.flush(); 123 } 124 125 void SnoopLogger::ListDependencies(ModuleList* list) { 126 // We have no dependencies 127 } 128 129 void SnoopLogger::Start() {} 130 131 void SnoopLogger::Stop() {} 132 133 std::string SnoopLogger::file_path = SnoopLogger::DefaultFilePath; 134 135 const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() { 136 return new SnoopLogger(); 137 }); 138 139 } // namespace hal 140 } // namespace bluetooth 141