xref: /libbtbb/lib/src/pcap.c (revision 8a8fc94e322282f43246b109ed9d785c28f21106)
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 #include "bluetooth_le_packet.h"
23f83b85cfSDominic Spill #include "bluetooth_packet.h"
248f3e7eeaSChristopher Kilgour #include "btbb.h"
258f3e7eeaSChristopher Kilgour #include "pcap-common.h"
268f3e7eeaSChristopher Kilgour 
278f3e7eeaSChristopher Kilgour #include <stdlib.h>
288f3e7eeaSChristopher Kilgour #include <string.h>
29fcf7b60bSDominic Spill #include <assert.h>
30*8a8fc94eSlistout #include <sys/types.h>
318f3e7eeaSChristopher Kilgour 
328f3e7eeaSChristopher Kilgour typedef enum {
338f3e7eeaSChristopher Kilgour 	PCAP_OK = 0,
348f3e7eeaSChristopher Kilgour 	PCAP_INVALID_HANDLE,
358f3e7eeaSChristopher Kilgour 	PCAP_FILE_NOT_ALLOWED,
368f3e7eeaSChristopher Kilgour 	PCAP_NO_MEMORY,
378f3e7eeaSChristopher Kilgour } PCAP_RESULT;
388f3e7eeaSChristopher Kilgour 
39c3b616c1SMike Ryan typedef struct __attribute__((packed)) pcap_hdr_s {
40c3b616c1SMike Ryan 	uint32_t magic_number;   /* magic number */
41c3b616c1SMike Ryan 	uint16_t version_major;  /* major version number */
42c3b616c1SMike Ryan 	uint16_t version_minor;  /* minor version number */
43c3b616c1SMike Ryan 	int32_t  thiszone;       /* GMT to local correction */
44c3b616c1SMike Ryan 	uint32_t sigfigs;        /* accuracy of timestamps */
45c3b616c1SMike Ryan 	uint32_t snaplen;        /* max length of captured packets, in octets */
46c3b616c1SMike Ryan 	uint32_t network;        /* data link type */
47c3b616c1SMike Ryan } pcap_hdr_t;
48c3b616c1SMike Ryan 
btbb_pcap_open(const char * filename,uint32_t dlt,uint32_t snaplen)49c3b616c1SMike Ryan FILE *btbb_pcap_open(const char *filename, uint32_t dlt, uint32_t snaplen) {
50c3b616c1SMike Ryan 	pcap_hdr_t pcap_header = {
51f1381c6fSTobias Svehagen 		.magic_number = 0xa1b23c4d,
52c3b616c1SMike Ryan 		.version_major = 2,
53c3b616c1SMike Ryan 		.version_minor = 4,
54c3b616c1SMike Ryan 		.thiszone = 0,
55c3b616c1SMike Ryan 		.sigfigs = 0,
56c3b616c1SMike Ryan 		.snaplen = snaplen,
57c3b616c1SMike Ryan 		.network = dlt,
58c3b616c1SMike Ryan 	};
59c3b616c1SMike Ryan 
60c3b616c1SMike Ryan 	FILE *pcap_file = fopen(filename, "w");
61c3b616c1SMike Ryan 	if (pcap_file == NULL) return NULL;
62c3b616c1SMike Ryan 
63c3b616c1SMike Ryan 	fwrite(&pcap_header, sizeof(pcap_header), 1, pcap_file);
64c3b616c1SMike Ryan 
65c3b616c1SMike Ryan 	return pcap_file;
66c3b616c1SMike Ryan }
67c3b616c1SMike Ryan 
688f3e7eeaSChristopher Kilgour /* BT BR/EDR support */
698f3e7eeaSChristopher Kilgour 
70d9528177SMike Ryan struct btbb_pcap_handle {
71c3b616c1SMike Ryan 	FILE *pcap_file;
72d9528177SMike Ryan };
738f3e7eeaSChristopher Kilgour 
748f3e7eeaSChristopher Kilgour int
btbb_pcap_create_file(const char * filename,btbb_pcap_handle ** ph)758f3e7eeaSChristopher Kilgour btbb_pcap_create_file(const char *filename, btbb_pcap_handle ** ph)
768f3e7eeaSChristopher Kilgour {
778f3e7eeaSChristopher Kilgour 	int retval = 0;
788f3e7eeaSChristopher Kilgour 	btbb_pcap_handle * handle = malloc( sizeof(btbb_pcap_handle) );
798f3e7eeaSChristopher Kilgour 	if (handle) {
808f3e7eeaSChristopher Kilgour 		memset(handle, 0, sizeof(*handle));
81c3b616c1SMike Ryan 		handle->pcap_file = btbb_pcap_open(filename, DLT_BLUETOOTH_BREDR_BB,
82c3b616c1SMike Ryan 											BREDR_MAX_PAYLOAD);
83c3b616c1SMike Ryan 		if (handle->pcap_file) {
848f3e7eeaSChristopher Kilgour 			*ph = handle;
858f3e7eeaSChristopher Kilgour 		}
868f3e7eeaSChristopher Kilgour 		else {
87c3b616c1SMike Ryan 			perror("PCAP error:");
888f3e7eeaSChristopher Kilgour 			retval = -PCAP_FILE_NOT_ALLOWED;
898f3e7eeaSChristopher Kilgour 			goto fail;
908f3e7eeaSChristopher Kilgour 		}
918f3e7eeaSChristopher Kilgour 	}
928f3e7eeaSChristopher Kilgour 	else {
938f3e7eeaSChristopher Kilgour 		retval = -PCAP_NO_MEMORY;
948f3e7eeaSChristopher Kilgour 		goto fail;
958f3e7eeaSChristopher Kilgour 	}
968f3e7eeaSChristopher Kilgour 	return retval;
978f3e7eeaSChristopher Kilgour fail:
988f3e7eeaSChristopher Kilgour 	(void) btbb_pcap_close( handle );
998f3e7eeaSChristopher Kilgour 	return retval;
1008f3e7eeaSChristopher Kilgour }
1018f3e7eeaSChristopher Kilgour 
102c3b616c1SMike Ryan typedef struct __attribute__((packed)) pcaprec_hdr_s {
10341e09bc5SDominic Spill 	uint32_t ts_sec;         /* timestamp seconds */
10441e09bc5SDominic Spill 	uint32_t ts_usec;        /* timestamp microseconds */
10541e09bc5SDominic Spill 	uint32_t incl_len;       /* number of octets of packet saved in file */
10641e09bc5SDominic Spill 	uint32_t orig_len;       /* actual length of packet */
107c3b616c1SMike Ryan } pcaprec_hdr_t;
108c3b616c1SMike Ryan 
1098f3e7eeaSChristopher Kilgour typedef struct {
110c3b616c1SMike Ryan 	pcaprec_hdr_t pcap_header;
1118f3e7eeaSChristopher Kilgour 	pcap_bluetooth_bredr_bb_header bredr_bb_header;
1128f3e7eeaSChristopher Kilgour } pcap_bredr_packet;
1138f3e7eeaSChristopher Kilgour 
btbb_pcap_dump(FILE * file,pcaprec_hdr_t * pcap_header,u_char * data)114c3b616c1SMike Ryan void btbb_pcap_dump(FILE *file, pcaprec_hdr_t *pcap_header, u_char *data) {
115c3b616c1SMike Ryan 	fwrite(pcap_header, sizeof(*pcap_header), 1, file);
116c3b616c1SMike Ryan 	fwrite(data, pcap_header->incl_len, 1, file);
117c3b616c1SMike Ryan 	fflush(file);
118c3b616c1SMike Ryan }
119c3b616c1SMike Ryan 
1208f3e7eeaSChristopher Kilgour static void
assemble_pcapng_bredr_packet(pcap_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 uint8_t * payload)1218f3e7eeaSChristopher Kilgour assemble_pcapng_bredr_packet( pcap_bredr_packet * pkt,
122d9528177SMike Ryan 			      const uint32_t interface_id __attribute__((unused)),
1238f3e7eeaSChristopher Kilgour 			      const uint64_t ns,
1248f3e7eeaSChristopher Kilgour 			      const uint32_t caplen,
1258f3e7eeaSChristopher Kilgour 			      const uint8_t rf_channel,
1268f3e7eeaSChristopher Kilgour 			      const int8_t signal_power,
1278f3e7eeaSChristopher Kilgour 			      const int8_t noise_power,
1288f3e7eeaSChristopher Kilgour 			      const uint8_t access_code_offenses,
1298f3e7eeaSChristopher Kilgour 			      const uint8_t payload_transport,
1308f3e7eeaSChristopher Kilgour 			      const uint8_t payload_rate,
1318f3e7eeaSChristopher Kilgour 			      const uint8_t corrected_header_bits,
1328f3e7eeaSChristopher Kilgour 			      const int16_t corrected_payload_bits,
1338f3e7eeaSChristopher Kilgour 			      const uint32_t lap,
1348f3e7eeaSChristopher Kilgour 			      const uint32_t ref_lap,
1358f3e7eeaSChristopher Kilgour 			      const uint8_t ref_uap,
1368f3e7eeaSChristopher Kilgour 			      const uint32_t bt_header,
1378f3e7eeaSChristopher Kilgour 			      const uint16_t flags,
1388f3e7eeaSChristopher Kilgour 			      const uint8_t * payload )
1398f3e7eeaSChristopher Kilgour {
1404de3c54fSSean Rivera 	uint32_t pcap_caplen = sizeof(pcap_bluetooth_bredr_bb_header) -
1414de3c54fSSean Rivera 				sizeof(pkt->bredr_bb_header.bredr_payload)
1424de3c54fSSean Rivera 				+ caplen;
1438f3e7eeaSChristopher Kilgour 	uint32_t reflapuap = (ref_lap&0xffffff) | (ref_uap<<24);
1448f3e7eeaSChristopher Kilgour 
145c3b616c1SMike Ryan 	pkt->pcap_header.ts_sec  = ns / 1000000000ull;
146c3b616c1SMike Ryan 	pkt->pcap_header.ts_usec = ns % 1000000000ull;
147c3b616c1SMike Ryan 	pkt->pcap_header.incl_len = pcap_caplen;
148c3b616c1SMike Ryan 	pkt->pcap_header.orig_len = pcap_caplen;
1498f3e7eeaSChristopher Kilgour 
1508f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.rf_channel = rf_channel;
1518f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.signal_power = signal_power;
1528f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.noise_power = noise_power;
1538f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.access_code_offenses = access_code_offenses;
1548f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.payload_transport_rate =
1558f3e7eeaSChristopher Kilgour 		(payload_transport << 4) | payload_rate;
1568f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.corrected_header_bits = corrected_header_bits;
1578f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.corrected_payload_bits = htole16( corrected_payload_bits );
1588f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.lap = htole32( lap );
1598f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.ref_lap_uap = htole32( reflapuap );
1608243a598SSean Rivera 	pkt->bredr_bb_header.bt_header = htole32( bt_header );
1618f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.flags = htole16( flags );
1628f3e7eeaSChristopher Kilgour 	if (caplen) {
1638243a598SSean Rivera 		assert(caplen <= sizeof(pkt->bredr_bb_header.bredr_payload)); // caller ensures this, but to be safe..
1648243a598SSean Rivera 		(void) memcpy( &pkt->bredr_bb_header.bredr_payload[0], payload, caplen );
165cb105de6SSean Rivera 		pkt->bredr_bb_header.flags |= htole16( BREDR_PAYLOAD_PRESENT );
1668f3e7eeaSChristopher Kilgour 	}
1678f3e7eeaSChristopher Kilgour 	else {
1688f3e7eeaSChristopher Kilgour 		pkt->bredr_bb_header.flags &= htole16( ~BREDR_PAYLOAD_PRESENT );
1698f3e7eeaSChristopher Kilgour 	}
1708f3e7eeaSChristopher Kilgour }
1718f3e7eeaSChristopher Kilgour 
1728f3e7eeaSChristopher Kilgour int
btbb_pcap_append_packet(btbb_pcap_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)1738f3e7eeaSChristopher Kilgour btbb_pcap_append_packet(btbb_pcap_handle * h, const uint64_t ns,
1748f3e7eeaSChristopher Kilgour 			const int8_t sigdbm, const int8_t noisedbm,
1758f3e7eeaSChristopher Kilgour 			const uint32_t reflap, const uint8_t refuap,
1768f3e7eeaSChristopher Kilgour 			const btbb_packet *pkt)
1778f3e7eeaSChristopher Kilgour {
178c3b616c1SMike Ryan 	if (h && h->pcap_file) {
1798f3e7eeaSChristopher Kilgour 		uint16_t flags = BREDR_DEWHITENED | BREDR_SIGPOWER_VALID |
1808f3e7eeaSChristopher Kilgour 			((noisedbm < sigdbm) ? BREDR_NOISEPOWER_VALID : 0) |
1818f3e7eeaSChristopher Kilgour 			((reflap != LAP_ANY) ? BREDR_REFLAP_VALID : 0) |
1828f3e7eeaSChristopher Kilgour 			((refuap != UAP_ANY) ? BREDR_REFUAP_VALID : 0);
1830e05cc66SDominic Spill 		uint32_t caplen = (uint32_t) btbb_packet_get_payload_length(pkt);
1840e05cc66SDominic Spill 		uint8_t payload_bytes[caplen];
1850e05cc66SDominic Spill 		btbb_get_payload_packed( pkt, (char *) &payload_bytes[0] );
1860e05cc66SDominic Spill 		caplen = MIN(BREDR_MAX_PAYLOAD, caplen);
1878f3e7eeaSChristopher Kilgour 		pcap_bredr_packet pcap_pkt;
1888f3e7eeaSChristopher Kilgour 		assemble_pcapng_bredr_packet( &pcap_pkt,
1898f3e7eeaSChristopher Kilgour 					      0,
1908f3e7eeaSChristopher Kilgour 					      ns,
1918f3e7eeaSChristopher Kilgour 					      caplen,
1928f3e7eeaSChristopher Kilgour 					      btbb_packet_get_channel(pkt),
1938f3e7eeaSChristopher Kilgour 					      sigdbm,
1948f3e7eeaSChristopher Kilgour 					      noisedbm,
1958f3e7eeaSChristopher Kilgour 					      btbb_packet_get_ac_errors(pkt),
196f83b85cfSDominic Spill 						  btbb_packet_get_transport(pkt),
197f83b85cfSDominic Spill 						  btbb_packet_get_modulation(pkt),
1988f3e7eeaSChristopher Kilgour 					      0, /* TODO: corrected header bits */
1998f3e7eeaSChristopher Kilgour 					      0, /* TODO: corrected payload bits */
2008f3e7eeaSChristopher Kilgour 					      btbb_packet_get_lap(pkt),
2018f3e7eeaSChristopher Kilgour 					      reflap,
2028f3e7eeaSChristopher Kilgour 					      refuap,
2038f3e7eeaSChristopher Kilgour 					      btbb_packet_get_header_packed(pkt),
2048f3e7eeaSChristopher Kilgour 					      flags,
2058f3e7eeaSChristopher Kilgour 					      payload_bytes );
206c3b616c1SMike Ryan 		btbb_pcap_dump(h->pcap_file, &pcap_pkt.pcap_header, (u_char *)&pcap_pkt.bredr_bb_header);
2078f3e7eeaSChristopher Kilgour 		return 0;
2088f3e7eeaSChristopher Kilgour 	}
2098f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
2108f3e7eeaSChristopher Kilgour }
2118f3e7eeaSChristopher Kilgour 
2128f3e7eeaSChristopher Kilgour int
btbb_pcap_close(btbb_pcap_handle * h)2138f3e7eeaSChristopher Kilgour btbb_pcap_close(btbb_pcap_handle * h)
2148f3e7eeaSChristopher Kilgour {
215c3b616c1SMike Ryan 	if (h && h->pcap_file) {
216c3b616c1SMike Ryan 		fclose(h->pcap_file);
2178f3e7eeaSChristopher Kilgour 	}
2188f3e7eeaSChristopher Kilgour 	if (h) {
2198f3e7eeaSChristopher Kilgour 		free(h);
2208f3e7eeaSChristopher Kilgour 		return 0;
2218f3e7eeaSChristopher Kilgour 	}
2228f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
2238f3e7eeaSChristopher Kilgour }
2248f3e7eeaSChristopher Kilgour 
2258f3e7eeaSChristopher Kilgour /* BTLE support */
2268f3e7eeaSChristopher Kilgour 
227d9528177SMike Ryan struct lell_pcap_handle {
228c3b616c1SMike Ryan 	FILE *pcap_file;
2298f3e7eeaSChristopher Kilgour 	int dlt;
2308f3e7eeaSChristopher Kilgour 	uint8_t btle_ppi_version;
231d9528177SMike Ryan };
2328f3e7eeaSChristopher Kilgour 
2338f3e7eeaSChristopher Kilgour static int
lell_pcap_create_file_dlt(const char * filename,int dlt,lell_pcap_handle ** ph)2348f3e7eeaSChristopher Kilgour lell_pcap_create_file_dlt(const char *filename, int dlt, lell_pcap_handle ** ph)
2358f3e7eeaSChristopher Kilgour {
2368f3e7eeaSChristopher Kilgour 	int retval = 0;
2378f3e7eeaSChristopher Kilgour 	lell_pcap_handle * handle = malloc( sizeof(lell_pcap_handle) );
2388f3e7eeaSChristopher Kilgour 	if (handle) {
2398f3e7eeaSChristopher Kilgour 		memset(handle, 0, sizeof(*handle));
240c3b616c1SMike Ryan 		handle->pcap_file = btbb_pcap_open(filename, dlt, BREDR_MAX_PAYLOAD);
241c3b616c1SMike Ryan 		if (handle->pcap_file) {
2428f3e7eeaSChristopher Kilgour 			handle->dlt = dlt;
2438f3e7eeaSChristopher Kilgour 			*ph = handle;
2448f3e7eeaSChristopher Kilgour 		}
2458f3e7eeaSChristopher Kilgour 		else {
2468f3e7eeaSChristopher Kilgour 			retval = -PCAP_FILE_NOT_ALLOWED;
2478f3e7eeaSChristopher Kilgour 			goto fail;
2488f3e7eeaSChristopher Kilgour 		}
2498f3e7eeaSChristopher Kilgour 	}
2508f3e7eeaSChristopher Kilgour 	else {
2518f3e7eeaSChristopher Kilgour 		retval = -PCAP_NO_MEMORY;
2528f3e7eeaSChristopher Kilgour 		goto fail;
2538f3e7eeaSChristopher Kilgour 	}
2548f3e7eeaSChristopher Kilgour 	return retval;
2558f3e7eeaSChristopher Kilgour fail:
2568f3e7eeaSChristopher Kilgour 	(void) lell_pcap_close( handle );
2578f3e7eeaSChristopher Kilgour 	return retval;
2588f3e7eeaSChristopher Kilgour }
2598f3e7eeaSChristopher Kilgour 
2608f3e7eeaSChristopher Kilgour int
lell_pcap_create_file(const char * filename,lell_pcap_handle ** ph)2618f3e7eeaSChristopher Kilgour lell_pcap_create_file(const char *filename, lell_pcap_handle ** ph)
2628f3e7eeaSChristopher Kilgour {
2638f3e7eeaSChristopher Kilgour 	return lell_pcap_create_file_dlt(filename, DLT_BLUETOOTH_LE_LL_WITH_PHDR, ph);
2648f3e7eeaSChristopher Kilgour }
2658f3e7eeaSChristopher Kilgour 
2668f3e7eeaSChristopher Kilgour int
lell_pcap_ppi_create_file(const char * filename,int btle_ppi_version,lell_pcap_handle ** ph)2678f3e7eeaSChristopher Kilgour lell_pcap_ppi_create_file(const char *filename, int btle_ppi_version,
2688f3e7eeaSChristopher Kilgour 			  lell_pcap_handle ** ph)
2698f3e7eeaSChristopher Kilgour {
2708f3e7eeaSChristopher Kilgour 	int retval = lell_pcap_create_file_dlt(filename, DLT_PPI, ph);
2718f3e7eeaSChristopher Kilgour 	if (!retval) {
2728f3e7eeaSChristopher Kilgour 		(*ph)->btle_ppi_version = btle_ppi_version;
2738f3e7eeaSChristopher Kilgour 	}
2748f3e7eeaSChristopher Kilgour 	return retval;
2758f3e7eeaSChristopher Kilgour }
2768f3e7eeaSChristopher Kilgour 
2778f3e7eeaSChristopher Kilgour typedef struct {
278c3b616c1SMike Ryan 	pcaprec_hdr_t pcap_header;
2798f3e7eeaSChristopher Kilgour 	pcap_bluetooth_le_ll_header le_ll_header;
2800e05cc66SDominic Spill 	uint8_t le_packet[LE_MAX_PAYLOAD];
2818f3e7eeaSChristopher Kilgour } pcap_le_packet;
2828f3e7eeaSChristopher Kilgour 
2838f3e7eeaSChristopher Kilgour static void
assemble_pcapng_le_packet(pcap_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)2848f3e7eeaSChristopher Kilgour assemble_pcapng_le_packet( pcap_le_packet * pkt,
285d9528177SMike Ryan 			   const uint32_t interface_id __attribute__((unused)),
2868f3e7eeaSChristopher Kilgour 			   const uint64_t ns,
2878f3e7eeaSChristopher Kilgour 			   const uint32_t caplen,
2888f3e7eeaSChristopher Kilgour 			   const uint8_t rf_channel,
2898f3e7eeaSChristopher Kilgour 			   const int8_t signal_power,
2908f3e7eeaSChristopher Kilgour 			   const int8_t noise_power,
2918f3e7eeaSChristopher Kilgour 			   const uint8_t access_address_offenses,
2928f3e7eeaSChristopher Kilgour 			   const uint32_t ref_access_address,
2938f3e7eeaSChristopher Kilgour 			   const uint16_t flags,
2948f3e7eeaSChristopher Kilgour 			   const uint8_t * lepkt )
2958f3e7eeaSChristopher Kilgour {
296cf15ca5eSMike Ryan 	uint32_t incl_len = MIN(LE_MAX_PAYLOAD, caplen);
2978f3e7eeaSChristopher Kilgour 
298c3b616c1SMike Ryan 	pkt->pcap_header.ts_sec  = ns / 1000000000ull;
299c3b616c1SMike Ryan 	pkt->pcap_header.ts_usec = ns % 1000000000ull;
300c3b616c1SMike Ryan 	pkt->pcap_header.incl_len = sizeof(pcap_bluetooth_le_ll_header)+caplen;
301c3b616c1SMike Ryan 	pkt->pcap_header.orig_len = sizeof(pcap_bluetooth_le_ll_header)+incl_len;
3028f3e7eeaSChristopher Kilgour 
3038f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.rf_channel = rf_channel;
3048f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.signal_power = signal_power;
3058f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.noise_power = noise_power;
3068f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.access_address_offenses = access_address_offenses;
3078f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.ref_access_address = htole32( ref_access_address );
3088f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.flags = htole16( flags );
309cf15ca5eSMike Ryan 	(void) memcpy( &pkt->le_packet[0], lepkt, incl_len );
3108f3e7eeaSChristopher Kilgour }
3118f3e7eeaSChristopher Kilgour 
3128f3e7eeaSChristopher Kilgour int
lell_pcap_append_packet(lell_pcap_handle * h,const uint64_t ns,const int8_t sigdbm,const int8_t noisedbm,const uint32_t refAA,const lell_packet * pkt)3138f3e7eeaSChristopher Kilgour lell_pcap_append_packet(lell_pcap_handle * h, const uint64_t ns,
3148f3e7eeaSChristopher Kilgour 			const int8_t sigdbm, const int8_t noisedbm,
3158f3e7eeaSChristopher Kilgour 			const uint32_t refAA, const lell_packet *pkt)
3168f3e7eeaSChristopher Kilgour {
317c3b616c1SMike Ryan 	if (h && h->pcap_file &&
3188f3e7eeaSChristopher Kilgour 	    (h->dlt == DLT_BLUETOOTH_LE_LL_WITH_PHDR)) {
3198f3e7eeaSChristopher Kilgour 		uint16_t flags = LE_DEWHITENED | LE_AA_OFFENSES_VALID |
3208f3e7eeaSChristopher Kilgour 			LE_SIGPOWER_VALID |
3218f3e7eeaSChristopher Kilgour 			((noisedbm < sigdbm) ? LE_NOISEPOWER_VALID : 0) |
3228f3e7eeaSChristopher Kilgour 			(lell_packet_is_data(pkt) ? 0 : LE_REF_AA_VALID);
3238f3e7eeaSChristopher Kilgour 		pcap_le_packet pcap_pkt;
3248f3e7eeaSChristopher Kilgour 		assemble_pcapng_le_packet( &pcap_pkt,
3258f3e7eeaSChristopher Kilgour 					   0,
3268f3e7eeaSChristopher Kilgour 					   ns,
327cf15ca5eSMike Ryan 					   pkt->length + 4 + 2 + 3, // AA + header + CRC
3288f3e7eeaSChristopher Kilgour 					   pkt->channel_k,
3298f3e7eeaSChristopher Kilgour 					   sigdbm,
3308f3e7eeaSChristopher Kilgour 					   noisedbm,
3318f3e7eeaSChristopher Kilgour 					   pkt->access_address_offenses,
3328f3e7eeaSChristopher Kilgour 					   refAA,
3338f3e7eeaSChristopher Kilgour 					   flags,
3348f3e7eeaSChristopher Kilgour 					   &pkt->symbols[0] );
335c3b616c1SMike Ryan 		btbb_pcap_dump(h->pcap_file, &pcap_pkt.pcap_header, (u_char *)&pcap_pkt.le_ll_header);
3368f3e7eeaSChristopher Kilgour 		return 0;
3378f3e7eeaSChristopher Kilgour 	}
3388f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
3398f3e7eeaSChristopher Kilgour }
3408f3e7eeaSChristopher Kilgour 
3418f3e7eeaSChristopher Kilgour #define PPI_BTLE 30006
3428f3e7eeaSChristopher Kilgour 
3438f3e7eeaSChristopher Kilgour typedef struct __attribute__((packed)) {
3448f3e7eeaSChristopher Kilgour 	uint8_t pph_version;
3458f3e7eeaSChristopher Kilgour 	uint8_t pph_flags;
3468f3e7eeaSChristopher Kilgour 	uint16_t pph_len;
3478f3e7eeaSChristopher Kilgour 	uint32_t pph_dlt;
3488f3e7eeaSChristopher Kilgour } ppi_packet_header_t;
3498f3e7eeaSChristopher Kilgour 
3508f3e7eeaSChristopher Kilgour typedef struct __attribute__((packed)) {
3518f3e7eeaSChristopher Kilgour 	uint16_t pfh_type;
3528f3e7eeaSChristopher Kilgour 	uint16_t pfh_datalen;
3538f3e7eeaSChristopher Kilgour } ppi_fieldheader_t;
3548f3e7eeaSChristopher Kilgour 
3558f3e7eeaSChristopher Kilgour typedef struct __attribute__((packed)) {
3568f3e7eeaSChristopher Kilgour 	uint8_t btle_version;
3578f3e7eeaSChristopher Kilgour 	uint16_t btle_channel;
3588f3e7eeaSChristopher Kilgour 	uint8_t btle_clkn_high;
3598f3e7eeaSChristopher Kilgour 	uint32_t btle_clk100ns;
3608f3e7eeaSChristopher Kilgour 	int8_t rssi_max;
3618f3e7eeaSChristopher Kilgour 	int8_t rssi_min;
3628f3e7eeaSChristopher Kilgour 	int8_t rssi_avg;
3638f3e7eeaSChristopher Kilgour 	uint8_t rssi_count;
3648f3e7eeaSChristopher Kilgour } ppi_btle_t;
3658f3e7eeaSChristopher Kilgour 
3668f3e7eeaSChristopher Kilgour typedef struct __attribute__((packed)) {
367c3b616c1SMike Ryan 	pcaprec_hdr_t pcap_header;
3688f3e7eeaSChristopher Kilgour         ppi_packet_header_t ppi_packet_header;
3698f3e7eeaSChristopher Kilgour 	ppi_fieldheader_t ppi_fieldheader;
3708f3e7eeaSChristopher Kilgour 	ppi_btle_t le_ll_ppi_header;
3710e05cc66SDominic Spill 	uint8_t le_packet[LE_MAX_PAYLOAD];
3728f3e7eeaSChristopher Kilgour } pcap_ppi_le_packet;
3738f3e7eeaSChristopher Kilgour 
3748f3e7eeaSChristopher Kilgour int
lell_pcap_append_ppi_packet(lell_pcap_handle * h,const uint64_t ns,const uint8_t clkn_high,const int8_t rssi_min,const int8_t rssi_max,const int8_t rssi_avg,const uint8_t rssi_count,const lell_packet * pkt)3758f3e7eeaSChristopher Kilgour lell_pcap_append_ppi_packet(lell_pcap_handle * h, const uint64_t ns,
3768f3e7eeaSChristopher Kilgour 			    const uint8_t clkn_high,
3778f3e7eeaSChristopher Kilgour 			    const int8_t rssi_min, const int8_t rssi_max,
3788f3e7eeaSChristopher Kilgour 			    const int8_t rssi_avg, const uint8_t rssi_count,
3798f3e7eeaSChristopher Kilgour 			    const lell_packet *pkt)
3808f3e7eeaSChristopher Kilgour {
381c3b616c1SMike Ryan 	if (h && h->pcap_file &&
3828f3e7eeaSChristopher Kilgour 	    (h->dlt == DLT_PPI)) {
3838f3e7eeaSChristopher Kilgour 		pcap_ppi_le_packet pcap_pkt;
384cf15ca5eSMike Ryan 		const uint16_t pcap_headerlen =
385cf15ca5eSMike Ryan 			sizeof(ppi_packet_header_t) +
386cf15ca5eSMike Ryan 			sizeof(ppi_fieldheader_t) +
387cf15ca5eSMike Ryan 			sizeof(ppi_btle_t);
3888f3e7eeaSChristopher Kilgour 		uint16_t MHz = 2402 + 2*lell_get_channel_k(pkt);
389cf15ca5eSMike Ryan 		unsigned packet_len = pkt->length + 4 + 2 + 3; // AA + header + CRC
390cf15ca5eSMike Ryan 		unsigned incl_len   = MIN(LE_MAX_PAYLOAD, packet_len);
3918f3e7eeaSChristopher Kilgour 
392c3b616c1SMike Ryan 		pcap_pkt.pcap_header.ts_sec  = ns / 1000000000ull;
393c3b616c1SMike Ryan 		pcap_pkt.pcap_header.ts_usec = ns % 1000000000ull;
394c3b616c1SMike Ryan 		pcap_pkt.pcap_header.incl_len = pcap_headerlen + incl_len;
395c3b616c1SMike Ryan 		pcap_pkt.pcap_header.orig_len = pcap_headerlen + packet_len;
3968f3e7eeaSChristopher Kilgour 
3978f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_packet_header.pph_version = 0;
3988f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_packet_header.pph_flags = 0;
399cf15ca5eSMike Ryan 		pcap_pkt.ppi_packet_header.pph_len = htole16(pcap_headerlen);
40075e6dc72SMichael Farrell 		pcap_pkt.ppi_packet_header.pph_dlt = htole32(DLT_BLUETOOTH_LE_LL);
4018f3e7eeaSChristopher Kilgour 
4028f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_fieldheader.pfh_type = htole16(PPI_BTLE);
403cf15ca5eSMike Ryan 		pcap_pkt.ppi_fieldheader.pfh_datalen = htole16(sizeof(ppi_btle_t));
4048f3e7eeaSChristopher Kilgour 
4058f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.btle_version = h->btle_ppi_version;
4068f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.btle_channel = htole16(MHz);
4078f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.btle_clkn_high = clkn_high;
4088f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.btle_clk100ns = htole32(pkt->clk100ns);
4098f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.rssi_max = rssi_max;
4108f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.rssi_min = rssi_min;
4118f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.rssi_avg = rssi_avg;
4128f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.rssi_count = rssi_count;
413cf15ca5eSMike Ryan 		(void) memcpy( &pcap_pkt.le_packet[0], &pkt->symbols[0], incl_len);
414c3b616c1SMike Ryan 		btbb_pcap_dump(h->pcap_file, &pcap_pkt.pcap_header, (u_char *)&pcap_pkt.ppi_packet_header);
4158f3e7eeaSChristopher Kilgour 		return 0;
4168f3e7eeaSChristopher Kilgour 	}
4178f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
4188f3e7eeaSChristopher Kilgour }
4198f3e7eeaSChristopher Kilgour 
4208f3e7eeaSChristopher Kilgour int
lell_pcap_close(lell_pcap_handle * h)4218f3e7eeaSChristopher Kilgour lell_pcap_close(lell_pcap_handle *h)
4228f3e7eeaSChristopher Kilgour {
423c3b616c1SMike Ryan 	if (h && h->pcap_file) {
424c3b616c1SMike Ryan 		fclose(h->pcap_file);
4258f3e7eeaSChristopher Kilgour 	}
4268f3e7eeaSChristopher Kilgour 	if (h) {
4278f3e7eeaSChristopher Kilgour 		free(h);
4288f3e7eeaSChristopher Kilgour 		return 0;
4298f3e7eeaSChristopher Kilgour 	}
4308f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
4318f3e7eeaSChristopher Kilgour }
432