xref: /libbtbb/lib/src/pcapng-bt.c (revision fa8f3df868301193d3539c909932f473208f02c7)
18f3e7eeaSChristopher Kilgour /* -*- c -*- */
28f3e7eeaSChristopher Kilgour /*
38f3e7eeaSChristopher Kilgour  * Copyright 2014 Christopher D. Kilgour techie AT whiterocker.com
48f3e7eeaSChristopher Kilgour  *
58f3e7eeaSChristopher Kilgour  * This file is part of libbtbb
68f3e7eeaSChristopher Kilgour  *
78f3e7eeaSChristopher Kilgour  * This program is free software; you can redistribute it and/or modify
88f3e7eeaSChristopher Kilgour  * it under the terms of the GNU General Public License as published by
98f3e7eeaSChristopher Kilgour  * the Free Software Foundation; either version 2, or (at your option)
108f3e7eeaSChristopher Kilgour  * any later version.
118f3e7eeaSChristopher Kilgour  *
128f3e7eeaSChristopher Kilgour  * This program is distributed in the hope that it will be useful,
138f3e7eeaSChristopher Kilgour  * but WITHOUT ANY WARRANTY; without even the implied warranty of
148f3e7eeaSChristopher Kilgour  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
158f3e7eeaSChristopher Kilgour  * GNU General Public License for more details.
168f3e7eeaSChristopher Kilgour  *
178f3e7eeaSChristopher Kilgour  * You should have received a copy of the GNU General Public License
188f3e7eeaSChristopher Kilgour  * along with libbtbb; see the file COPYING.  If not, write to
198f3e7eeaSChristopher Kilgour  * the Free Software Foundation, Inc., 51 Franklin Street,
208f3e7eeaSChristopher Kilgour  * Boston, MA 02110-1301, USA.
218f3e7eeaSChristopher Kilgour  */
228f3e7eeaSChristopher Kilgour 
238f3e7eeaSChristopher Kilgour #include "btbb.h"
248f3e7eeaSChristopher Kilgour #include "bluetooth_le_packet.h"
25f83b85cfSDominic Spill #include "bluetooth_packet.h"
268f3e7eeaSChristopher Kilgour #include "pcapng-bt.h"
278f3e7eeaSChristopher Kilgour #include <stdlib.h>
288f3e7eeaSChristopher Kilgour #include <string.h>
298f3e7eeaSChristopher Kilgour #include <time.h>
308f3e7eeaSChristopher Kilgour #include <unistd.h>
310e05cc66SDominic Spill #include <stdio.h>
321ce7419bSTheodore A. Roth #include <assert.h>
338f3e7eeaSChristopher Kilgour 
348f3e7eeaSChristopher Kilgour /* generic section options indicating libbtbb */
358f3e7eeaSChristopher Kilgour const struct {
368f3e7eeaSChristopher Kilgour 	struct {
378f3e7eeaSChristopher Kilgour 		option_header hdr;
388f3e7eeaSChristopher Kilgour 		char libname[8];
398f3e7eeaSChristopher Kilgour 	} libopt;
408f3e7eeaSChristopher Kilgour 	struct {
418f3e7eeaSChristopher Kilgour 		option_header hdr;
428f3e7eeaSChristopher Kilgour 	} termopt;
438f3e7eeaSChristopher Kilgour } libbtbb_section_options = {
448f3e7eeaSChristopher Kilgour 	.libopt = {
458f3e7eeaSChristopher Kilgour 		.hdr = {
468f3e7eeaSChristopher Kilgour 			.option_code = SHB_USERAPPL,
478f3e7eeaSChristopher Kilgour 			.option_length = 7 },
488f3e7eeaSChristopher Kilgour 		.libname = "libbtbb"
498f3e7eeaSChristopher Kilgour 	},
508f3e7eeaSChristopher Kilgour 	.termopt = {
518f3e7eeaSChristopher Kilgour 		.hdr = {
528f3e7eeaSChristopher Kilgour 			.option_code = OPT_ENDOFOPT,
538f3e7eeaSChristopher Kilgour 			.option_length = 0
548f3e7eeaSChristopher Kilgour 		}
558f3e7eeaSChristopher Kilgour 	}
568f3e7eeaSChristopher Kilgour };
578f3e7eeaSChristopher Kilgour 
588f3e7eeaSChristopher Kilgour static PCAPNG_RESULT
check_and_fix_tsresol(PCAPNG_HANDLE * handle,const option_header * interface_options)598f3e7eeaSChristopher Kilgour check_and_fix_tsresol( PCAPNG_HANDLE * handle,
608f3e7eeaSChristopher Kilgour 		       const option_header * interface_options )
618f3e7eeaSChristopher Kilgour {
628f3e7eeaSChristopher Kilgour 	PCAPNG_RESULT retval = PCAPNG_OK;
638f3e7eeaSChristopher Kilgour 	int got_tsresol = 0;
648f3e7eeaSChristopher Kilgour 
658f3e7eeaSChristopher Kilgour 	while( !got_tsresol &&
668f3e7eeaSChristopher Kilgour 	       interface_options &&
678f3e7eeaSChristopher Kilgour 	       interface_options->option_code &&
688f3e7eeaSChristopher Kilgour 	       interface_options->option_length) {
698f3e7eeaSChristopher Kilgour 		if (interface_options->option_code == IF_TSRESOL) {
708f3e7eeaSChristopher Kilgour 			got_tsresol = 1;
718f3e7eeaSChristopher Kilgour 		}
728f3e7eeaSChristopher Kilgour 		else {
738f3e7eeaSChristopher Kilgour 			size_t step = 4+4*((interface_options->option_length+3)/4);
748f3e7eeaSChristopher Kilgour 			uint8_t * next = &((uint8_t *)interface_options)[step];
758f3e7eeaSChristopher Kilgour 			interface_options = (const option_header *) next;
768f3e7eeaSChristopher Kilgour 		}
778f3e7eeaSChristopher Kilgour 	}
788f3e7eeaSChristopher Kilgour 
798f3e7eeaSChristopher Kilgour 	if (!got_tsresol) {
808f3e7eeaSChristopher Kilgour 		const struct {
818f3e7eeaSChristopher Kilgour 			option_header hdr;
828f3e7eeaSChristopher Kilgour 			uint8_t resol;
838f3e7eeaSChristopher Kilgour 		} tsresol = {
848f3e7eeaSChristopher Kilgour 			.hdr = {
858f3e7eeaSChristopher Kilgour 				.option_code = IF_TSRESOL,
868f3e7eeaSChristopher Kilgour 				.option_length = 1,
878f3e7eeaSChristopher Kilgour 			},
888f3e7eeaSChristopher Kilgour 			.resol = 9 /* 10^-9 is nanoseconds */
898f3e7eeaSChristopher Kilgour 		};
908f3e7eeaSChristopher Kilgour 
918f3e7eeaSChristopher Kilgour 		retval = pcapng_append_interface_option( handle,
928f3e7eeaSChristopher Kilgour 							 (const option_header *) &tsresol );
938f3e7eeaSChristopher Kilgour 	}
948f3e7eeaSChristopher Kilgour 
958f3e7eeaSChristopher Kilgour 	return retval;
968f3e7eeaSChristopher Kilgour }
978f3e7eeaSChristopher Kilgour 
988f3e7eeaSChristopher Kilgour /* --------------------------------- BR/EDR ----------------------------- */
998f3e7eeaSChristopher Kilgour 
1008f3e7eeaSChristopher Kilgour static PCAPNG_RESULT
create_bredr_capture_file_single_interface(PCAPNG_HANDLE * handle,const char * filename,const option_header * interface_options)1018f3e7eeaSChristopher Kilgour create_bredr_capture_file_single_interface( PCAPNG_HANDLE * handle,
1028f3e7eeaSChristopher Kilgour 					    const char * filename,
1038f3e7eeaSChristopher Kilgour 					    const option_header * interface_options )
1048f3e7eeaSChristopher Kilgour {
1058f3e7eeaSChristopher Kilgour 	PCAPNG_RESULT retval = PCAPNG_OK;
1068f3e7eeaSChristopher Kilgour 
1078f3e7eeaSChristopher Kilgour 	retval = pcapng_create( handle,
1088f3e7eeaSChristopher Kilgour 				filename,
1098f3e7eeaSChristopher Kilgour 				(const option_header *) &libbtbb_section_options,
1108f3e7eeaSChristopher Kilgour 				(size_t) getpagesize( ),
1118f3e7eeaSChristopher Kilgour 				DLT_BLUETOOTH_BREDR_BB,
1120e05cc66SDominic Spill 				BREDR_MAX_PAYLOAD,
1138f3e7eeaSChristopher Kilgour 				interface_options,
1148f3e7eeaSChristopher Kilgour 				(size_t) getpagesize( ) );
1158f3e7eeaSChristopher Kilgour 
1168f3e7eeaSChristopher Kilgour 	if (retval == PCAPNG_OK) {
1178f3e7eeaSChristopher Kilgour 		/* if there is no timestamp resolution alread in the
1188f3e7eeaSChristopher Kilgour 		   interface options, record nanosecond resolution */
1198f3e7eeaSChristopher Kilgour 		retval = check_and_fix_tsresol( handle, interface_options );
1208f3e7eeaSChristopher Kilgour 
1218f3e7eeaSChristopher Kilgour 		if (retval != PCAPNG_OK) {
1228f3e7eeaSChristopher Kilgour 			(void) pcapng_close( handle );
1238f3e7eeaSChristopher Kilgour 		}
1248f3e7eeaSChristopher Kilgour 	}
1258f3e7eeaSChristopher Kilgour 
1268f3e7eeaSChristopher Kilgour 	return retval;
1278f3e7eeaSChristopher Kilgour }
1288f3e7eeaSChristopher Kilgour 
btbb_pcapng_create_file(const char * filename,const char * interface_desc,btbb_pcapng_handle ** ph)1298f3e7eeaSChristopher Kilgour int btbb_pcapng_create_file( const char *filename,
1308f3e7eeaSChristopher Kilgour 			     const char *interface_desc,
1318f3e7eeaSChristopher Kilgour 			     btbb_pcapng_handle ** ph )
1328f3e7eeaSChristopher Kilgour {
1338f3e7eeaSChristopher Kilgour 	int retval = PCAPNG_OK;
1348f3e7eeaSChristopher Kilgour 	PCAPNG_HANDLE * handle = malloc( sizeof(PCAPNG_HANDLE) );
1358f3e7eeaSChristopher Kilgour 	if (handle) {
1368f3e7eeaSChristopher Kilgour 		const option_header * popt = NULL;
1378f3e7eeaSChristopher Kilgour 		struct {
1388f3e7eeaSChristopher Kilgour 			option_header header;
1398f3e7eeaSChristopher Kilgour 			char desc[256];
1408f3e7eeaSChristopher Kilgour 		} ifopt = {
1418f3e7eeaSChristopher Kilgour 			.header = {
1428f3e7eeaSChristopher Kilgour 				.option_code = IF_DESCRIPTION,
1438f3e7eeaSChristopher Kilgour 			}
1448f3e7eeaSChristopher Kilgour 		};
1458f3e7eeaSChristopher Kilgour 		if (interface_desc) {
1468f3e7eeaSChristopher Kilgour 			(void) strncpy( &ifopt.desc[0], interface_desc, 256 );
1478f3e7eeaSChristopher Kilgour 			ifopt.desc[255] = '\0';
1488f3e7eeaSChristopher Kilgour 			ifopt.header.option_length = strlen( ifopt.desc );
1498f3e7eeaSChristopher Kilgour 			popt = (const option_header *) &ifopt;
1508f3e7eeaSChristopher Kilgour 		}
1518f3e7eeaSChristopher Kilgour 
1528f3e7eeaSChristopher Kilgour 		retval = -create_bredr_capture_file_single_interface( handle,
1538f3e7eeaSChristopher Kilgour 								      filename,
1548f3e7eeaSChristopher Kilgour 								      popt );
1558f3e7eeaSChristopher Kilgour 		if (retval == PCAPNG_OK) {
1568f3e7eeaSChristopher Kilgour 			*ph = (btbb_pcapng_handle *) handle;
1578f3e7eeaSChristopher Kilgour 		}
1588f3e7eeaSChristopher Kilgour 		else {
1598f3e7eeaSChristopher Kilgour 			free( handle );
1608f3e7eeaSChristopher Kilgour 		}
1618f3e7eeaSChristopher Kilgour 	}
1628f3e7eeaSChristopher Kilgour 	else {
1638f3e7eeaSChristopher Kilgour 		retval = -PCAPNG_NO_MEMORY;
1648f3e7eeaSChristopher Kilgour 	}
1658f3e7eeaSChristopher Kilgour 	return retval;
1668f3e7eeaSChristopher Kilgour }
1678f3e7eeaSChristopher Kilgour 
1688f3e7eeaSChristopher Kilgour static PCAPNG_RESULT
append_bredr_packet(PCAPNG_HANDLE * handle,pcapng_bredr_packet * pkt)1698f3e7eeaSChristopher Kilgour append_bredr_packet( PCAPNG_HANDLE * handle,
1708f3e7eeaSChristopher Kilgour 		     pcapng_bredr_packet * pkt )
1718f3e7eeaSChristopher Kilgour {
1728f3e7eeaSChristopher Kilgour 	return pcapng_append_packet( handle, ( const enhanced_packet_block *) pkt );
1738f3e7eeaSChristopher Kilgour }
1748f3e7eeaSChristopher Kilgour 
1758f3e7eeaSChristopher Kilgour 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)1768f3e7eeaSChristopher Kilgour assemble_pcapng_bredr_packet( pcapng_bredr_packet * pkt,
1778f3e7eeaSChristopher Kilgour 			      const uint32_t interface_id,
1788f3e7eeaSChristopher Kilgour 			      const uint64_t ns,
1798f3e7eeaSChristopher Kilgour 			      const uint32_t caplen,
1808f3e7eeaSChristopher Kilgour 			      const uint8_t rf_channel,
1818f3e7eeaSChristopher Kilgour 			      const int8_t signal_power,
1828f3e7eeaSChristopher Kilgour 			      const int8_t noise_power,
1838f3e7eeaSChristopher Kilgour 			      const uint8_t access_code_offenses,
1848f3e7eeaSChristopher Kilgour 			      const uint8_t payload_transport,
1858f3e7eeaSChristopher Kilgour 			      const uint8_t payload_rate,
1868f3e7eeaSChristopher Kilgour 			      const uint8_t corrected_header_bits,
1878f3e7eeaSChristopher Kilgour 			      const int16_t corrected_payload_bits,
1888f3e7eeaSChristopher Kilgour 			      const uint32_t lap,
1898f3e7eeaSChristopher Kilgour 			      const uint32_t ref_lap,
1908f3e7eeaSChristopher Kilgour                               const uint8_t ref_uap,
1918f3e7eeaSChristopher Kilgour 			      const uint32_t bt_header,
1928f3e7eeaSChristopher Kilgour 			      const uint16_t flags,
1930e05cc66SDominic Spill 			      const char * payload )
1948f3e7eeaSChristopher Kilgour {
195fa25b38eSSean Rivera 	uint32_t pcapng_caplen = sizeof(pcap_bluetooth_bredr_bb_header)
196fa25b38eSSean Rivera 				 - sizeof(pkt->bredr_bb_header.bredr_payload)
197fa25b38eSSean Rivera 				 + caplen;
1988f3e7eeaSChristopher Kilgour 	uint32_t block_length  = 4*((36+pcapng_caplen+3)/4);
1998f3e7eeaSChristopher Kilgour 	uint32_t reflapuap = (ref_lap&0xffffff) | (ref_uap<<24);
2008f3e7eeaSChristopher Kilgour 
2018f3e7eeaSChristopher Kilgour 	pkt->blk_header.block_type = BLOCK_TYPE_ENHANCED_PACKET;
2028f3e7eeaSChristopher Kilgour 	pkt->blk_header.block_total_length = block_length;
2038f3e7eeaSChristopher Kilgour 	pkt->blk_header.interface_id = interface_id;
2048f3e7eeaSChristopher Kilgour 	pkt->blk_header.timestamp_high = (uint32_t) (ns >> 32);
2058f3e7eeaSChristopher Kilgour 	pkt->blk_header.timestamp_low = (uint32_t) (ns & 0x0ffffffffull);
2068f3e7eeaSChristopher Kilgour 	pkt->blk_header.captured_len = pcapng_caplen;
2078f3e7eeaSChristopher Kilgour 	pkt->blk_header.packet_len = pcapng_caplen;
2088f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.rf_channel = rf_channel;
2098f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.signal_power = signal_power;
2108f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.noise_power = noise_power;
2118f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.access_code_offenses = access_code_offenses;
2128f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.payload_transport_rate =
2138f3e7eeaSChristopher Kilgour 		(payload_transport << 4) | payload_rate;
2148f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.corrected_header_bits = corrected_header_bits;
2158f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.corrected_payload_bits = htole16( corrected_payload_bits );
2168f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.lap = htole32( lap );
2178f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.ref_lap_uap = htole32( reflapuap );
218fa25b38eSSean Rivera 	pkt->bredr_bb_header.bt_header = htole32( bt_header );
2198f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.flags = htole16( flags );
2208f3e7eeaSChristopher Kilgour 	if (caplen) {
221fa25b38eSSean Rivera 		assert(caplen <= sizeof(pkt->bredr_bb_header.bredr_payload)); // caller ensures this, but to be safe..
222fa25b38eSSean Rivera 		(void) memcpy( &pkt->bredr_bb_header.bredr_payload[0], payload, caplen );
223fa25b38eSSean Rivera 		pkt->bredr_bb_header.flags |= htole16( BREDR_PAYLOAD_PRESENT );
2248f3e7eeaSChristopher Kilgour 	}
2258f3e7eeaSChristopher Kilgour 	else {
2268f3e7eeaSChristopher Kilgour 		pkt->bredr_bb_header.flags &= htole16( ~BREDR_PAYLOAD_PRESENT );
2278f3e7eeaSChristopher Kilgour 	}
2288f3e7eeaSChristopher Kilgour 	((uint32_t *)pkt)[block_length/4-2] = 0x00000000; /* no-options */
2298f3e7eeaSChristopher Kilgour 	((uint32_t *)pkt)[block_length/4-1] = block_length;
2308f3e7eeaSChristopher Kilgour }
2318f3e7eeaSChristopher Kilgour 
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)2328f3e7eeaSChristopher Kilgour int btbb_pcapng_append_packet(btbb_pcapng_handle * h, const uint64_t ns,
2338f3e7eeaSChristopher Kilgour 			      const int8_t sigdbm, const int8_t noisedbm,
2348f3e7eeaSChristopher Kilgour 			      const uint32_t reflap, const uint8_t refuap,
2358f3e7eeaSChristopher Kilgour 			      const btbb_packet *pkt)
2368f3e7eeaSChristopher Kilgour {
2378f3e7eeaSChristopher Kilgour 	uint16_t flags = BREDR_DEWHITENED | BREDR_SIGPOWER_VALID |
2388f3e7eeaSChristopher Kilgour 		((noisedbm < sigdbm) ? BREDR_NOISEPOWER_VALID : 0) |
2398f3e7eeaSChristopher Kilgour 		((reflap != LAP_ANY) ? BREDR_REFLAP_VALID : 0) |
2408f3e7eeaSChristopher Kilgour 		((refuap != UAP_ANY) ? BREDR_REFUAP_VALID : 0);
2410e05cc66SDominic Spill 	int caplen = btbb_packet_get_payload_length(pkt);
2420e05cc66SDominic Spill 	char payload_bytes[caplen];
2430e05cc66SDominic Spill 	btbb_get_payload_packed( pkt, &payload_bytes[0] );
2440e05cc66SDominic Spill 	caplen = MIN(BREDR_MAX_PAYLOAD, caplen);
2458f3e7eeaSChristopher Kilgour 	pcapng_bredr_packet pcapng_pkt;
2468f3e7eeaSChristopher Kilgour 	assemble_pcapng_bredr_packet( &pcapng_pkt,
2478f3e7eeaSChristopher Kilgour 				      0,
2488f3e7eeaSChristopher Kilgour 				      ns,
2498f3e7eeaSChristopher Kilgour 				      caplen,
2508f3e7eeaSChristopher Kilgour 				      btbb_packet_get_channel(pkt),
2518f3e7eeaSChristopher Kilgour 				      sigdbm,
2528f3e7eeaSChristopher Kilgour 				      noisedbm,
2538f3e7eeaSChristopher Kilgour 				      btbb_packet_get_ac_errors(pkt),
254f83b85cfSDominic Spill 				      btbb_packet_get_transport(pkt),
255f83b85cfSDominic Spill 				      btbb_packet_get_modulation(pkt),
2568f3e7eeaSChristopher Kilgour 				      0, /* TODO: corrected header bits */
2578f3e7eeaSChristopher Kilgour 				      0, /* TODO: corrected payload bits */
2588f3e7eeaSChristopher Kilgour 				      btbb_packet_get_lap(pkt),
2598f3e7eeaSChristopher Kilgour 				      reflap,
2608f3e7eeaSChristopher Kilgour 				      refuap,
2618f3e7eeaSChristopher Kilgour 				      btbb_packet_get_header_packed(pkt),
2628f3e7eeaSChristopher Kilgour 				      flags,
2638f3e7eeaSChristopher Kilgour 				      payload_bytes );
2648f3e7eeaSChristopher Kilgour 	return -append_bredr_packet( (PCAPNG_HANDLE *)h, &pcapng_pkt );
2658f3e7eeaSChristopher Kilgour }
2668f3e7eeaSChristopher Kilgour 
2678f3e7eeaSChristopher Kilgour 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)2688f3e7eeaSChristopher Kilgour record_bd_addr_info( PCAPNG_HANDLE * handle,
2698f3e7eeaSChristopher Kilgour 		     const uint64_t bd_addr,
2708f3e7eeaSChristopher Kilgour 		     const uint8_t  uap_mask,
2718f3e7eeaSChristopher Kilgour                      const uint8_t  nap_valid )
2728f3e7eeaSChristopher Kilgour {
2738f3e7eeaSChristopher Kilgour 	const bredr_br_addr_option bdopt = {
2748f3e7eeaSChristopher Kilgour 		.header = {
2758f3e7eeaSChristopher Kilgour 			.option_code = PCAPNG_BREDR_OPTION_BD_ADDR,
2768f3e7eeaSChristopher Kilgour 			.option_length = sizeof(bredr_br_addr_option),
2778f3e7eeaSChristopher Kilgour 		},
2788f3e7eeaSChristopher Kilgour 		.bd_addr_info = {
2798f3e7eeaSChristopher Kilgour 			.bd_addr = {
2808f3e7eeaSChristopher Kilgour 				((bd_addr>>0)  & 0xff),
2818f3e7eeaSChristopher Kilgour 				((bd_addr>>8)  & 0xff),
2828f3e7eeaSChristopher Kilgour 				((bd_addr>>16) & 0xff),
2838f3e7eeaSChristopher Kilgour 				((bd_addr>>24) & 0xff),
2848f3e7eeaSChristopher Kilgour 				((bd_addr>>32) & 0xff),
2858f3e7eeaSChristopher Kilgour 				((bd_addr>>40) & 0xff)
2868f3e7eeaSChristopher Kilgour 			},
2878f3e7eeaSChristopher Kilgour 			.uap_mask = uap_mask,
2888f3e7eeaSChristopher Kilgour 			.nap_valid = nap_valid,
2898f3e7eeaSChristopher Kilgour 		}
2908f3e7eeaSChristopher Kilgour 	};
2918f3e7eeaSChristopher Kilgour 	return pcapng_append_interface_option( handle,
2928f3e7eeaSChristopher Kilgour 					       (const option_header *) &bdopt );
2938f3e7eeaSChristopher Kilgour }
2948f3e7eeaSChristopher Kilgour 
btbb_pcapng_record_bdaddr(btbb_pcapng_handle * h,const uint64_t bdaddr,const uint8_t uapmask,const uint8_t napvalid)2958f3e7eeaSChristopher Kilgour int btbb_pcapng_record_bdaddr(btbb_pcapng_handle * h, const uint64_t bdaddr,
2968f3e7eeaSChristopher Kilgour                               const uint8_t uapmask, const uint8_t napvalid)
2978f3e7eeaSChristopher Kilgour {
2988f3e7eeaSChristopher Kilgour 	return -record_bd_addr_info( (PCAPNG_HANDLE *) h,
2998f3e7eeaSChristopher Kilgour 				     bdaddr, uapmask, napvalid );
3008f3e7eeaSChristopher Kilgour }
3018f3e7eeaSChristopher Kilgour 
3028f3e7eeaSChristopher Kilgour 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)3038f3e7eeaSChristopher Kilgour record_bredr_master_clock_info( PCAPNG_HANDLE * handle,
3048f3e7eeaSChristopher Kilgour 				const uint64_t bd_addr,
3058f3e7eeaSChristopher Kilgour 				const uint64_t ns,
3068f3e7eeaSChristopher Kilgour 				const uint32_t clk,
3078f3e7eeaSChristopher Kilgour 	                        const uint32_t clk_mask)
3088f3e7eeaSChristopher Kilgour {
3098f3e7eeaSChristopher Kilgour 	const bredr_clk_option mcopt = {
3108f3e7eeaSChristopher Kilgour 		.header = {
3118f3e7eeaSChristopher Kilgour 			.option_code = PCAPNG_BREDR_OPTION_MASTER_CLOCK_INFO,
3128f3e7eeaSChristopher Kilgour 			.option_length = sizeof(bredr_clk_option)
3138f3e7eeaSChristopher Kilgour 		},
3148f3e7eeaSChristopher Kilgour 		.clock_info = {
3158f3e7eeaSChristopher Kilgour 			.ts = ns,
3168f3e7eeaSChristopher Kilgour 			.lap_uap = htole32(bd_addr & 0xffffffff),
3178f3e7eeaSChristopher Kilgour 			.clk = clk,
3188f3e7eeaSChristopher Kilgour 			.clk_mask = clk_mask
3198f3e7eeaSChristopher Kilgour 		}
3208f3e7eeaSChristopher Kilgour 	};
3218f3e7eeaSChristopher Kilgour 	return pcapng_append_interface_option( handle,
3228f3e7eeaSChristopher Kilgour 					       (const option_header *) &mcopt );
3238f3e7eeaSChristopher Kilgour }
3248f3e7eeaSChristopher Kilgour 
btbb_pcapng_record_btclock(btbb_pcapng_handle * h,const uint64_t bdaddr,const uint64_t ns,const uint32_t clk,const uint32_t clkmask)3258f3e7eeaSChristopher Kilgour int btbb_pcapng_record_btclock(btbb_pcapng_handle * h, const uint64_t bdaddr,
3268f3e7eeaSChristopher Kilgour                                const uint64_t ns, const uint32_t clk,
3278f3e7eeaSChristopher Kilgour 			       const uint32_t clkmask)
3288f3e7eeaSChristopher Kilgour {
3298f3e7eeaSChristopher Kilgour 	return -record_bredr_master_clock_info( (PCAPNG_HANDLE *) h,
3308f3e7eeaSChristopher Kilgour 						bdaddr, ns, clk, clkmask );
3318f3e7eeaSChristopher Kilgour }
3328f3e7eeaSChristopher Kilgour 
btbb_pcapng_close(btbb_pcapng_handle * h)3338f3e7eeaSChristopher Kilgour int btbb_pcapng_close(btbb_pcapng_handle * h)
3348f3e7eeaSChristopher Kilgour {
3358f3e7eeaSChristopher Kilgour 	pcapng_close( (PCAPNG_HANDLE *) h );
3368f3e7eeaSChristopher Kilgour 	if (h) {
3378f3e7eeaSChristopher Kilgour 		free( h );
3388f3e7eeaSChristopher Kilgour 	}
3398f3e7eeaSChristopher Kilgour 	return -PCAPNG_INVALID_HANDLE;
3408f3e7eeaSChristopher Kilgour }
3418f3e7eeaSChristopher Kilgour 
3428f3e7eeaSChristopher Kilgour /* --------------------------------- Low Energy ---------------------------- */
3438f3e7eeaSChristopher Kilgour 
3448f3e7eeaSChristopher Kilgour static PCAPNG_RESULT
create_le_capture_file_single_interface(PCAPNG_HANDLE * handle,const char * filename,const option_header * interface_options)3458f3e7eeaSChristopher Kilgour create_le_capture_file_single_interface( PCAPNG_HANDLE * handle,
3468f3e7eeaSChristopher Kilgour 					 const char * filename,
3478f3e7eeaSChristopher Kilgour 					 const option_header * interface_options )
3488f3e7eeaSChristopher Kilgour {
3498f3e7eeaSChristopher Kilgour 	PCAPNG_RESULT retval = PCAPNG_OK;
3508f3e7eeaSChristopher Kilgour 
3518f3e7eeaSChristopher Kilgour 	retval = pcapng_create( handle,
3528f3e7eeaSChristopher Kilgour 				filename,
3538f3e7eeaSChristopher Kilgour 				(const option_header *) &libbtbb_section_options,
3548f3e7eeaSChristopher Kilgour 				(size_t) getpagesize( ),
3558f3e7eeaSChristopher Kilgour 				DLT_BLUETOOTH_LE_LL_WITH_PHDR,
3568f3e7eeaSChristopher Kilgour 				64,
3578f3e7eeaSChristopher Kilgour 				interface_options,
3588f3e7eeaSChristopher Kilgour 				(size_t) getpagesize( ) );
3598f3e7eeaSChristopher Kilgour 
3608f3e7eeaSChristopher Kilgour 	if (retval == PCAPNG_OK) {
3618f3e7eeaSChristopher Kilgour 		/* if there is no timestamp resolution alread in the
3628f3e7eeaSChristopher Kilgour 		   interface options, record nanosecond resolution */
3638f3e7eeaSChristopher Kilgour 		retval = check_and_fix_tsresol( handle, interface_options );
3648f3e7eeaSChristopher Kilgour 
3658f3e7eeaSChristopher Kilgour 		if (retval != PCAPNG_OK) {
3668f3e7eeaSChristopher Kilgour 			(void) pcapng_close( handle );
3678f3e7eeaSChristopher Kilgour 		}
3688f3e7eeaSChristopher Kilgour 	}
3698f3e7eeaSChristopher Kilgour 
3708f3e7eeaSChristopher Kilgour 	return retval;
3718f3e7eeaSChristopher Kilgour }
3728f3e7eeaSChristopher Kilgour 
3738f3e7eeaSChristopher Kilgour int
lell_pcapng_create_file(const char * filename,const char * interface_desc,lell_pcapng_handle ** ph)3748f3e7eeaSChristopher Kilgour lell_pcapng_create_file(const char *filename, const char *interface_desc,
3758f3e7eeaSChristopher Kilgour 			lell_pcapng_handle ** ph)
3768f3e7eeaSChristopher Kilgour {
3778f3e7eeaSChristopher Kilgour 	int retval = PCAPNG_OK;
3788f3e7eeaSChristopher Kilgour 	PCAPNG_HANDLE * handle = malloc( sizeof(PCAPNG_HANDLE) );
3798f3e7eeaSChristopher Kilgour 	if (handle) {
3808f3e7eeaSChristopher Kilgour 		const option_header * popt = NULL;
3818f3e7eeaSChristopher Kilgour 		struct {
3828f3e7eeaSChristopher Kilgour 			option_header header;
3838f3e7eeaSChristopher Kilgour 			char desc[256];
3848f3e7eeaSChristopher Kilgour 		} ifopt = {
3858f3e7eeaSChristopher Kilgour 			.header = {
3868f3e7eeaSChristopher Kilgour 				.option_code = IF_DESCRIPTION,
3878f3e7eeaSChristopher Kilgour 			}
3888f3e7eeaSChristopher Kilgour 		};
3898f3e7eeaSChristopher Kilgour 		if (interface_desc) {
3908f3e7eeaSChristopher Kilgour 			(void) strncpy( &ifopt.desc[0], interface_desc, 256 );
3918f3e7eeaSChristopher Kilgour 			ifopt.desc[255] = '\0';
3928f3e7eeaSChristopher Kilgour 			ifopt.header.option_length = strlen( ifopt.desc );
3938f3e7eeaSChristopher Kilgour 			popt = (const option_header *) &ifopt;
3948f3e7eeaSChristopher Kilgour 		}
3958f3e7eeaSChristopher Kilgour 
3968f3e7eeaSChristopher Kilgour 		retval = -create_le_capture_file_single_interface( handle,
3978f3e7eeaSChristopher Kilgour 								   filename,
3988f3e7eeaSChristopher Kilgour 								   popt );
3998f3e7eeaSChristopher Kilgour 		if (retval == PCAPNG_OK) {
4008f3e7eeaSChristopher Kilgour 			*ph = (lell_pcapng_handle *) handle;
4018f3e7eeaSChristopher Kilgour 		}
4028f3e7eeaSChristopher Kilgour 		else {
4038f3e7eeaSChristopher Kilgour 			free( handle );
4048f3e7eeaSChristopher Kilgour 		}
4058f3e7eeaSChristopher Kilgour 	}
4068f3e7eeaSChristopher Kilgour 	else {
4078f3e7eeaSChristopher Kilgour 		retval = -PCAPNG_NO_MEMORY;
4088f3e7eeaSChristopher Kilgour 	}
4098f3e7eeaSChristopher Kilgour 	return retval;
4108f3e7eeaSChristopher Kilgour }
4118f3e7eeaSChristopher Kilgour 
4128f3e7eeaSChristopher Kilgour static PCAPNG_RESULT
append_le_packet(PCAPNG_HANDLE * handle,pcapng_le_packet * pkt)4138f3e7eeaSChristopher Kilgour append_le_packet( PCAPNG_HANDLE * handle,
4148f3e7eeaSChristopher Kilgour 		  pcapng_le_packet * pkt )
4158f3e7eeaSChristopher Kilgour {
4168f3e7eeaSChristopher Kilgour 	return pcapng_append_packet( handle, ( const enhanced_packet_block *) pkt );
4178f3e7eeaSChristopher Kilgour }
4188f3e7eeaSChristopher Kilgour 
419871ad2c0STheodore A. Roth /* Size of a PCAPNG enhanced packet block with no packet data.
420871ad2c0STheodore A. Roth    NOTE: The pcap_bluetooth_le_ll_header is part of the packet data of
421871ad2c0STheodore A. Roth    the enhanced block. */
422871ad2c0STheodore A. Roth #define PCAPNG_ENHANCED_BLK_SZ 36
423871ad2c0STheodore A. Roth 
4248f3e7eeaSChristopher Kilgour 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)4258f3e7eeaSChristopher Kilgour assemble_pcapng_le_packet( pcapng_le_packet * pkt,
4268f3e7eeaSChristopher Kilgour 			   const uint32_t interface_id,
4278f3e7eeaSChristopher Kilgour 			   const uint64_t ns,
4288f3e7eeaSChristopher Kilgour 			   const uint32_t caplen,
4298f3e7eeaSChristopher Kilgour 			   const uint8_t rf_channel,
4308f3e7eeaSChristopher Kilgour 			   const int8_t signal_power,
4318f3e7eeaSChristopher Kilgour 			   const int8_t noise_power,
4328f3e7eeaSChristopher Kilgour 			   const uint8_t access_address_offenses,
4338f3e7eeaSChristopher Kilgour 			   const uint32_t ref_access_address,
4348f3e7eeaSChristopher Kilgour 			   const uint16_t flags,
4358f3e7eeaSChristopher Kilgour 			   const uint8_t * lepkt )
4368f3e7eeaSChristopher Kilgour {
4378f3e7eeaSChristopher Kilgour 	uint32_t pcapng_caplen = sizeof(pcap_bluetooth_le_ll_header)+caplen;
438871ad2c0STheodore A. Roth 	uint32_t block_length  = 4*((PCAPNG_ENHANCED_BLK_SZ+pcapng_caplen+3)/4);
4398f3e7eeaSChristopher Kilgour 
440*fa8f3df8SMike Ryan 	// TODO this should never happen, but handle it if it does
4411ce7419bSTheodore A. Roth 	assert(caplen <= LE_MAX_PAYLOAD);
4421ce7419bSTheodore A. Roth 
4438f3e7eeaSChristopher Kilgour 	pkt->blk_header.block_type = BLOCK_TYPE_ENHANCED_PACKET;
4448f3e7eeaSChristopher Kilgour 	pkt->blk_header.block_total_length = block_length;
4458f3e7eeaSChristopher Kilgour 	pkt->blk_header.interface_id = interface_id;
4468f3e7eeaSChristopher Kilgour 	pkt->blk_header.timestamp_high = (uint32_t) (ns >> 32);
4478f3e7eeaSChristopher Kilgour 	pkt->blk_header.timestamp_low = (uint32_t) (ns & 0x0ffffffffull);
4488f3e7eeaSChristopher Kilgour 	pkt->blk_header.captured_len = pcapng_caplen;
4498f3e7eeaSChristopher Kilgour 	pkt->blk_header.packet_len = pcapng_caplen;
4508f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.rf_channel = rf_channel;
4518f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.signal_power = signal_power;
4528f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.noise_power = noise_power;
4538f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.access_address_offenses = access_address_offenses;
4548f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.ref_access_address = htole32( ref_access_address );
4558f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.flags = htole16( flags );
4568f3e7eeaSChristopher Kilgour 	(void) memcpy( &pkt->le_packet[0], lepkt, caplen );
4578f3e7eeaSChristopher Kilgour 	((uint32_t *)pkt)[block_length/4-2] = 0x00000000; /* no-options */
4588f3e7eeaSChristopher Kilgour 	((uint32_t *)pkt)[block_length/4-1] = block_length;
4598f3e7eeaSChristopher Kilgour }
4608f3e7eeaSChristopher Kilgour 
4618f3e7eeaSChristopher Kilgour 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)4628f3e7eeaSChristopher Kilgour lell_pcapng_append_packet(lell_pcapng_handle * h, const uint64_t ns,
4638f3e7eeaSChristopher Kilgour 			  const int8_t sigdbm, const int8_t noisedbm,
4648f3e7eeaSChristopher Kilgour 			  const uint32_t refAA, const lell_packet *pkt)
4658f3e7eeaSChristopher Kilgour {
4668f3e7eeaSChristopher Kilgour 	uint16_t flags = LE_DEWHITENED | LE_AA_OFFENSES_VALID |
4678f3e7eeaSChristopher Kilgour 		LE_SIGPOWER_VALID |
4688f3e7eeaSChristopher Kilgour 		((noisedbm < sigdbm) ? LE_NOISEPOWER_VALID : 0) |
4698f3e7eeaSChristopher Kilgour 		(lell_packet_is_data(pkt) ? 0 : LE_REF_AA_VALID);
4708f3e7eeaSChristopher Kilgour 	pcapng_le_packet pcapng_pkt;
471871ad2c0STheodore A. Roth 
472871ad2c0STheodore A. Roth 	/* The extra 9 bytes added to the packet length are for:
473871ad2c0STheodore A. Roth 	   4 bytes for Access Address
474871ad2c0STheodore A. Roth 	   2 bytes for PDU header
475871ad2c0STheodore A. Roth 	   3 bytes for CRC */
4768f3e7eeaSChristopher Kilgour 	assemble_pcapng_le_packet( &pcapng_pkt,
4778f3e7eeaSChristopher Kilgour 				   0,
4788f3e7eeaSChristopher Kilgour 				   ns,
4798f3e7eeaSChristopher Kilgour 				   9+pkt->length,
4808f3e7eeaSChristopher Kilgour 				   pkt->channel_k,
4818f3e7eeaSChristopher Kilgour 				   sigdbm,
4828f3e7eeaSChristopher Kilgour 				   noisedbm,
4838f3e7eeaSChristopher Kilgour 				   pkt->access_address_offenses,
4848f3e7eeaSChristopher Kilgour 				   refAA,
4858f3e7eeaSChristopher Kilgour 				   flags,
4868f3e7eeaSChristopher Kilgour 				   &pkt->symbols[0] );
4878f3e7eeaSChristopher Kilgour 	int retval = -append_le_packet( (PCAPNG_HANDLE *) h, &pcapng_pkt );
4888f3e7eeaSChristopher Kilgour 	if ((retval == 0) && !lell_packet_is_data(pkt) && (pkt->adv_type == CONNECT_REQ)) {
4898f3e7eeaSChristopher Kilgour 		(void) lell_pcapng_record_connect_req(h, ns, &pkt->symbols[0]);
4908f3e7eeaSChristopher Kilgour 	}
4918f3e7eeaSChristopher Kilgour 	return retval;
4928f3e7eeaSChristopher Kilgour }
4938f3e7eeaSChristopher Kilgour 
4948f3e7eeaSChristopher Kilgour static PCAPNG_RESULT
record_le_connect_req_info(PCAPNG_HANDLE * handle,const uint64_t ns,const uint8_t * pdu)4958f3e7eeaSChristopher Kilgour record_le_connect_req_info( PCAPNG_HANDLE * handle,
4968f3e7eeaSChristopher Kilgour 			    const uint64_t ns,
4978f3e7eeaSChristopher Kilgour 			    const uint8_t * pdu )
4988f3e7eeaSChristopher Kilgour {
4998f3e7eeaSChristopher Kilgour 	le_ll_connection_info_option cropt = {
5008f3e7eeaSChristopher Kilgour 		.header = {
5018f3e7eeaSChristopher Kilgour 			.option_code = PCAPNG_LE_LL_CONNECTION_INFO,
5028f3e7eeaSChristopher Kilgour 			.option_length = sizeof(le_ll_connection_info_option)
5038f3e7eeaSChristopher Kilgour 		},
5048f3e7eeaSChristopher Kilgour 		.connection_info = {
5058f3e7eeaSChristopher Kilgour 			.ns = ns
5068f3e7eeaSChristopher Kilgour 		}
5078f3e7eeaSChristopher Kilgour 	};
5088f3e7eeaSChristopher Kilgour 	(void) memcpy( &cropt.connection_info.pdu.bytes[0], pdu, 34 );
5098f3e7eeaSChristopher Kilgour 	return pcapng_append_interface_option( handle,
5108f3e7eeaSChristopher Kilgour 					       (const option_header *) &cropt );
5118f3e7eeaSChristopher Kilgour }
5128f3e7eeaSChristopher Kilgour 
5138f3e7eeaSChristopher Kilgour int
lell_pcapng_record_connect_req(lell_pcapng_handle * h,const uint64_t ns,const uint8_t * pdu)5148f3e7eeaSChristopher Kilgour lell_pcapng_record_connect_req(lell_pcapng_handle * h, const uint64_t ns,
5158f3e7eeaSChristopher Kilgour 			       const uint8_t * pdu)
5168f3e7eeaSChristopher Kilgour {
5178f3e7eeaSChristopher Kilgour 	return -record_le_connect_req_info( (PCAPNG_HANDLE *) h, ns, pdu );
5188f3e7eeaSChristopher Kilgour }
5198f3e7eeaSChristopher Kilgour 
lell_pcapng_close(lell_pcapng_handle * h)5208f3e7eeaSChristopher Kilgour int lell_pcapng_close(lell_pcapng_handle *h)
5218f3e7eeaSChristopher Kilgour {
5228f3e7eeaSChristopher Kilgour 	pcapng_close( (PCAPNG_HANDLE *) h );
5238f3e7eeaSChristopher Kilgour 	if (h) {
5248f3e7eeaSChristopher Kilgour 		free( h );
5258f3e7eeaSChristopher Kilgour 	}
5268f3e7eeaSChristopher Kilgour 	return -PCAPNG_INVALID_HANDLE;
5278f3e7eeaSChristopher Kilgour }
528