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 <sys/stat.h>
21
22 #include <algorithm>
23 #include <bitset>
24 #include <chrono>
25 #include <sstream>
26
27 #include "common/circular_buffer.h"
28 #include "common/init_flags.h"
29 #include "common/strings.h"
30 #include "os/files.h"
31 #include "os/log.h"
32 #include "os/parameter_provider.h"
33 #include "os/system_properties.h"
34
35 namespace bluetooth {
36 namespace hal {
37
38 namespace {
39
40 // Epoch in microseconds since 01/01/0000.
41 constexpr uint64_t kBtSnoopEpochDelta = 0x00dcddb30f2f8000ULL;
42
43 constexpr uint32_t kBytesToTest = 0x12345678;
44 constexpr uint8_t kFirstByte = (const uint8_t&)kBytesToTest;
45 constexpr bool isLittleEndian = kFirstByte == 0x78;
46 constexpr bool isBigEndian = kFirstByte == 0x12;
47 static_assert(isLittleEndian || isBigEndian && isLittleEndian != isBigEndian);
48
49 constexpr uint32_t BTSNOOP_VERSION_NUMBER = isLittleEndian ? 0x01000000 : 1;
50 constexpr uint32_t BTSNOOP_DATALINK_TYPE =
51 isLittleEndian ? 0xea030000 : 0x03ea; // Datalink Type code for HCI UART (H4) is 1002
htonll(uint64_t ll)52 uint64_t htonll(uint64_t ll) {
53 if constexpr (isLittleEndian) {
54 return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32);
55 } else {
56 return ll;
57 }
58 }
59
60 constexpr SnoopLogger::FileHeaderType kBtSnoopFileHeader = {
61 .identification_pattern = {'b', 't', 's', 'n', 'o', 'o', 'p', 0x00},
62 .version_number = BTSNOOP_VERSION_NUMBER,
63 .datalink_type = BTSNOOP_DATALINK_TYPE};
64
65 // The number of packets per btsnoop file before we rotate to the next file. As of right now there
66 // are two snoop files that are rotated through. The size can be dynamically configured by setting
67 // the relevant system property
68 constexpr size_t kDefaultBtSnoopMaxPacketsPerFile = 0xffff;
69
70 // We want to use at most 256 KB memory for btsnooz log
71 constexpr size_t kDefaultBtsnoozMaxMemoryUsageBytes = 256 * 1024;
72 // We restrict the maximum packet size to 150 bytes
73 constexpr size_t kDefaultBtSnoozMaxBytesPerPacket = 150;
74 constexpr size_t kDefaultBtSnoozMaxPayloadBytesPerPacket =
75 kDefaultBtSnoozMaxBytesPerPacket - sizeof(SnoopLogger::PacketHeaderType);
76 // Calculate max number of packets based on max memory usage and max packet size
77 constexpr size_t kDefaultBtSnoozMaxPacketsPerBuffer =
78 kDefaultBtsnoozMaxMemoryUsageBytes / kDefaultBtSnoozMaxBytesPerPacket;
79
get_btsnoop_log_path(std::string log_dir,bool filtered)80 std::string get_btsnoop_log_path(std::string log_dir, bool filtered) {
81 if (filtered) {
82 log_dir.append(".filtered");
83 }
84 return log_dir;
85 }
86
get_last_log_path(std::string log_file_path)87 std::string get_last_log_path(std::string log_file_path) {
88 return log_file_path.append(".last");
89 }
90
delete_btsnoop_files(const std::string & log_path)91 void delete_btsnoop_files(const std::string& log_path) {
92 LOG_INFO("Deleting logs if they exist");
93 if (os::FileExists(log_path)) {
94 if (!os::RemoveFile(log_path)) {
95 LOG_ERROR("Failed to remove main log file at \"%s\"", log_path.c_str());
96 }
97 } else {
98 LOG_INFO("Main log file does not exist at \"%s\"", log_path.c_str());
99 }
100 auto last_log_path = get_last_log_path(log_path);
101 if (os::FileExists(last_log_path)) {
102 if (!os::RemoveFile(last_log_path)) {
103 LOG_ERROR("Failed to remove last log file at \"%s\"", log_path.c_str());
104 }
105 } else {
106 LOG_INFO("Last log file does not exist at \"%s\"", log_path.c_str());
107 }
108 }
109
get_btsnooz_packet_length_to_write(const HciPacket & packet,SnoopLogger::PacketType type)110 size_t get_btsnooz_packet_length_to_write(const HciPacket& packet, SnoopLogger::PacketType type) {
111 static const size_t kAclHeaderSize = 4;
112 static const size_t kL2capHeaderSize = 4;
113 static const size_t kL2capCidOffset = (kAclHeaderSize + 2);
114 static const uint16_t kL2capSignalingCid = 0x0001;
115
116 // Maximum amount of ACL data to log.
117 // Enough for an RFCOMM frame up to the frame check;
118 // not enough for a HID report or audio data.
119 static const size_t kMaxBtsnoozAclSize = 14;
120
121 // Calculate packet length to be included
122 size_t included_length = 0;
123 switch (type) {
124 case SnoopLogger::PacketType::CMD:
125 case SnoopLogger::PacketType::EVT:
126 included_length = packet.size();
127 break;
128
129 case SnoopLogger::PacketType::ACL: {
130 // Log ACL and L2CAP header by default
131 size_t len_hci_acl = kAclHeaderSize + kL2capHeaderSize;
132 // Check if we have enough data for an L2CAP header
133 if (packet.size() > len_hci_acl) {
134 uint16_t l2cap_cid =
135 static_cast<uint16_t>(packet[kL2capCidOffset]) |
136 static_cast<uint16_t>((static_cast<uint16_t>(packet[kL2capCidOffset + 1]) << static_cast<uint16_t>(8)));
137 if (l2cap_cid == kL2capSignalingCid) {
138 // For the signaling CID, take the full packet.
139 // That way, the PSM setup is captured, allowing decoding of PSMs down
140 // the road.
141 return packet.size();
142 } else {
143 // Otherwise, return as much as we reasonably can
144 len_hci_acl = kMaxBtsnoozAclSize;
145 }
146 }
147 included_length = std::min(len_hci_acl, packet.size());
148 break;
149 }
150
151 case SnoopLogger::PacketType::ISO:
152 case SnoopLogger::PacketType::SCO:
153 default:
154 // We are not logging SCO and ISO packets in snooz log as they may contain voice data
155 break;
156 }
157 return std::min(included_length, kDefaultBtSnoozMaxPayloadBytesPerPacket);
158 }
159
160 } // namespace
161
162 const std::string SnoopLogger::kBtSnoopLogModeDisabled = "disabled";
163 const std::string SnoopLogger::kBtSnoopLogModeFiltered = "filtered";
164 const std::string SnoopLogger::kBtSnoopLogModeFull = "full";
165
166 const std::string SnoopLogger::kBtSnoopMaxPacketsPerFileProperty = "persist.bluetooth.btsnoopsize";
167 const std::string SnoopLogger::kIsDebuggableProperty = "ro.debuggable";
168 const std::string SnoopLogger::kBtSnoopLogModeProperty = "persist.bluetooth.btsnooplogmode";
169 const std::string SnoopLogger::kBtSnoopDefaultLogModeProperty = "persist.bluetooth.btsnoopdefaultmode";
170
SnoopLogger(std::string snoop_log_path,std::string snooz_log_path,size_t max_packets_per_file,const std::string & btsnoop_mode)171 SnoopLogger::SnoopLogger(
172 std::string snoop_log_path,
173 std::string snooz_log_path,
174 size_t max_packets_per_file,
175 const std::string& btsnoop_mode)
176 : snoop_log_path_(std::move(snoop_log_path)),
177 snooz_log_path_(std::move(snooz_log_path)),
178 max_packets_per_file_(max_packets_per_file),
179 btsnooz_buffer_(kDefaultBtSnoozMaxPacketsPerBuffer) {
180 if (false && btsnoop_mode == kBtSnoopLogModeFiltered) {
181 // TODO(b/163733538): implement filtered snoop log in GD, currently filtered == disabled
182 LOG_INFO("Filtered Snoop Logs enabled");
183 is_enabled_ = true;
184 is_filtered_ = true;
185 // delete unfiltered logs
186 delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, false));
187 // delete snooz logs
188 delete_btsnoop_files(snooz_log_path_);
189 } else if (btsnoop_mode == kBtSnoopLogModeFull) {
190 LOG_INFO("Snoop Logs fully enabled");
191 is_enabled_ = true;
192 is_filtered_ = false;
193 // delete filtered logs
194 delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, true));
195 // delete snooz logs
196 delete_btsnoop_files(snooz_log_path_);
197 } else {
198 LOG_INFO("Snoop Logs disabled");
199 is_enabled_ = false;
200 is_filtered_ = false;
201 // delete both filtered and unfiltered logs
202 delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, true));
203 delete_btsnoop_files(get_btsnoop_log_path(snoop_log_path_, false));
204 }
205 // Add ".filtered" extension if necessary
206 snoop_log_path_ = get_btsnoop_log_path(snoop_log_path_, is_filtered_);
207 }
208
CloseCurrentSnoopLogFile()209 void SnoopLogger::CloseCurrentSnoopLogFile() {
210 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
211 if (btsnoop_ostream_.is_open()) {
212 btsnoop_ostream_.flush();
213 btsnoop_ostream_.close();
214 }
215 packet_counter_ = 0;
216 }
217
OpenNextSnoopLogFile()218 void SnoopLogger::OpenNextSnoopLogFile() {
219 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
220 CloseCurrentSnoopLogFile();
221
222 auto last_file_path = get_last_log_path(snoop_log_path_);
223
224 if (os::FileExists(snoop_log_path_)) {
225 if (!os::RenameFile(snoop_log_path_, last_file_path)) {
226 LOG_ERROR(
227 "Unabled to rename existing snoop log from \"%s\" to \"%s\"",
228 snoop_log_path_.c_str(),
229 last_file_path.c_str());
230 }
231 } else {
232 LOG_INFO("Previous log file \"%s\" does not exist, skip renaming", snoop_log_path_.c_str());
233 }
234
235 mode_t prevmask = umask(0);
236 // do not use std::ios::app as we want override the existing file
237 btsnoop_ostream_.open(snoop_log_path_, std::ios::binary | std::ios::out);
238 if (!btsnoop_ostream_.good()) {
239 LOG_ALWAYS_FATAL("Unable to open snoop log at \"%s\", error: \"%s\"", snoop_log_path_.c_str(), strerror(errno));
240 }
241 umask(prevmask);
242 if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(&kBtSnoopFileHeader), sizeof(FileHeaderType))) {
243 LOG_ALWAYS_FATAL("Unable to write file header to \"%s\", error: \"%s\"", snoop_log_path_.c_str(), strerror(errno));
244 }
245 if (!btsnoop_ostream_.flush()) {
246 LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
247 }
248 }
249
Capture(const HciPacket & packet,Direction direction,PacketType type)250 void SnoopLogger::Capture(const HciPacket& packet, Direction direction, PacketType type) {
251 uint64_t timestamp_us =
252 std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch())
253 .count();
254 std::bitset<32> flags = 0;
255 switch (type) {
256 case PacketType::CMD:
257 flags.set(0, false);
258 flags.set(1, true);
259 break;
260 case PacketType::ACL:
261 case PacketType::SCO:
262 flags.set(0, direction == Direction::INCOMING);
263 flags.set(1, false);
264 break;
265 case PacketType::EVT:
266 flags.set(0, true);
267 flags.set(1, true);
268 break;
269 case PacketType::ISO:
270 flags.set(0, direction == Direction::INCOMING);
271 flags.set(1, true);
272 break;
273 }
274 uint32_t length = packet.size() + /* type byte */ 1;
275 PacketHeaderType header = {.length_original = htonl(length),
276 .length_captured = htonl(length),
277 .flags = htonl(static_cast<uint32_t>(flags.to_ulong())),
278 .dropped_packets = 0,
279 .timestamp = htonll(timestamp_us + kBtSnoopEpochDelta),
280 .type = static_cast<uint8_t>(type)};
281 {
282 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
283 if (!is_enabled_) {
284 // btsnoop disabled, log in-memory btsnooz log only
285 std::stringstream ss;
286 size_t included_length = get_btsnooz_packet_length_to_write(packet, type);
287 header.length_captured = htonl(included_length + /* type byte */ 1);
288 if (!ss.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
289 LOG_ERROR("Failed to write packet header for btsnooz, error: \"%s\"", strerror(errno));
290 }
291 if (!ss.write(reinterpret_cast<const char*>(packet.data()), included_length)) {
292 LOG_ERROR("Failed to write packet payload for btsnooz, error: \"%s\"", strerror(errno));
293 }
294 btsnooz_buffer_.Push(ss.str());
295 return;
296 }
297 packet_counter_++;
298 if (packet_counter_ > max_packets_per_file_) {
299 OpenNextSnoopLogFile();
300 }
301 if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(&header), sizeof(PacketHeaderType))) {
302 LOG_ERROR("Failed to write packet header for btsnoop, error: \"%s\"", strerror(errno));
303 }
304 if (!btsnoop_ostream_.write(reinterpret_cast<const char*>(packet.data()), packet.size())) {
305 LOG_ERROR("Failed to write packet payload for btsnoop, error: \"%s\"", strerror(errno));
306 }
307 // std::ofstream::flush() pushes user data into kernel memory. The data will be written even if this process
308 // crashes. However, data will be lost if there is a kernel panic, which is out of scope of BT snoop log.
309 // NOTE: std::ofstream::write() followed by std::ofstream::flush() has similar effect as UNIX write(fd, data, len)
310 // as write() syscall dumps data into kernel memory directly
311 if (!btsnoop_ostream_.flush()) {
312 LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
313 }
314 }
315 }
316
DumpSnoozLogToFile(const std::vector<std::string> & data) const317 void SnoopLogger::DumpSnoozLogToFile(const std::vector<std::string>& data) const {
318 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
319 if (is_enabled_) {
320 LOG_DEBUG("btsnoop log is enabled, skip dumping btsnooz log");
321 return;
322 }
323
324 auto last_file_path = get_last_log_path(snooz_log_path_);
325
326 if (os::FileExists(snooz_log_path_)) {
327 if (!os::RenameFile(snooz_log_path_, last_file_path)) {
328 LOG_ERROR(
329 "Unabled to rename existing snooz log from \"%s\" to \"%s\"",
330 snooz_log_path_.c_str(),
331 last_file_path.c_str());
332 }
333 } else {
334 LOG_INFO("Previous log file \"%s\" does not exist, skip renaming", snooz_log_path_.c_str());
335 }
336
337 mode_t prevmask = umask(0);
338 // do not use std::ios::app as we want override the existing file
339 std::ofstream btsnooz_ostream(snooz_log_path_, std::ios::binary | std::ios::out);
340 if (!btsnooz_ostream.good()) {
341 LOG_ALWAYS_FATAL("Unable to open snoop log at \"%s\", error: \"%s\"", snooz_log_path_.c_str(), strerror(errno));
342 }
343 umask(prevmask);
344 if (!btsnooz_ostream.write(reinterpret_cast<const char*>(&kBtSnoopFileHeader), sizeof(FileHeaderType))) {
345 LOG_ALWAYS_FATAL("Unable to write file header to \"%s\", error: \"%s\"", snooz_log_path_.c_str(), strerror(errno));
346 }
347 for (const auto& packet : data) {
348 if (!btsnooz_ostream.write(packet.data(), packet.size())) {
349 LOG_ERROR("Failed to write packet payload for btsnooz, error: \"%s\"", strerror(errno));
350 }
351 }
352 if (!btsnooz_ostream.flush()) {
353 LOG_ERROR("Failed to flush, error: \"%s\"", strerror(errno));
354 }
355 }
356
ListDependencies(ModuleList * list)357 void SnoopLogger::ListDependencies(ModuleList* list) {
358 // We have no dependencies
359 }
360
Start()361 void SnoopLogger::Start() {
362 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
363 if (is_enabled_) {
364 OpenNextSnoopLogFile();
365 }
366 }
367
Stop()368 void SnoopLogger::Stop() {
369 std::lock_guard<std::recursive_mutex> lock(file_mutex_);
370 LOG_DEBUG("Dumping btsnooz log data to %s", snooz_log_path_.c_str());
371 DumpSnoozLogToFile(btsnooz_buffer_.Drain());
372 LOG_DEBUG("Closing btsnoop log data at %s", snoop_log_path_.c_str());
373 CloseCurrentSnoopLogFile();
374 }
375
GetDumpsysData(flatbuffers::FlatBufferBuilder * builder) const376 DumpsysDataFinisher SnoopLogger::GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const {
377 LOG_DEBUG("Dumping btsnooz log data to %s", snooz_log_path_.c_str());
378 DumpSnoozLogToFile(btsnooz_buffer_.Pull());
379 return Module::GetDumpsysData(builder);
380 }
381
GetMaxPacketsPerFile()382 size_t SnoopLogger::GetMaxPacketsPerFile() {
383 // Allow override max packet per file via system property
384 auto max_packets_per_file = kDefaultBtSnoopMaxPacketsPerFile;
385 {
386 auto max_packets_per_file_prop = os::GetSystemProperty(kBtSnoopMaxPacketsPerFileProperty);
387 if (max_packets_per_file_prop) {
388 auto max_packets_per_file_number = common::Uint64FromString(max_packets_per_file_prop.value());
389 if (max_packets_per_file_number) {
390 max_packets_per_file = max_packets_per_file_number.value();
391 }
392 }
393 }
394 return max_packets_per_file;
395 }
396
GetBtSnoopMode()397 std::string SnoopLogger::GetBtSnoopMode() {
398 // Default mode is DISABLED on user build.
399 // In userdebug/eng build, it can also be overwritten by modifying the global setting
400 std::string default_mode = kBtSnoopLogModeDisabled;
401 {
402 auto is_debuggable = os::GetSystemProperty(kIsDebuggableProperty);
403 if (is_debuggable.has_value() && common::StringTrim(is_debuggable.value()) == "1") {
404 auto default_mode_property = os::GetSystemProperty(kBtSnoopDefaultLogModeProperty);
405 if (default_mode_property) {
406 default_mode = std::move(default_mode_property.value());
407 }
408 }
409 }
410
411 // Get the actual mode if exist
412 std::string btsnoop_mode = default_mode;
413 {
414 auto btsnoop_mode_prop = os::GetSystemProperty(kBtSnoopLogModeProperty);
415 if (btsnoop_mode_prop) {
416 btsnoop_mode = std::move(btsnoop_mode_prop.value());
417 }
418 }
419 return btsnoop_mode;
420 }
421
__anon5855be7f0202() 422 const ModuleFactory SnoopLogger::Factory = ModuleFactory([]() {
423 return new SnoopLogger(
424 os::ParameterProvider::SnoopLogFilePath(),
425 os::ParameterProvider::SnoozLogFilePath(),
426 GetMaxPacketsPerFile(),
427 GetBtSnoopMode());
428 });
429
430 } // namespace hal
431 } // namespace bluetooth
432