4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
14 #include "file_wrappers.h"
17 * Symbian's btsnoop format is derived from Sun's snoop format.
18 * See RFC 1761 for a description of the "snoop" file format.
21 * https://gitlab.com/wireshark/wireshark/uploads/6d44fa94c164b58516e8577f44a6ccdc/btmodified_rfc1761.txt
23 * for a description of the btsnoop format.
26 /* Magic number in "btsnoop" files. */
27 static const char btsnoop_magic
[] = {
28 'b', 't', 's', 'n', 'o', 'o', 'p', '\0'
31 /* "btsnoop" file header (minus magic number). */
33 uint32_t version
; /* version number (should be 1) */
34 uint32_t datalink
; /* datalink type */
37 /* "btsnoop" record header. */
38 struct btsnooprec_hdr
{
39 uint32_t orig_len
; /* actual length of packet */
40 uint32_t incl_len
; /* number of octets captured in file */
41 uint32_t flags
; /* packet flags */
42 uint32_t cum_drops
; /* cumulative number of dropped packets */
43 int64_t ts_usec
; /* timestamp microseconds */
46 /* H1 is unframed data with the packet type encoded in the flags field of capture header */
47 /* It can be used for any datalink by placing logging above the datalink layer of HCI */
48 #define KHciLoggerDatalinkTypeH1 1001
49 /* H4 is the serial HCI with packet type encoded in the first byte of each packet */
50 #define KHciLoggerDatalinkTypeH4 1002
51 /* CSR's PPP derived bluecore serial protocol - in practice we log in H1 format after deframing */
52 #define KHciLoggerDatalinkTypeBCSP 1003
53 /* H5 is the official three wire serial protocol derived from BCSP*/
54 #define KHciLoggerDatalinkTypeH5 1004
56 #define KHciLoggerDatalinkLinuxMonitor 2001
57 /* BlueZ 5 Simulator */
58 #define KHciLoggerDatalinkBlueZ5Simulator 2002
60 #define KHciLoggerHostToController 0
61 #define KHciLoggerControllerToHost 0x00000001
62 #define KHciLoggerACLDataFrame 0
63 #define KHciLoggerCommandOrEvent 0x00000002
65 static const int64_t KUnixTimeBase
= INT64_C(0x00dcddb30f2f8000); /* offset from symbian - unix time */
67 static bool btsnoop_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
68 int *err
, char **err_info
, int64_t *offset
);
69 static bool btsnoop_seek_read(wtap
*wth
, int64_t seek_off
,
70 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
);
71 static bool btsnoop_read_record(wtap
*wth
, FILE_T fh
,
72 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
);
74 static int btsnoop_file_type_subtype
= -1;
76 void register_btsnoop(void);
78 wtap_open_return_val
btsnoop_open(wtap
*wth
, int *err
, char **err_info
)
80 char magic
[sizeof btsnoop_magic
];
81 struct btsnoop_hdr hdr
;
83 int file_encap
=WTAP_ENCAP_UNKNOWN
;
85 /* Read in the string that should be at the start of a "btsnoop" file */
86 if (!wtap_read_bytes(wth
->fh
, magic
, sizeof magic
, err
, err_info
)) {
87 if (*err
!= WTAP_ERR_SHORT_READ
)
88 return WTAP_OPEN_ERROR
;
89 return WTAP_OPEN_NOT_MINE
;
92 if (memcmp(magic
, btsnoop_magic
, sizeof btsnoop_magic
) != 0) {
93 return WTAP_OPEN_NOT_MINE
;
96 /* Read the rest of the header. */
97 if (!wtap_read_bytes(wth
->fh
, &hdr
, sizeof hdr
, err
, err_info
))
98 return WTAP_OPEN_ERROR
;
101 * Make sure it's a version we support.
103 hdr
.version
= g_ntohl(hdr
.version
);
104 if (hdr
.version
!= 1) {
105 *err
= WTAP_ERR_UNSUPPORTED
;
106 *err_info
= ws_strdup_printf("btsnoop: version %u unsupported", hdr
.version
);
107 return WTAP_OPEN_ERROR
;
110 hdr
.datalink
= g_ntohl(hdr
.datalink
);
111 switch (hdr
.datalink
) {
112 case KHciLoggerDatalinkTypeH1
:
113 file_encap
=WTAP_ENCAP_BLUETOOTH_HCI
;
115 case KHciLoggerDatalinkTypeH4
:
116 file_encap
=WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR
;
118 case KHciLoggerDatalinkTypeBCSP
:
119 *err
= WTAP_ERR_UNSUPPORTED
;
120 *err_info
= g_strdup("btsnoop: BCSP capture logs unsupported");
121 return WTAP_OPEN_ERROR
;
122 case KHciLoggerDatalinkTypeH5
:
123 *err
= WTAP_ERR_UNSUPPORTED
;
124 *err_info
= g_strdup("btsnoop: H5 capture logs unsupported");
125 return WTAP_OPEN_ERROR
;
126 case KHciLoggerDatalinkLinuxMonitor
:
127 file_encap
=WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR
;
129 case KHciLoggerDatalinkBlueZ5Simulator
:
130 *err
= WTAP_ERR_UNSUPPORTED
;
131 *err_info
= g_strdup("btsnoop: BlueZ 5 Simulator capture logs unsupported");
132 return WTAP_OPEN_ERROR
;
134 *err
= WTAP_ERR_UNSUPPORTED
;
135 *err_info
= ws_strdup_printf("btsnoop: datalink type %u unknown or unsupported", hdr
.datalink
);
136 return WTAP_OPEN_ERROR
;
139 wth
->subtype_read
= btsnoop_read
;
140 wth
->subtype_seek_read
= btsnoop_seek_read
;
141 wth
->file_encap
= file_encap
;
142 wth
->snapshot_length
= 0; /* not available in header */
143 wth
->file_tsprec
= WTAP_TSPREC_USEC
;
144 wth
->file_type_subtype
= btsnoop_file_type_subtype
;
147 * Add an IDB; we don't know how many interfaces were
148 * involved, so we just say one interface, about which
149 * we only know the link-layer type, snapshot length,
150 * and time stamp resolution.
152 wtap_add_generated_idb(wth
);
154 return WTAP_OPEN_MINE
;
157 static bool btsnoop_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
,
158 int *err
, char **err_info
, int64_t *offset
)
160 *offset
= file_tell(wth
->fh
);
162 return btsnoop_read_record(wth
, wth
->fh
, rec
, buf
, err
, err_info
);
165 static bool btsnoop_seek_read(wtap
*wth
, int64_t seek_off
,
166 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
168 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
171 return btsnoop_read_record(wth
, wth
->random_fh
, rec
, buf
, err
, err_info
);
174 static bool btsnoop_read_record(wtap
*wth
, FILE_T fh
,
175 wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
177 struct btsnooprec_hdr hdr
;
178 uint32_t packet_size
;
183 /* Read record header. */
185 if (!wtap_read_bytes_or_eof(fh
, &hdr
, sizeof hdr
, err
, err_info
))
188 packet_size
= g_ntohl(hdr
.incl_len
);
189 orig_size
= g_ntohl(hdr
.orig_len
);
190 flags
= g_ntohl(hdr
.flags
);
191 if (packet_size
> WTAP_MAX_PACKET_SIZE_STANDARD
) {
193 * Probably a corrupt capture file; don't blow up trying
194 * to allocate space for an immensely-large packet.
196 *err
= WTAP_ERR_BAD_FILE
;
197 *err_info
= ws_strdup_printf("btsnoop: File has %u-byte packet, bigger than maximum of %u",
198 packet_size
, WTAP_MAX_PACKET_SIZE_STANDARD
);
202 ts
= GINT64_FROM_BE(hdr
.ts_usec
);
205 rec
->rec_type
= REC_TYPE_PACKET
;
206 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
207 rec
->presence_flags
= WTAP_HAS_TS
|WTAP_HAS_CAP_LEN
;
208 rec
->ts
.secs
= (unsigned)(ts
/ 1000000);
209 rec
->ts
.nsecs
= (unsigned)((ts
% 1000000) * 1000);
210 rec
->rec_header
.packet_header
.caplen
= packet_size
;
211 rec
->rec_header
.packet_header
.len
= orig_size
;
212 if(wth
->file_encap
== WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR
)
214 rec
->rec_header
.packet_header
.pseudo_header
.p2p
.sent
= (flags
& KHciLoggerControllerToHost
) ? false : true;
215 } else if(wth
->file_encap
== WTAP_ENCAP_BLUETOOTH_HCI
) {
216 rec
->rec_header
.packet_header
.pseudo_header
.bthci
.sent
= (flags
& KHciLoggerControllerToHost
) ? false : true;
217 if(flags
& KHciLoggerCommandOrEvent
)
219 if(rec
->rec_header
.packet_header
.pseudo_header
.bthci
.sent
)
221 rec
->rec_header
.packet_header
.pseudo_header
.bthci
.channel
= BTHCI_CHANNEL_COMMAND
;
225 rec
->rec_header
.packet_header
.pseudo_header
.bthci
.channel
= BTHCI_CHANNEL_EVENT
;
230 rec
->rec_header
.packet_header
.pseudo_header
.bthci
.channel
= BTHCI_CHANNEL_ACL
;
232 } else if (wth
->file_encap
== WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR
) {
233 rec
->rec_header
.packet_header
.pseudo_header
.btmon
.opcode
= flags
& 0xFFFF;
234 rec
->rec_header
.packet_header
.pseudo_header
.btmon
.adapter_id
= flags
>> 16;
238 /* Read packet data. */
239 return wtap_read_packet_bytes(fh
, buf
, rec
->rec_header
.packet_header
.caplen
, err
, err_info
);
242 /* Returns 0 if we could write the specified encapsulation type,
243 an error indication otherwise. */
244 static int btsnoop_dump_can_write_encap(int encap
)
246 /* Per-packet encapsulations aren't supported. */
247 if (encap
== WTAP_ENCAP_PER_PACKET
)
248 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
251 * XXX - for now we only support WTAP_ENCAP_BLUETOOTH_HCI,
252 * WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR, and
253 * WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR.
255 if (encap
!= WTAP_ENCAP_BLUETOOTH_HCI
&&
256 encap
!= WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR
&&
257 encap
!= WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR
)
258 return WTAP_ERR_UNWRITABLE_ENCAP
;
263 static bool btsnoop_dump(wtap_dumper
*wdh
,
265 const uint8_t *pd
, int *err
, char **err_info
)
267 const union wtap_pseudo_header
*pseudo_header
= &rec
->rec_header
.packet_header
.pseudo_header
;
268 struct btsnooprec_hdr rec_hdr
;
273 /* We can only write packet records. */
274 if (rec
->rec_type
!= REC_TYPE_PACKET
) {
275 *err
= WTAP_ERR_UNWRITABLE_REC_TYPE
;
280 * Make sure this packet doesn't have a link-layer type that
281 * differs from the one for the file.
283 if (wdh
->file_encap
!= rec
->rec_header
.packet_header
.pkt_encap
) {
284 *err
= WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED
;
288 /* Don't write out anything bigger than we can read. */
289 if (rec
->rec_header
.packet_header
.caplen
> WTAP_MAX_PACKET_SIZE_STANDARD
) {
290 *err
= WTAP_ERR_PACKET_TOO_LARGE
;
294 rec_hdr
.incl_len
= GUINT32_TO_BE(rec
->rec_header
.packet_header
.caplen
);
295 rec_hdr
.orig_len
= GUINT32_TO_BE(rec
->rec_header
.packet_header
.len
);
297 switch (wdh
->file_encap
) {
299 case WTAP_ENCAP_BLUETOOTH_HCI
:
300 switch (pseudo_header
->bthci
.channel
) {
302 case BTHCI_CHANNEL_COMMAND
:
303 if (!pseudo_header
->bthci
.sent
) {
304 *err
= WTAP_ERR_UNWRITABLE_REC_DATA
;
305 *err_info
= ws_strdup_printf("btsnoop: Command channel, sent false");
308 flags
= KHciLoggerCommandOrEvent
|KHciLoggerHostToController
;
311 case BTHCI_CHANNEL_EVENT
:
312 if (pseudo_header
->bthci
.sent
) {
313 *err
= WTAP_ERR_UNWRITABLE_REC_DATA
;
314 *err_info
= ws_strdup_printf("btsnoop: Event channel, sent true");
317 flags
= KHciLoggerCommandOrEvent
|KHciLoggerControllerToHost
;
320 case BTHCI_CHANNEL_ACL
:
321 if (pseudo_header
->bthci
.sent
)
322 flags
= KHciLoggerACLDataFrame
|KHciLoggerHostToController
;
324 flags
= KHciLoggerACLDataFrame
|KHciLoggerControllerToHost
;
328 *err
= WTAP_ERR_UNWRITABLE_REC_DATA
;
329 *err_info
= ws_strdup_printf("btsnoop: Unknown channel %u",
330 pseudo_header
->bthci
.channel
);
335 case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR
:
336 if (pseudo_header
->p2p
.sent
)
337 flags
= KHciLoggerHostToController
;
339 flags
= KHciLoggerControllerToHost
;
340 if (rec
->rec_header
.packet_header
.caplen
>= 1 &&
341 (pd
[0] == 0x01 || pd
[0] == 0x04))
342 flags
|= KHciLoggerCommandOrEvent
;
345 case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR
:
346 flags
= (pseudo_header
->btmon
.adapter_id
<< 16) | pseudo_header
->btmon
.opcode
;
350 /* We should never get here - our open routine should only get
351 called for the types above. */
352 *err
= WTAP_ERR_INTERNAL
;
353 *err_info
= ws_strdup_printf("btsnoop: invalid encapsulation %u",
357 rec_hdr
.flags
= GUINT32_TO_BE(flags
);
358 rec_hdr
.cum_drops
= GUINT32_TO_BE(0);
360 nsecs
= rec
->ts
.nsecs
;
361 ts_usec
= ((int64_t) rec
->ts
.secs
* 1000000) + (nsecs
/ 1000);
362 ts_usec
+= KUnixTimeBase
;
363 rec_hdr
.ts_usec
= GINT64_TO_BE(ts_usec
);
365 if (!wtap_dump_file_write(wdh
, &rec_hdr
, sizeof rec_hdr
, err
))
367 if (!wtap_dump_file_write(wdh
, pd
, rec
->rec_header
.packet_header
.caplen
, err
))
372 /* Returns true on success, false on failure; sets "*err" to an error code on
374 static bool btsnoop_dump_open(wtap_dumper
*wdh
, int *err
, char **err_info _U_
)
376 struct btsnoop_hdr file_hdr
;
379 /* This is a btsnoop file */
380 wdh
->subtype_write
= btsnoop_dump
;
382 switch (wdh
->file_encap
) {
384 case WTAP_ENCAP_BLUETOOTH_HCI
:
385 datalink
= KHciLoggerDatalinkTypeH1
;
388 case WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR
:
389 datalink
= KHciLoggerDatalinkTypeH4
;
392 case WTAP_ENCAP_BLUETOOTH_LINUX_MONITOR
:
393 datalink
= KHciLoggerDatalinkLinuxMonitor
;
397 /* We should never get here - our open routine should only get
398 called for the types above. */
399 *err
= WTAP_ERR_INTERNAL
;
400 *err_info
= ws_strdup_printf("btsnoop: invalid encapsulation %u",
405 /* Write the file header. */
406 if (!wtap_dump_file_write(wdh
, btsnoop_magic
, sizeof btsnoop_magic
, err
))
409 /* current "btsnoop" format is 1 */
410 file_hdr
.version
= GUINT32_TO_BE(1);
411 /* HCI type encoded in first byte */
412 file_hdr
.datalink
= GUINT32_TO_BE(datalink
);
414 if (!wtap_dump_file_write(wdh
, &file_hdr
, sizeof file_hdr
, err
))
420 static const struct supported_block_type btsnoop_blocks_supported
[] = {
422 * We support packet blocks, with no comments or other options.
424 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, NO_OPTIONS_SUPPORTED
}
427 static const struct file_type_subtype_info btsnoop_info
= {
428 "Symbian OS btsnoop", "btsnoop", "log", NULL
,
429 false, BLOCKS_SUPPORTED(btsnoop_blocks_supported
),
430 btsnoop_dump_can_write_encap
, btsnoop_dump_open
, NULL
433 void register_btsnoop(void)
435 btsnoop_file_type_subtype
= wtap_register_file_type_subtype(&btsnoop_info
);
438 * Register name for backwards compatibility with the
439 * wtap_filetypes table in Lua.
441 wtap_register_backwards_compatibility_lua_name("BTSNOOP",
442 btsnoop_file_type_subtype
);
446 * Editor modelines - https://www.wireshark.org/tools/modelines.html
451 * indent-tabs-mode: nil
454 * vi: set shiftwidth=4 tabstop=8 expandtab:
455 * :indentSize=4:tabSize=8:noTabs=true: