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 
22 #include <bitset>
23 #include <chrono>
24 
25 #include "os/log.h"
26 
27 namespace bluetooth {
28 namespace hal {
29 
30 namespace {
31 typedef struct {
32   uint32_t length_original;
33   uint32_t length_captured;
34   uint32_t flags;
35   uint32_t dropped_packets;
36   uint64_t timestamp;
37   uint8_t type;
38 } __attribute__((__packed__)) btsnoop_packet_header_t;
39 
40 typedef struct {
41   uint8_t identification_pattern[8];
42   uint32_t version_number;
43   uint32_t datalink_type;
44 } __attribute__((__packed__)) btsnoop_file_header_t;
45 
46 constexpr uint64_t BTSNOOP_EPOCH_DELTA = 0x00dcddb30f2f8000ULL;
47 
48 constexpr uint32_t kBytesToTest = 0x12345678;
49 constexpr uint8_t kFirstByte = (const uint8_t&)kBytesToTest;
50 constexpr bool isLittleEndian = kFirstByte == 0x78;
51 constexpr bool isBigEndian = kFirstByte == 0x12;
52 static_assert(isLittleEndian || isBigEndian && isLittleEndian != isBigEndian);
53 
54 constexpr uint32_t BTSNOOP_VERSION_NUMBER = isLittleEndian ? 0x01000000 : 1;
55 constexpr uint32_t BTSNOOP_DATALINK_TYPE =
56     isLittleEndian ? 0xea030000 : 0x03ea;  // Datalink Type code for HCI UART (H4) is 1002
htonll(uint64_t ll)57 uint64_t htonll(uint64_t ll) {
58   if constexpr (isLittleEndian) {
59     return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
60   } else {
61     return ll;
62   }
63 }
64 
65 constexpr btsnoop_file_header_t BTSNOOP_FILE_HEADER = {
66     .identification_pattern = {'b', 't', 's', 'n', 'o', 'o', 'p', 0x00},
67     .version_number = BTSNOOP_VERSION_NUMBER,
68     .datalink_type = BTSNOOP_DATALINK_TYPE};
69 }  // namespace
70 
SnoopLogger()71 SnoopLogger::SnoopLogger() {
72   bool file_exists;
73   {
74     std::ifstream btsnoop_istream(file_path);
75     file_exists = btsnoop_istream.is_open();
76   }
77   btsnoop_ostream_.open(file_path, std::ios::binary | std::ios::app | std::ios::out);
78   if (!file_exists) {
79     LOG_INFO("Creating new BTSNOOP");
80     btsnoop_ostream_.write(reinterpret_cast<const char*>(&BTSNOOP_FILE_HEADER), sizeof(btsnoop_file_header_t));
81   } else {
82     LOG_INFO("Appending to old BTSNOOP");
83   }
84 }
85 
SetFilePath(const std::string & filename)86 void SnoopLogger::SetFilePath(const std::string& filename) {
87   file_path = filename;
88 }
89 
capture(const HciPacket & packet,Direction direction,PacketType type)90 void SnoopLogger::capture(const HciPacket& packet, Direction direction, PacketType type) {
91   uint64_t timestamp_us =
92       std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
93           .count();
94   std::lock_guard<std::mutex> lock(file_mutex_);
95   std::bitset<32> flags = 0;
96   switch (type) {
97     case PacketType::CMD:
98       flags.set(0, false);
99       flags.set(1, true);
100       break;
101     case PacketType::ACL:
102       flags.set(0, direction == Direction::INCOMING);
103       flags.set(1, false);
104       break;
105     case PacketType::SCO:
106       flags.set(0, direction == Direction::INCOMING);
107       flags.set(1, false);
108       break;
109     case PacketType::EVT:
110       flags.set(0, true);
111       flags.set(1, true);
112       break;
113   }
114   uint32_t length = packet.size() + /* type byte */ 1;
115   btsnoop_packet_header_t header = {.length_original = htonl(length),
116                                     .length_captured = htonl(length),
117                                     .flags = htonl(static_cast<uint32_t>(flags.to_ulong())),
118                                     .dropped_packets = 0,
119                                     .timestamp = htonll(timestamp_us + BTSNOOP_EPOCH_DELTA),
120                                     .type = static_cast<uint8_t>(type)};
121   btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(btsnoop_packet_header_t));
122   btsnoop_ostream_.write(reinterpret_cast<const char*>(packet.data()), packet.size());
123   if (AlwaysFlush) btsnoop_ostream_.flush();
124 }
125 
ListDependencies(ModuleList * list)126 void SnoopLogger::ListDependencies(ModuleList* list) {
127   // We have no dependencies
128 }
129 
Start()130 void SnoopLogger::Start() {}
131 
Stop()132 void SnoopLogger::Stop() {}
133 
134 std::string SnoopLogger::file_path = SnoopLogger::DefaultFilePath;
135 
__anon726fad980402() 136 const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() { return new SnoopLogger(); });
137 
138 }  // namespace hal
139 }  // namespace bluetooth
140