1 /*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "btm_snoop.h"
17
18 #include <stdio.h>
19
20 #include <securec.h>
21 #include <sys/time.h>
22
23 #include "hci/hci.h"
24 #include "platform/include/allocator.h"
25 #include "platform/include/bt_endian.h"
26 #include "platform/include/mutex.h"
27 #include "platform/include/queue.h"
28 #include "platform/include/reactor.h"
29 #include "platform/include/thread.h"
30
31 #include "btm.h"
32 #include "btm/btm_snoop_filter.h"
33 #include "log.h"
34
35 #define SNOOP_INDENTIFICATION_PATTERN \
36 { \
37 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 \
38 }
39 #define SNOOP_VERSION_NUMBER 1
40 #define SNOOP_DATALINK_TYPE_H4 1002
41
42 #define MICROSECOND_1970BASE 62168256000000000
43
44 #define H4_HEADER_CMD 0x01
45 #define H4_HEADER_ACLDATA 0x02
46 #define H4_HEADER_EVENT 0x04
47
48 #define SNOOP_PACKET_FLAG_SENT 0x00
49 #define SNOOP_PACKET_FLAG_RECEIVED 0x01
50 #define SNOOP_PACKET_FLAG_DATA 0x00
51 #define SNOOP_PACKET_FLAG_CMD_EVENT 0x02
52
53 #define SNOOP_LAST_FILE_TAIL ".last"
54
55 #define MICROSECOND 1000000
56
57 #define SNOOP_BLOCK_IOV_COUNT 3
58
59 #define HCI_LOG_PATH "./hci.log"
60
61 #define HCI_H4_HEADER_LEN 1
62
63 #pragma pack(1)
64 typedef struct {
65 uint8_t identificationPattern[8]; // { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 }
66 uint32_t versionNumber; // 1
67 uint32_t datalinkType; // 1002
68 } BtmSnoopFileHeader;
69
70 typedef struct {
71 uint32_t originalLength;
72 uint32_t includedLength;
73 uint32_t packetFlags;
74 uint32_t cumulativeDrops;
75 uint64_t timestamp; // microseconds
76 } BtmSnoopPacketHeader;
77 #pragma pack()
78
79 static bool g_output = false;
80 static char *g_outputPath = NULL;
81 static FILE *g_outputFile = NULL;
82 static bool g_hciLogOuput = false;
83 static Mutex *g_outputMutex = NULL;
84
GetH4HeaderAndPacketFlags(uint8_t type,uint8_t * h4Header,uint32_t * packetFlags)85 static void GetH4HeaderAndPacketFlags(uint8_t type, uint8_t *h4Header, uint32_t *packetFlags)
86 {
87 switch (type) {
88 case TRANSMISSON_TYPE_H2C_CMD:
89 *h4Header = H4_HEADER_CMD;
90 *packetFlags = SNOOP_PACKET_FLAG_SENT | SNOOP_PACKET_FLAG_CMD_EVENT;
91 break;
92 case TRANSMISSON_TYPE_C2H_EVENT:
93 *h4Header = H4_HEADER_EVENT;
94 *packetFlags = SNOOP_PACKET_FLAG_RECEIVED | SNOOP_PACKET_FLAG_CMD_EVENT;
95 break;
96 case TRANSMISSON_TYPE_H2C_DATA:
97 *h4Header = H4_HEADER_ACLDATA;
98 *packetFlags = SNOOP_PACKET_FLAG_SENT | SNOOP_PACKET_FLAG_DATA;
99 break;
100 case TRANSMISSON_TYPE_C2H_DATA:
101 *h4Header = H4_HEADER_ACLDATA;
102 *packetFlags = SNOOP_PACKET_FLAG_RECEIVED | SNOOP_PACKET_FLAG_DATA;
103 break;
104 default:
105 break;
106 }
107 }
108
BtmSnoopOutput(uint8_t type,const uint8_t * data,uint16_t length)109 static void BtmSnoopOutput(uint8_t type, const uint8_t *data, uint16_t length)
110 {
111 struct timeval tv;
112 gettimeofday(&tv, NULL);
113
114 const uint64_t timestamp = MICROSECOND_1970BASE + tv.tv_sec * MICROSECOND + tv.tv_usec;
115
116 uint8_t h4Header = 0;
117 uint32_t packetFlags = 0;
118
119 GetH4HeaderAndPacketFlags(type, &h4Header, &packetFlags);
120
121 uint16_t originalLength = length + 1;
122 uint16_t includedLength = length + 1;
123 const uint8_t *outputData = data;
124
125 BtmHciFilter(type, &outputData, originalLength, &includedLength);
126
127 BtmSnoopPacketHeader header = {
128 .originalLength = H2BE_32(originalLength),
129 .includedLength = H2BE_32(includedLength),
130 .cumulativeDrops = H2BE_32(0),
131 .packetFlags = H2BE_32(packetFlags),
132 .timestamp = H2BE_64(timestamp),
133 };
134
135 if (g_outputFile == NULL) {
136 LOG_ERROR("%{public}s, g_outputFile is NULL", __FUNCTION__);
137 return;
138 }
139 MutexLock(g_outputMutex);
140
141 (void)fwrite(&header, 1, sizeof(BtmSnoopPacketHeader), g_outputFile);
142 (void)fwrite(&h4Header, 1, HCI_H4_HEADER_LEN, g_outputFile);
143 (void)fwrite(outputData, 1, includedLength - HCI_H4_HEADER_LEN, g_outputFile);
144
145 fflush(g_outputFile);
146
147 if (outputData != data) {
148 MEM_MALLOC.free((void *)outputData);
149 }
150
151 MutexUnlock(g_outputMutex);
152 }
153
BtmOnHciTransmission(uint8_t type,const uint8_t * data,uint16_t length)154 static void BtmOnHciTransmission(uint8_t type, const uint8_t *data, uint16_t length)
155 {
156 BtmSnoopOutput(type, data, length);
157 }
158
BtmWriteSnoopFileHeader(void)159 static void BtmWriteSnoopFileHeader(void)
160 {
161 BtmSnoopFileHeader header = {
162 .identificationPattern = SNOOP_INDENTIFICATION_PATTERN,
163 .versionNumber = H2BE_32(SNOOP_VERSION_NUMBER),
164 .datalinkType = H2BE_32(SNOOP_DATALINK_TYPE_H4),
165 };
166
167 if (g_outputFile == NULL) {
168 LOG_ERROR("%{public}s, g_outputFile is NULL", __FUNCTION__);
169 return;
170 }
171 MutexLock(g_outputMutex);
172
173 (void)fwrite(&header, 1, sizeof(BtmSnoopFileHeader), g_outputFile);
174
175 fflush(g_outputFile);
176
177 MutexUnlock(g_outputMutex);
178 }
179
BtmIsFileExists(const char * path)180 static bool BtmIsFileExists(const char *path)
181 {
182 bool exists = false;
183 FILE *file = fopen(path, "r");
184 if (file != NULL) {
185 exists = true;
186 fclose(file);
187 }
188 return exists;
189 }
190
BtmPrepareSnoopFile(void)191 static void BtmPrepareSnoopFile(void)
192 {
193 if (g_hciLogOuput) {
194 bool exists = BtmIsFileExists(HCI_LOG_PATH);
195 if (exists) {
196 g_outputFile = fopen(HCI_LOG_PATH, "a");
197 if (g_outputFile == NULL) {
198 return;
199 }
200 } else {
201 g_outputFile = fopen(HCI_LOG_PATH, "w");
202 if (g_outputFile == NULL) {
203 return;
204 }
205
206 BtmWriteSnoopFileHeader();
207 }
208 } else {
209 bool exists = BtmIsFileExists(g_outputPath);
210 if (exists) {
211 const int length = strlen(g_outputPath) + strlen(SNOOP_LAST_FILE_TAIL) + 1;
212 char *bakPath = MEM_CALLOC.alloc(length);
213 if (bakPath == NULL) {
214 return;
215 }
216 if (strcpy_s(bakPath, length, g_outputPath) != EOK) {
217 return;
218 }
219 (void)strcat_s(bakPath, length, SNOOP_LAST_FILE_TAIL);
220 rename(g_outputPath, bakPath);
221
222 MEM_CALLOC.free(bakPath);
223 }
224
225 g_outputFile = fopen(g_outputPath, "w");
226 if (g_outputFile == NULL) {
227 LOG_ERROR("%{public}s, g_outputFile is NULL:%{public}s", __FUNCTION__, strerror(errno));
228 return;
229 }
230
231 BtmWriteSnoopFileHeader();
232 }
233 }
234
BtmCloseSnoopFile(void)235 static void BtmCloseSnoopFile(void)
236 {
237 if (g_outputFile != NULL) {
238 fclose(g_outputFile);
239 g_outputFile = NULL;
240 }
241 }
242
BTM_SetSnoopFilePath(const char * path,uint16_t length)243 int BTM_SetSnoopFilePath(const char *path, uint16_t length)
244 {
245 g_outputPath = (char *)MEM_MALLOC.alloc(length + 1);
246 if (g_outputPath == NULL) {
247 return BT_NO_MEMORY;
248 }
249
250 (void)memcpy_s(g_outputPath, length + 1, path, length);
251 g_outputPath[length] = '\0';
252 return BT_SUCCESS;
253 }
254
BTM_EnableSnoopFileOutput(bool filter)255 int BTM_EnableSnoopFileOutput(bool filter)
256 {
257 g_output = true;
258 if (filter) {
259 BtmEnableSnoopFilter();
260 } else {
261 BtmDisableSnoopFilter();
262 }
263 return BT_SUCCESS;
264 }
265
BTM_DisableSnoopFileOutput(void)266 int BTM_DisableSnoopFileOutput(void)
267 {
268 if (g_output) {
269 BtmDisableSnoopFilter();
270 }
271 g_output = false;
272 return BT_SUCCESS;
273 }
274
BtmStartSnoopOutput(void)275 void BtmStartSnoopOutput(void)
276 {
277 if (g_hciLogOuput || (g_output && g_outputPath != NULL)) {
278 BtmPrepareSnoopFile();
279
280 HCI_SetTransmissionCaptureCallback(BtmOnHciTransmission);
281 HCI_EnableTransmissionCapture();
282 }
283 }
284
BtmStopSnoopOutput(void)285 void BtmStopSnoopOutput(void)
286 {
287 HCI_DisableTransmissionCapture();
288
289 BtmCloseSnoopFile();
290 }
291
BtmInitSnoop(void)292 void BtmInitSnoop(void)
293 {
294 g_outputMutex = MutexCreate();
295 BtmInitSnoopFilter();
296 }
297
BtmCloseSnoop(void)298 void BtmCloseSnoop(void)
299 {
300 g_output = false;
301 g_hciLogOuput = false;
302
303 BtmCloseSnoopFilter();
304
305 if (g_outputMutex != NULL) {
306 MutexDelete(g_outputMutex);
307 g_outputMutex = NULL;
308 }
309
310 if (g_outputPath != NULL) {
311 MEM_MALLOC.free(g_outputPath);
312 g_outputPath = NULL;
313 }
314 }
315
BTM_EnableHciLogOutput(bool filter)316 int BTM_EnableHciLogOutput(bool filter)
317 {
318 g_hciLogOuput = true;
319 if (filter) {
320 BtmEnableSnoopFilter();
321 } else {
322 BtmDisableSnoopFilter();
323 }
324 return BT_SUCCESS;
325 }
326
BTM_DisableHciLogOutput(void)327 int BTM_DisableHciLogOutput(void)
328 {
329 if (g_hciLogOuput) {
330 BtmDisableSnoopFilter();
331 }
332 g_hciLogOuput = false;
333 return BT_SUCCESS;
334 }