1 /* -*- c -*- */
2 /*
3 * Copyright 2014 Christopher D. Kilgour techie AT whiterocker.com
4 *
5 * This file is part of libbtbb
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with libbtbb; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "btbb.h"
24 #include "bluetooth_le_packet.h"
25 #include "bluetooth_packet.h"
26 #include "pcapng-bt.h"
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <assert.h>
33
34 /* generic section options indicating libbtbb */
35 const struct {
36 struct {
37 option_header hdr;
38 char libname[8];
39 } libopt;
40 struct {
41 option_header hdr;
42 } termopt;
43 } libbtbb_section_options = {
44 .libopt = {
45 .hdr = {
46 .option_code = SHB_USERAPPL,
47 .option_length = 7 },
48 .libname = "libbtbb"
49 },
50 .termopt = {
51 .hdr = {
52 .option_code = OPT_ENDOFOPT,
53 .option_length = 0
54 }
55 }
56 };
57
58 static PCAPNG_RESULT
check_and_fix_tsresol(PCAPNG_HANDLE * handle,const option_header * interface_options)59 check_and_fix_tsresol( PCAPNG_HANDLE * handle,
60 const option_header * interface_options )
61 {
62 PCAPNG_RESULT retval = PCAPNG_OK;
63 int got_tsresol = 0;
64
65 while( !got_tsresol &&
66 interface_options &&
67 interface_options->option_code &&
68 interface_options->option_length) {
69 if (interface_options->option_code == IF_TSRESOL) {
70 got_tsresol = 1;
71 }
72 else {
73 size_t step = 4+4*((interface_options->option_length+3)/4);
74 uint8_t * next = &((uint8_t *)interface_options)[step];
75 interface_options = (const option_header *) next;
76 }
77 }
78
79 if (!got_tsresol) {
80 const struct {
81 option_header hdr;
82 uint8_t resol;
83 } tsresol = {
84 .hdr = {
85 .option_code = IF_TSRESOL,
86 .option_length = 1,
87 },
88 .resol = 9 /* 10^-9 is nanoseconds */
89 };
90
91 retval = pcapng_append_interface_option( handle,
92 (const option_header *) &tsresol );
93 }
94
95 return retval;
96 }
97
98 /* --------------------------------- BR/EDR ----------------------------- */
99
100 static PCAPNG_RESULT
create_bredr_capture_file_single_interface(PCAPNG_HANDLE * handle,const char * filename,const option_header * interface_options)101 create_bredr_capture_file_single_interface( PCAPNG_HANDLE * handle,
102 const char * filename,
103 const option_header * interface_options )
104 {
105 PCAPNG_RESULT retval = PCAPNG_OK;
106
107 retval = pcapng_create( handle,
108 filename,
109 (const option_header *) &libbtbb_section_options,
110 (size_t) getpagesize( ),
111 DLT_BLUETOOTH_BREDR_BB,
112 BREDR_MAX_PAYLOAD,
113 interface_options,
114 (size_t) getpagesize( ) );
115
116 if (retval == PCAPNG_OK) {
117 /* if there is no timestamp resolution alread in the
118 interface options, record nanosecond resolution */
119 retval = check_and_fix_tsresol( handle, interface_options );
120
121 if (retval != PCAPNG_OK) {
122 (void) pcapng_close( handle );
123 }
124 }
125
126 return retval;
127 }
128
btbb_pcapng_create_file(const char * filename,const char * interface_desc,btbb_pcapng_handle ** ph)129 int btbb_pcapng_create_file( const char *filename,
130 const char *interface_desc,
131 btbb_pcapng_handle ** ph )
132 {
133 int retval = PCAPNG_OK;
134 PCAPNG_HANDLE * handle = malloc( sizeof(PCAPNG_HANDLE) );
135 if (handle) {
136 const option_header * popt = NULL;
137 struct {
138 option_header header;
139 char desc[256];
140 } ifopt = {
141 .header = {
142 .option_code = IF_DESCRIPTION,
143 }
144 };
145 if (interface_desc) {
146 (void) strncpy( &ifopt.desc[0], interface_desc, 256 );
147 ifopt.desc[255] = '\0';
148 ifopt.header.option_length = strlen( ifopt.desc );
149 popt = (const option_header *) &ifopt;
150 }
151
152 retval = -create_bredr_capture_file_single_interface( handle,
153 filename,
154 popt );
155 if (retval == PCAPNG_OK) {
156 *ph = (btbb_pcapng_handle *) handle;
157 }
158 else {
159 free( handle );
160 }
161 }
162 else {
163 retval = -PCAPNG_NO_MEMORY;
164 }
165 return retval;
166 }
167
168 static PCAPNG_RESULT
append_bredr_packet(PCAPNG_HANDLE * handle,pcapng_bredr_packet * pkt)169 append_bredr_packet( PCAPNG_HANDLE * handle,
170 pcapng_bredr_packet * pkt )
171 {
172 return pcapng_append_packet( handle, ( const enhanced_packet_block *) pkt );
173 }
174
175 static void
assemble_pcapng_bredr_packet(pcapng_bredr_packet * pkt,const uint32_t interface_id,const uint64_t ns,const uint32_t caplen,const uint8_t rf_channel,const int8_t signal_power,const int8_t noise_power,const uint8_t access_code_offenses,const uint8_t payload_transport,const uint8_t payload_rate,const uint8_t corrected_header_bits,const int16_t corrected_payload_bits,const uint32_t lap,const uint32_t ref_lap,const uint8_t ref_uap,const uint32_t bt_header,const uint16_t flags,const char * payload)176 assemble_pcapng_bredr_packet( pcapng_bredr_packet * pkt,
177 const uint32_t interface_id,
178 const uint64_t ns,
179 const uint32_t caplen,
180 const uint8_t rf_channel,
181 const int8_t signal_power,
182 const int8_t noise_power,
183 const uint8_t access_code_offenses,
184 const uint8_t payload_transport,
185 const uint8_t payload_rate,
186 const uint8_t corrected_header_bits,
187 const int16_t corrected_payload_bits,
188 const uint32_t lap,
189 const uint32_t ref_lap,
190 const uint8_t ref_uap,
191 const uint32_t bt_header,
192 const uint16_t flags,
193 const char * payload )
194 {
195 uint32_t pcapng_caplen = sizeof(pcap_bluetooth_bredr_bb_header)
196 - sizeof(pkt->bredr_bb_header.bredr_payload)
197 + caplen;
198 uint32_t block_length = 4*((36+pcapng_caplen+3)/4);
199 uint32_t reflapuap = (ref_lap&0xffffff) | (ref_uap<<24);
200
201 pkt->blk_header.block_type = BLOCK_TYPE_ENHANCED_PACKET;
202 pkt->blk_header.block_total_length = block_length;
203 pkt->blk_header.interface_id = interface_id;
204 pkt->blk_header.timestamp_high = (uint32_t) (ns >> 32);
205 pkt->blk_header.timestamp_low = (uint32_t) (ns & 0x0ffffffffull);
206 pkt->blk_header.captured_len = pcapng_caplen;
207 pkt->blk_header.packet_len = pcapng_caplen;
208 pkt->bredr_bb_header.rf_channel = rf_channel;
209 pkt->bredr_bb_header.signal_power = signal_power;
210 pkt->bredr_bb_header.noise_power = noise_power;
211 pkt->bredr_bb_header.access_code_offenses = access_code_offenses;
212 pkt->bredr_bb_header.payload_transport_rate =
213 (payload_transport << 4) | payload_rate;
214 pkt->bredr_bb_header.corrected_header_bits = corrected_header_bits;
215 pkt->bredr_bb_header.corrected_payload_bits = htole16( corrected_payload_bits );
216 pkt->bredr_bb_header.lap = htole32( lap );
217 pkt->bredr_bb_header.ref_lap_uap = htole32( reflapuap );
218 pkt->bredr_bb_header.bt_header = htole32( bt_header );
219 pkt->bredr_bb_header.flags = htole16( flags );
220 if (caplen) {
221 assert(caplen <= sizeof(pkt->bredr_bb_header.bredr_payload)); // caller ensures this, but to be safe..
222 (void) memcpy( &pkt->bredr_bb_header.bredr_payload[0], payload, caplen );
223 pkt->bredr_bb_header.flags |= htole16( BREDR_PAYLOAD_PRESENT );
224 }
225 else {
226 pkt->bredr_bb_header.flags &= htole16( ~BREDR_PAYLOAD_PRESENT );
227 }
228 ((uint32_t *)pkt)[block_length/4-2] = 0x00000000; /* no-options */
229 ((uint32_t *)pkt)[block_length/4-1] = block_length;
230 }
231
btbb_pcapng_append_packet(btbb_pcapng_handle * h,const uint64_t ns,const int8_t sigdbm,const int8_t noisedbm,const uint32_t reflap,const uint8_t refuap,const btbb_packet * pkt)232 int btbb_pcapng_append_packet(btbb_pcapng_handle * h, const uint64_t ns,
233 const int8_t sigdbm, const int8_t noisedbm,
234 const uint32_t reflap, const uint8_t refuap,
235 const btbb_packet *pkt)
236 {
237 uint16_t flags = BREDR_DEWHITENED | BREDR_SIGPOWER_VALID |
238 ((noisedbm < sigdbm) ? BREDR_NOISEPOWER_VALID : 0) |
239 ((reflap != LAP_ANY) ? BREDR_REFLAP_VALID : 0) |
240 ((refuap != UAP_ANY) ? BREDR_REFUAP_VALID : 0);
241 int caplen = btbb_packet_get_payload_length(pkt);
242 char payload_bytes[caplen];
243 btbb_get_payload_packed( pkt, &payload_bytes[0] );
244 caplen = MIN(BREDR_MAX_PAYLOAD, caplen);
245 pcapng_bredr_packet pcapng_pkt;
246 assemble_pcapng_bredr_packet( &pcapng_pkt,
247 0,
248 ns,
249 caplen,
250 btbb_packet_get_channel(pkt),
251 sigdbm,
252 noisedbm,
253 btbb_packet_get_ac_errors(pkt),
254 btbb_packet_get_transport(pkt),
255 btbb_packet_get_modulation(pkt),
256 0, /* TODO: corrected header bits */
257 0, /* TODO: corrected payload bits */
258 btbb_packet_get_lap(pkt),
259 reflap,
260 refuap,
261 btbb_packet_get_header_packed(pkt),
262 flags,
263 payload_bytes );
264 return -append_bredr_packet( (PCAPNG_HANDLE *)h, &pcapng_pkt );
265 }
266
267 static PCAPNG_RESULT
record_bd_addr_info(PCAPNG_HANDLE * handle,const uint64_t bd_addr,const uint8_t uap_mask,const uint8_t nap_valid)268 record_bd_addr_info( PCAPNG_HANDLE * handle,
269 const uint64_t bd_addr,
270 const uint8_t uap_mask,
271 const uint8_t nap_valid )
272 {
273 const bredr_br_addr_option bdopt = {
274 .header = {
275 .option_code = PCAPNG_BREDR_OPTION_BD_ADDR,
276 .option_length = sizeof(bredr_br_addr_option),
277 },
278 .bd_addr_info = {
279 .bd_addr = {
280 ((bd_addr>>0) & 0xff),
281 ((bd_addr>>8) & 0xff),
282 ((bd_addr>>16) & 0xff),
283 ((bd_addr>>24) & 0xff),
284 ((bd_addr>>32) & 0xff),
285 ((bd_addr>>40) & 0xff)
286 },
287 .uap_mask = uap_mask,
288 .nap_valid = nap_valid,
289 }
290 };
291 return pcapng_append_interface_option( handle,
292 (const option_header *) &bdopt );
293 }
294
btbb_pcapng_record_bdaddr(btbb_pcapng_handle * h,const uint64_t bdaddr,const uint8_t uapmask,const uint8_t napvalid)295 int btbb_pcapng_record_bdaddr(btbb_pcapng_handle * h, const uint64_t bdaddr,
296 const uint8_t uapmask, const uint8_t napvalid)
297 {
298 return -record_bd_addr_info( (PCAPNG_HANDLE *) h,
299 bdaddr, uapmask, napvalid );
300 }
301
302 static PCAPNG_RESULT
record_bredr_master_clock_info(PCAPNG_HANDLE * handle,const uint64_t bd_addr,const uint64_t ns,const uint32_t clk,const uint32_t clk_mask)303 record_bredr_master_clock_info( PCAPNG_HANDLE * handle,
304 const uint64_t bd_addr,
305 const uint64_t ns,
306 const uint32_t clk,
307 const uint32_t clk_mask)
308 {
309 const bredr_clk_option mcopt = {
310 .header = {
311 .option_code = PCAPNG_BREDR_OPTION_MASTER_CLOCK_INFO,
312 .option_length = sizeof(bredr_clk_option)
313 },
314 .clock_info = {
315 .ts = ns,
316 .lap_uap = htole32(bd_addr & 0xffffffff),
317 .clk = clk,
318 .clk_mask = clk_mask
319 }
320 };
321 return pcapng_append_interface_option( handle,
322 (const option_header *) &mcopt );
323 }
324
btbb_pcapng_record_btclock(btbb_pcapng_handle * h,const uint64_t bdaddr,const uint64_t ns,const uint32_t clk,const uint32_t clkmask)325 int btbb_pcapng_record_btclock(btbb_pcapng_handle * h, const uint64_t bdaddr,
326 const uint64_t ns, const uint32_t clk,
327 const uint32_t clkmask)
328 {
329 return -record_bredr_master_clock_info( (PCAPNG_HANDLE *) h,
330 bdaddr, ns, clk, clkmask );
331 }
332
btbb_pcapng_close(btbb_pcapng_handle * h)333 int btbb_pcapng_close(btbb_pcapng_handle * h)
334 {
335 pcapng_close( (PCAPNG_HANDLE *) h );
336 if (h) {
337 free( h );
338 }
339 return -PCAPNG_INVALID_HANDLE;
340 }
341
342 /* --------------------------------- Low Energy ---------------------------- */
343
344 static PCAPNG_RESULT
create_le_capture_file_single_interface(PCAPNG_HANDLE * handle,const char * filename,const option_header * interface_options)345 create_le_capture_file_single_interface( PCAPNG_HANDLE * handle,
346 const char * filename,
347 const option_header * interface_options )
348 {
349 PCAPNG_RESULT retval = PCAPNG_OK;
350
351 retval = pcapng_create( handle,
352 filename,
353 (const option_header *) &libbtbb_section_options,
354 (size_t) getpagesize( ),
355 DLT_BLUETOOTH_LE_LL_WITH_PHDR,
356 64,
357 interface_options,
358 (size_t) getpagesize( ) );
359
360 if (retval == PCAPNG_OK) {
361 /* if there is no timestamp resolution alread in the
362 interface options, record nanosecond resolution */
363 retval = check_and_fix_tsresol( handle, interface_options );
364
365 if (retval != PCAPNG_OK) {
366 (void) pcapng_close( handle );
367 }
368 }
369
370 return retval;
371 }
372
373 int
lell_pcapng_create_file(const char * filename,const char * interface_desc,lell_pcapng_handle ** ph)374 lell_pcapng_create_file(const char *filename, const char *interface_desc,
375 lell_pcapng_handle ** ph)
376 {
377 int retval = PCAPNG_OK;
378 PCAPNG_HANDLE * handle = malloc( sizeof(PCAPNG_HANDLE) );
379 if (handle) {
380 const option_header * popt = NULL;
381 struct {
382 option_header header;
383 char desc[256];
384 } ifopt = {
385 .header = {
386 .option_code = IF_DESCRIPTION,
387 }
388 };
389 if (interface_desc) {
390 (void) strncpy( &ifopt.desc[0], interface_desc, 256 );
391 ifopt.desc[255] = '\0';
392 ifopt.header.option_length = strlen( ifopt.desc );
393 popt = (const option_header *) &ifopt;
394 }
395
396 retval = -create_le_capture_file_single_interface( handle,
397 filename,
398 popt );
399 if (retval == PCAPNG_OK) {
400 *ph = (lell_pcapng_handle *) handle;
401 }
402 else {
403 free( handle );
404 }
405 }
406 else {
407 retval = -PCAPNG_NO_MEMORY;
408 }
409 return retval;
410 }
411
412 static PCAPNG_RESULT
append_le_packet(PCAPNG_HANDLE * handle,pcapng_le_packet * pkt)413 append_le_packet( PCAPNG_HANDLE * handle,
414 pcapng_le_packet * pkt )
415 {
416 return pcapng_append_packet( handle, ( const enhanced_packet_block *) pkt );
417 }
418
419 /* Size of a PCAPNG enhanced packet block with no packet data.
420 NOTE: The pcap_bluetooth_le_ll_header is part of the packet data of
421 the enhanced block. */
422 #define PCAPNG_ENHANCED_BLK_SZ 36
423
424 static void
assemble_pcapng_le_packet(pcapng_le_packet * pkt,const uint32_t interface_id,const uint64_t ns,const uint32_t caplen,const uint8_t rf_channel,const int8_t signal_power,const int8_t noise_power,const uint8_t access_address_offenses,const uint32_t ref_access_address,const uint16_t flags,const uint8_t * lepkt)425 assemble_pcapng_le_packet( pcapng_le_packet * pkt,
426 const uint32_t interface_id,
427 const uint64_t ns,
428 const uint32_t caplen,
429 const uint8_t rf_channel,
430 const int8_t signal_power,
431 const int8_t noise_power,
432 const uint8_t access_address_offenses,
433 const uint32_t ref_access_address,
434 const uint16_t flags,
435 const uint8_t * lepkt )
436 {
437 uint32_t pcapng_caplen = sizeof(pcap_bluetooth_le_ll_header)+caplen;
438 uint32_t block_length = 4*((PCAPNG_ENHANCED_BLK_SZ+pcapng_caplen+3)/4);
439
440 // TODO this should never happen, but handle it if it does
441 assert(caplen <= LE_MAX_PAYLOAD);
442
443 pkt->blk_header.block_type = BLOCK_TYPE_ENHANCED_PACKET;
444 pkt->blk_header.block_total_length = block_length;
445 pkt->blk_header.interface_id = interface_id;
446 pkt->blk_header.timestamp_high = (uint32_t) (ns >> 32);
447 pkt->blk_header.timestamp_low = (uint32_t) (ns & 0x0ffffffffull);
448 pkt->blk_header.captured_len = pcapng_caplen;
449 pkt->blk_header.packet_len = pcapng_caplen;
450 pkt->le_ll_header.rf_channel = rf_channel;
451 pkt->le_ll_header.signal_power = signal_power;
452 pkt->le_ll_header.noise_power = noise_power;
453 pkt->le_ll_header.access_address_offenses = access_address_offenses;
454 pkt->le_ll_header.ref_access_address = htole32( ref_access_address );
455 pkt->le_ll_header.flags = htole16( flags );
456 (void) memcpy( &pkt->le_packet[0], lepkt, caplen );
457 ((uint32_t *)pkt)[block_length/4-2] = 0x00000000; /* no-options */
458 ((uint32_t *)pkt)[block_length/4-1] = block_length;
459 }
460
461 int
lell_pcapng_append_packet(lell_pcapng_handle * h,const uint64_t ns,const int8_t sigdbm,const int8_t noisedbm,const uint32_t refAA,const lell_packet * pkt)462 lell_pcapng_append_packet(lell_pcapng_handle * h, const uint64_t ns,
463 const int8_t sigdbm, const int8_t noisedbm,
464 const uint32_t refAA, const lell_packet *pkt)
465 {
466 uint16_t flags = LE_DEWHITENED | LE_AA_OFFENSES_VALID |
467 LE_SIGPOWER_VALID |
468 ((noisedbm < sigdbm) ? LE_NOISEPOWER_VALID : 0) |
469 (lell_packet_is_data(pkt) ? 0 : LE_REF_AA_VALID);
470 pcapng_le_packet pcapng_pkt;
471
472 /* The extra 9 bytes added to the packet length are for:
473 4 bytes for Access Address
474 2 bytes for PDU header
475 3 bytes for CRC */
476 assemble_pcapng_le_packet( &pcapng_pkt,
477 0,
478 ns,
479 9+pkt->length,
480 pkt->channel_k,
481 sigdbm,
482 noisedbm,
483 pkt->access_address_offenses,
484 refAA,
485 flags,
486 &pkt->symbols[0] );
487 int retval = -append_le_packet( (PCAPNG_HANDLE *) h, &pcapng_pkt );
488 if ((retval == 0) && !lell_packet_is_data(pkt) && (pkt->adv_type == CONNECT_REQ)) {
489 (void) lell_pcapng_record_connect_req(h, ns, &pkt->symbols[0]);
490 }
491 return retval;
492 }
493
494 static PCAPNG_RESULT
record_le_connect_req_info(PCAPNG_HANDLE * handle,const uint64_t ns,const uint8_t * pdu)495 record_le_connect_req_info( PCAPNG_HANDLE * handle,
496 const uint64_t ns,
497 const uint8_t * pdu )
498 {
499 le_ll_connection_info_option cropt = {
500 .header = {
501 .option_code = PCAPNG_LE_LL_CONNECTION_INFO,
502 .option_length = sizeof(le_ll_connection_info_option)
503 },
504 .connection_info = {
505 .ns = ns
506 }
507 };
508 (void) memcpy( &cropt.connection_info.pdu.bytes[0], pdu, 34 );
509 return pcapng_append_interface_option( handle,
510 (const option_header *) &cropt );
511 }
512
513 int
lell_pcapng_record_connect_req(lell_pcapng_handle * h,const uint64_t ns,const uint8_t * pdu)514 lell_pcapng_record_connect_req(lell_pcapng_handle * h, const uint64_t ns,
515 const uint8_t * pdu)
516 {
517 return -record_le_connect_req_info( (PCAPNG_HANDLE *) h, ns, pdu );
518 }
519
lell_pcapng_close(lell_pcapng_handle * h)520 int lell_pcapng_close(lell_pcapng_handle *h)
521 {
522 pcapng_close( (PCAPNG_HANDLE *) h );
523 if (h) {
524 free( h );
525 }
526 return -PCAPNG_INVALID_HANDLE;
527 }
528