xref: /libbtbb/lib/src/pcap.c (revision 8f3e7eea1eae731e517c4fd7ac22f1ce3ad3cd5f)
1*8f3e7eeaSChristopher Kilgour /* -*- c -*- */
2*8f3e7eeaSChristopher Kilgour /*
3*8f3e7eeaSChristopher Kilgour  * Copyright 2014 Christopher D. Kilgour techie AT whiterocker.com
4*8f3e7eeaSChristopher Kilgour  *
5*8f3e7eeaSChristopher Kilgour  * This file is part of libbtbb
6*8f3e7eeaSChristopher Kilgour  *
7*8f3e7eeaSChristopher Kilgour  * This program is free software; you can redistribute it and/or modify
8*8f3e7eeaSChristopher Kilgour  * it under the terms of the GNU General Public License as published by
9*8f3e7eeaSChristopher Kilgour  * the Free Software Foundation; either version 2, or (at your option)
10*8f3e7eeaSChristopher Kilgour  * any later version.
11*8f3e7eeaSChristopher Kilgour  *
12*8f3e7eeaSChristopher Kilgour  * This program is distributed in the hope that it will be useful,
13*8f3e7eeaSChristopher Kilgour  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14*8f3e7eeaSChristopher Kilgour  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*8f3e7eeaSChristopher Kilgour  * GNU General Public License for more details.
16*8f3e7eeaSChristopher Kilgour  *
17*8f3e7eeaSChristopher Kilgour  * You should have received a copy of the GNU General Public License
18*8f3e7eeaSChristopher Kilgour  * along with libbtbb; see the file COPYING.  If not, write to
19*8f3e7eeaSChristopher Kilgour  * the Free Software Foundation, Inc., 51 Franklin Street,
20*8f3e7eeaSChristopher Kilgour  * Boston, MA 02110-1301, USA.
21*8f3e7eeaSChristopher Kilgour  */
22*8f3e7eeaSChristopher Kilgour #include "bluetooth_le_packet.h"
23*8f3e7eeaSChristopher Kilgour #include "btbb.h"
24*8f3e7eeaSChristopher Kilgour #include "pcap-common.h"
25*8f3e7eeaSChristopher Kilgour 
26*8f3e7eeaSChristopher Kilgour #include <stdlib.h>
27*8f3e7eeaSChristopher Kilgour #include <string.h>
28*8f3e7eeaSChristopher Kilgour 
29*8f3e7eeaSChristopher Kilgour typedef enum {
30*8f3e7eeaSChristopher Kilgour 	PCAP_OK = 0,
31*8f3e7eeaSChristopher Kilgour 	PCAP_INVALID_HANDLE,
32*8f3e7eeaSChristopher Kilgour 	PCAP_FILE_NOT_ALLOWED,
33*8f3e7eeaSChristopher Kilgour 	PCAP_NO_MEMORY,
34*8f3e7eeaSChristopher Kilgour } PCAP_RESULT;
35*8f3e7eeaSChristopher Kilgour 
36*8f3e7eeaSChristopher Kilgour #if defined(USE_PCAP)
37*8f3e7eeaSChristopher Kilgour 
38*8f3e7eeaSChristopher Kilgour /* BT BR/EDR support */
39*8f3e7eeaSChristopher Kilgour 
40*8f3e7eeaSChristopher Kilgour typedef struct btbb_pcap_handle {
41*8f3e7eeaSChristopher Kilgour 	pcap_t *        pcap;
42*8f3e7eeaSChristopher Kilgour 	pcap_dumper_t * dumper;
43*8f3e7eeaSChristopher Kilgour } btbb_pcap_handle;
44*8f3e7eeaSChristopher Kilgour 
45*8f3e7eeaSChristopher Kilgour int
46*8f3e7eeaSChristopher Kilgour btbb_pcap_create_file(const char *filename, btbb_pcap_handle ** ph)
47*8f3e7eeaSChristopher Kilgour {
48*8f3e7eeaSChristopher Kilgour 	int retval = 0;
49*8f3e7eeaSChristopher Kilgour 	btbb_pcap_handle * handle = malloc( sizeof(btbb_pcap_handle) );
50*8f3e7eeaSChristopher Kilgour 	if (handle) {
51*8f3e7eeaSChristopher Kilgour 		memset(handle, 0, sizeof(*handle));
52*8f3e7eeaSChristopher Kilgour 		handle->pcap = pcap_open_dead_with_tstamp_precision(DLT_BLUETOOTH_BREDR_BB,
53*8f3e7eeaSChristopher Kilgour 								    400,
54*8f3e7eeaSChristopher Kilgour 								    PCAP_TSTAMP_PRECISION_NANO);
55*8f3e7eeaSChristopher Kilgour 		if (handle->pcap) {
56*8f3e7eeaSChristopher Kilgour 			handle->dumper = pcap_dump_open(handle->pcap, filename);
57*8f3e7eeaSChristopher Kilgour 			if (handle->dumper) {
58*8f3e7eeaSChristopher Kilgour 				*ph = handle;
59*8f3e7eeaSChristopher Kilgour 			}
60*8f3e7eeaSChristopher Kilgour 			else {
61*8f3e7eeaSChristopher Kilgour 				retval = -PCAP_FILE_NOT_ALLOWED;
62*8f3e7eeaSChristopher Kilgour 				goto fail;
63*8f3e7eeaSChristopher Kilgour 			}
64*8f3e7eeaSChristopher Kilgour 		}
65*8f3e7eeaSChristopher Kilgour 		else {
66*8f3e7eeaSChristopher Kilgour 			retval = -PCAP_INVALID_HANDLE;
67*8f3e7eeaSChristopher Kilgour 			goto fail;
68*8f3e7eeaSChristopher Kilgour 		}
69*8f3e7eeaSChristopher Kilgour 	}
70*8f3e7eeaSChristopher Kilgour 	else {
71*8f3e7eeaSChristopher Kilgour 		retval = -PCAP_NO_MEMORY;
72*8f3e7eeaSChristopher Kilgour 		goto fail;
73*8f3e7eeaSChristopher Kilgour 	}
74*8f3e7eeaSChristopher Kilgour 	return retval;
75*8f3e7eeaSChristopher Kilgour fail:
76*8f3e7eeaSChristopher Kilgour 	(void) btbb_pcap_close( handle );
77*8f3e7eeaSChristopher Kilgour 	return retval;
78*8f3e7eeaSChristopher Kilgour }
79*8f3e7eeaSChristopher Kilgour 
80*8f3e7eeaSChristopher Kilgour typedef struct {
81*8f3e7eeaSChristopher Kilgour 	struct pcap_pkthdr pcap_header;
82*8f3e7eeaSChristopher Kilgour 	pcap_bluetooth_bredr_bb_header bredr_bb_header;
83*8f3e7eeaSChristopher Kilgour 	uint8_t bredr_payload[400];
84*8f3e7eeaSChristopher Kilgour } pcap_bredr_packet;
85*8f3e7eeaSChristopher Kilgour 
86*8f3e7eeaSChristopher Kilgour static void
87*8f3e7eeaSChristopher Kilgour assemble_pcapng_bredr_packet( pcap_bredr_packet * pkt,
88*8f3e7eeaSChristopher Kilgour 			      const uint32_t interface_id,
89*8f3e7eeaSChristopher Kilgour 			      const uint64_t ns,
90*8f3e7eeaSChristopher Kilgour 			      const uint32_t caplen,
91*8f3e7eeaSChristopher Kilgour 			      const uint8_t rf_channel,
92*8f3e7eeaSChristopher Kilgour 			      const int8_t signal_power,
93*8f3e7eeaSChristopher Kilgour 			      const int8_t noise_power,
94*8f3e7eeaSChristopher Kilgour 			      const uint8_t access_code_offenses,
95*8f3e7eeaSChristopher Kilgour 			      const uint8_t payload_transport,
96*8f3e7eeaSChristopher Kilgour 			      const uint8_t payload_rate,
97*8f3e7eeaSChristopher Kilgour 			      const uint8_t corrected_header_bits,
98*8f3e7eeaSChristopher Kilgour 			      const int16_t corrected_payload_bits,
99*8f3e7eeaSChristopher Kilgour 			      const uint32_t lap,
100*8f3e7eeaSChristopher Kilgour 			      const uint32_t ref_lap,
101*8f3e7eeaSChristopher Kilgour 			      const uint8_t ref_uap,
102*8f3e7eeaSChristopher Kilgour 			      const uint32_t bt_header,
103*8f3e7eeaSChristopher Kilgour 			      const uint16_t flags,
104*8f3e7eeaSChristopher Kilgour 			      const uint8_t * payload )
105*8f3e7eeaSChristopher Kilgour {
106*8f3e7eeaSChristopher Kilgour 	uint32_t pcap_caplen = sizeof(pcap_bluetooth_bredr_bb_header)+caplen;
107*8f3e7eeaSChristopher Kilgour 	uint32_t reflapuap = (ref_lap&0xffffff) | (ref_uap<<24);
108*8f3e7eeaSChristopher Kilgour 
109*8f3e7eeaSChristopher Kilgour 	pkt->pcap_header.ts.tv_sec  = ns / 1000000000ull;
110*8f3e7eeaSChristopher Kilgour 	pkt->pcap_header.ts.tv_usec = ns % 1000000000ull;
111*8f3e7eeaSChristopher Kilgour 	pkt->pcap_header.caplen = pcap_caplen;
112*8f3e7eeaSChristopher Kilgour 	pkt->pcap_header.len = pcap_caplen;
113*8f3e7eeaSChristopher Kilgour 
114*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.rf_channel = rf_channel;
115*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.signal_power = signal_power;
116*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.noise_power = noise_power;
117*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.access_code_offenses = access_code_offenses;
118*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.payload_transport_rate =
119*8f3e7eeaSChristopher Kilgour 		(payload_transport << 4) | payload_rate;
120*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.corrected_header_bits = corrected_header_bits;
121*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.corrected_payload_bits = htole16( corrected_payload_bits );
122*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.lap = htole32( lap );
123*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.ref_lap_uap = htole32( reflapuap );
124*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.bt_header = htole16( bt_header );
125*8f3e7eeaSChristopher Kilgour 	pkt->bredr_bb_header.flags = htole16( flags );
126*8f3e7eeaSChristopher Kilgour 	if (caplen) {
127*8f3e7eeaSChristopher Kilgour 		(void) memcpy( &pkt->bredr_payload[0], payload, caplen );
128*8f3e7eeaSChristopher Kilgour 	}
129*8f3e7eeaSChristopher Kilgour 	else {
130*8f3e7eeaSChristopher Kilgour 		pkt->bredr_bb_header.flags &= htole16( ~BREDR_PAYLOAD_PRESENT );
131*8f3e7eeaSChristopher Kilgour 	}
132*8f3e7eeaSChristopher Kilgour }
133*8f3e7eeaSChristopher Kilgour 
134*8f3e7eeaSChristopher Kilgour int
135*8f3e7eeaSChristopher Kilgour btbb_pcap_append_packet(btbb_pcap_handle * h, const uint64_t ns,
136*8f3e7eeaSChristopher Kilgour 			const int8_t sigdbm, const int8_t noisedbm,
137*8f3e7eeaSChristopher Kilgour 			const uint32_t reflap, const uint8_t refuap,
138*8f3e7eeaSChristopher Kilgour 			const btbb_packet *pkt)
139*8f3e7eeaSChristopher Kilgour {
140*8f3e7eeaSChristopher Kilgour 	if (h && h->dumper) {
141*8f3e7eeaSChristopher Kilgour 		uint16_t flags = BREDR_DEWHITENED | BREDR_SIGPOWER_VALID |
142*8f3e7eeaSChristopher Kilgour 			((noisedbm < sigdbm) ? BREDR_NOISEPOWER_VALID : 0) |
143*8f3e7eeaSChristopher Kilgour 			((reflap != LAP_ANY) ? BREDR_REFLAP_VALID : 0) |
144*8f3e7eeaSChristopher Kilgour 			((refuap != UAP_ANY) ? BREDR_REFUAP_VALID : 0);
145*8f3e7eeaSChristopher Kilgour 		uint8_t payload_bytes[400];
146*8f3e7eeaSChristopher Kilgour 		uint32_t caplen = (uint32_t) btbb_get_payload_packed( pkt, (char *) &payload_bytes[0] );
147*8f3e7eeaSChristopher Kilgour 		pcap_bredr_packet pcap_pkt;
148*8f3e7eeaSChristopher Kilgour 		assemble_pcapng_bredr_packet( &pcap_pkt,
149*8f3e7eeaSChristopher Kilgour 					      0,
150*8f3e7eeaSChristopher Kilgour 					      ns,
151*8f3e7eeaSChristopher Kilgour 					      caplen,
152*8f3e7eeaSChristopher Kilgour 					      btbb_packet_get_channel(pkt),
153*8f3e7eeaSChristopher Kilgour 					      sigdbm,
154*8f3e7eeaSChristopher Kilgour 					      noisedbm,
155*8f3e7eeaSChristopher Kilgour 					      btbb_packet_get_ac_errors(pkt),
156*8f3e7eeaSChristopher Kilgour 					      BREDR_TRANSPORT_ANY,
157*8f3e7eeaSChristopher Kilgour 					      BREDR_GFSK, /* currently only supported */
158*8f3e7eeaSChristopher Kilgour 					      0, /* TODO: corrected header bits */
159*8f3e7eeaSChristopher Kilgour 					      0, /* TODO: corrected payload bits */
160*8f3e7eeaSChristopher Kilgour 					      btbb_packet_get_lap(pkt),
161*8f3e7eeaSChristopher Kilgour 					      reflap,
162*8f3e7eeaSChristopher Kilgour 					      refuap,
163*8f3e7eeaSChristopher Kilgour 					      btbb_packet_get_header_packed(pkt),
164*8f3e7eeaSChristopher Kilgour 					      flags,
165*8f3e7eeaSChristopher Kilgour 					      payload_bytes );
166*8f3e7eeaSChristopher Kilgour 		pcap_dump((u_char *)h->dumper, &pcap_pkt.pcap_header, (u_char *)&pcap_pkt.bredr_bb_header);
167*8f3e7eeaSChristopher Kilgour 		return 0;
168*8f3e7eeaSChristopher Kilgour 	}
169*8f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
170*8f3e7eeaSChristopher Kilgour }
171*8f3e7eeaSChristopher Kilgour 
172*8f3e7eeaSChristopher Kilgour int
173*8f3e7eeaSChristopher Kilgour btbb_pcap_close(btbb_pcap_handle * h)
174*8f3e7eeaSChristopher Kilgour {
175*8f3e7eeaSChristopher Kilgour 	if (h && h->dumper) {
176*8f3e7eeaSChristopher Kilgour 		pcap_dump_close(h->dumper);
177*8f3e7eeaSChristopher Kilgour 	}
178*8f3e7eeaSChristopher Kilgour 	if (h && h->pcap) {
179*8f3e7eeaSChristopher Kilgour 		pcap_close(h->pcap);
180*8f3e7eeaSChristopher Kilgour 	}
181*8f3e7eeaSChristopher Kilgour 	if (h) {
182*8f3e7eeaSChristopher Kilgour 		free(h);
183*8f3e7eeaSChristopher Kilgour 		return 0;
184*8f3e7eeaSChristopher Kilgour 	}
185*8f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
186*8f3e7eeaSChristopher Kilgour }
187*8f3e7eeaSChristopher Kilgour 
188*8f3e7eeaSChristopher Kilgour /* BTLE support */
189*8f3e7eeaSChristopher Kilgour 
190*8f3e7eeaSChristopher Kilgour typedef struct lell_pcap_handle {
191*8f3e7eeaSChristopher Kilgour 	pcap_t * pcap;
192*8f3e7eeaSChristopher Kilgour 	pcap_dumper_t * dumper;
193*8f3e7eeaSChristopher Kilgour 	int dlt;
194*8f3e7eeaSChristopher Kilgour 	uint8_t btle_ppi_version;
195*8f3e7eeaSChristopher Kilgour } lell_pcap_handle;
196*8f3e7eeaSChristopher Kilgour 
197*8f3e7eeaSChristopher Kilgour static int
198*8f3e7eeaSChristopher Kilgour lell_pcap_create_file_dlt(const char *filename, int dlt, lell_pcap_handle ** ph)
199*8f3e7eeaSChristopher Kilgour {
200*8f3e7eeaSChristopher Kilgour 	int retval = 0;
201*8f3e7eeaSChristopher Kilgour 	lell_pcap_handle * handle = malloc( sizeof(lell_pcap_handle) );
202*8f3e7eeaSChristopher Kilgour 	if (handle) {
203*8f3e7eeaSChristopher Kilgour 		memset(handle, 0, sizeof(*handle));
204*8f3e7eeaSChristopher Kilgour 		handle->pcap = pcap_open_dead_with_tstamp_precision(dlt,
205*8f3e7eeaSChristopher Kilgour 								    400,
206*8f3e7eeaSChristopher Kilgour 								    PCAP_TSTAMP_PRECISION_NANO);
207*8f3e7eeaSChristopher Kilgour 		if (handle->pcap) {
208*8f3e7eeaSChristopher Kilgour 			handle->dumper = pcap_dump_open(handle->pcap, filename);
209*8f3e7eeaSChristopher Kilgour 			if (handle->dumper) {
210*8f3e7eeaSChristopher Kilgour 				handle->dlt = dlt;
211*8f3e7eeaSChristopher Kilgour 				*ph = handle;
212*8f3e7eeaSChristopher Kilgour 			}
213*8f3e7eeaSChristopher Kilgour 			else {
214*8f3e7eeaSChristopher Kilgour 				retval = -PCAP_FILE_NOT_ALLOWED;
215*8f3e7eeaSChristopher Kilgour 				goto fail;
216*8f3e7eeaSChristopher Kilgour 			}
217*8f3e7eeaSChristopher Kilgour 		}
218*8f3e7eeaSChristopher Kilgour 		else {
219*8f3e7eeaSChristopher Kilgour 			retval = -PCAP_INVALID_HANDLE;
220*8f3e7eeaSChristopher Kilgour 			goto fail;
221*8f3e7eeaSChristopher Kilgour 		}
222*8f3e7eeaSChristopher Kilgour 	}
223*8f3e7eeaSChristopher Kilgour 	else {
224*8f3e7eeaSChristopher Kilgour 		retval = -PCAP_NO_MEMORY;
225*8f3e7eeaSChristopher Kilgour 		goto fail;
226*8f3e7eeaSChristopher Kilgour 	}
227*8f3e7eeaSChristopher Kilgour 	return retval;
228*8f3e7eeaSChristopher Kilgour fail:
229*8f3e7eeaSChristopher Kilgour 	(void) lell_pcap_close( handle );
230*8f3e7eeaSChristopher Kilgour 	return retval;
231*8f3e7eeaSChristopher Kilgour }
232*8f3e7eeaSChristopher Kilgour 
233*8f3e7eeaSChristopher Kilgour int
234*8f3e7eeaSChristopher Kilgour lell_pcap_create_file(const char *filename, lell_pcap_handle ** ph)
235*8f3e7eeaSChristopher Kilgour {
236*8f3e7eeaSChristopher Kilgour 	return lell_pcap_create_file_dlt(filename, DLT_BLUETOOTH_LE_LL_WITH_PHDR, ph);
237*8f3e7eeaSChristopher Kilgour }
238*8f3e7eeaSChristopher Kilgour 
239*8f3e7eeaSChristopher Kilgour int
240*8f3e7eeaSChristopher Kilgour lell_pcap_ppi_create_file(const char *filename, int btle_ppi_version,
241*8f3e7eeaSChristopher Kilgour 			  lell_pcap_handle ** ph)
242*8f3e7eeaSChristopher Kilgour {
243*8f3e7eeaSChristopher Kilgour 	int retval = lell_pcap_create_file_dlt(filename, DLT_PPI, ph);
244*8f3e7eeaSChristopher Kilgour 	if (!retval) {
245*8f3e7eeaSChristopher Kilgour 		(*ph)->btle_ppi_version = btle_ppi_version;
246*8f3e7eeaSChristopher Kilgour 	}
247*8f3e7eeaSChristopher Kilgour 	return retval;
248*8f3e7eeaSChristopher Kilgour }
249*8f3e7eeaSChristopher Kilgour 
250*8f3e7eeaSChristopher Kilgour typedef struct {
251*8f3e7eeaSChristopher Kilgour 	struct pcap_pkthdr pcap_header;
252*8f3e7eeaSChristopher Kilgour 	pcap_bluetooth_le_ll_header le_ll_header;
253*8f3e7eeaSChristopher Kilgour 	uint8_t le_packet[48];
254*8f3e7eeaSChristopher Kilgour } pcap_le_packet;
255*8f3e7eeaSChristopher Kilgour 
256*8f3e7eeaSChristopher Kilgour static void
257*8f3e7eeaSChristopher Kilgour assemble_pcapng_le_packet( pcap_le_packet * pkt,
258*8f3e7eeaSChristopher Kilgour 			   const uint32_t interface_id,
259*8f3e7eeaSChristopher Kilgour 			   const uint64_t ns,
260*8f3e7eeaSChristopher Kilgour 			   const uint32_t caplen,
261*8f3e7eeaSChristopher Kilgour 			   const uint8_t rf_channel,
262*8f3e7eeaSChristopher Kilgour 			   const int8_t signal_power,
263*8f3e7eeaSChristopher Kilgour 			   const int8_t noise_power,
264*8f3e7eeaSChristopher Kilgour 			   const uint8_t access_address_offenses,
265*8f3e7eeaSChristopher Kilgour 			   const uint32_t ref_access_address,
266*8f3e7eeaSChristopher Kilgour 			   const uint16_t flags,
267*8f3e7eeaSChristopher Kilgour 			   const uint8_t * lepkt )
268*8f3e7eeaSChristopher Kilgour {
269*8f3e7eeaSChristopher Kilgour 	uint32_t pcap_caplen = sizeof(pcap_bluetooth_le_ll_header)+caplen;
270*8f3e7eeaSChristopher Kilgour 
271*8f3e7eeaSChristopher Kilgour 	pkt->pcap_header.ts.tv_sec  = ns / 1000000000ull;
272*8f3e7eeaSChristopher Kilgour 	pkt->pcap_header.ts.tv_usec = ns % 1000000000ull;
273*8f3e7eeaSChristopher Kilgour 	pkt->pcap_header.caplen = pcap_caplen;
274*8f3e7eeaSChristopher Kilgour 	pkt->pcap_header.len = pcap_caplen;
275*8f3e7eeaSChristopher Kilgour 
276*8f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.rf_channel = rf_channel;
277*8f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.signal_power = signal_power;
278*8f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.noise_power = noise_power;
279*8f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.access_address_offenses = access_address_offenses;
280*8f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.ref_access_address = htole32( ref_access_address );
281*8f3e7eeaSChristopher Kilgour 	pkt->le_ll_header.flags = htole16( flags );
282*8f3e7eeaSChristopher Kilgour 	(void) memcpy( &pkt->le_packet[0], lepkt, caplen );
283*8f3e7eeaSChristopher Kilgour }
284*8f3e7eeaSChristopher Kilgour 
285*8f3e7eeaSChristopher Kilgour int
286*8f3e7eeaSChristopher Kilgour lell_pcap_append_packet(lell_pcap_handle * h, const uint64_t ns,
287*8f3e7eeaSChristopher Kilgour 			const int8_t sigdbm, const int8_t noisedbm,
288*8f3e7eeaSChristopher Kilgour 			const uint32_t refAA, const lell_packet *pkt)
289*8f3e7eeaSChristopher Kilgour {
290*8f3e7eeaSChristopher Kilgour 	if (h && h->dumper &&
291*8f3e7eeaSChristopher Kilgour 	    (h->dlt == DLT_BLUETOOTH_LE_LL_WITH_PHDR)) {
292*8f3e7eeaSChristopher Kilgour 		uint16_t flags = LE_DEWHITENED | LE_AA_OFFENSES_VALID |
293*8f3e7eeaSChristopher Kilgour 			LE_SIGPOWER_VALID |
294*8f3e7eeaSChristopher Kilgour 			((noisedbm < sigdbm) ? LE_NOISEPOWER_VALID : 0) |
295*8f3e7eeaSChristopher Kilgour 			(lell_packet_is_data(pkt) ? 0 : LE_REF_AA_VALID);
296*8f3e7eeaSChristopher Kilgour 		pcap_le_packet pcap_pkt;
297*8f3e7eeaSChristopher Kilgour 		assemble_pcapng_le_packet( &pcap_pkt,
298*8f3e7eeaSChristopher Kilgour 					   0,
299*8f3e7eeaSChristopher Kilgour 					   ns,
300*8f3e7eeaSChristopher Kilgour 					   9+pkt->length,
301*8f3e7eeaSChristopher Kilgour 					   pkt->channel_k,
302*8f3e7eeaSChristopher Kilgour 					   sigdbm,
303*8f3e7eeaSChristopher Kilgour 					   noisedbm,
304*8f3e7eeaSChristopher Kilgour 					   pkt->access_address_offenses,
305*8f3e7eeaSChristopher Kilgour 					   refAA,
306*8f3e7eeaSChristopher Kilgour 					   flags,
307*8f3e7eeaSChristopher Kilgour 					   &pkt->symbols[0] );
308*8f3e7eeaSChristopher Kilgour 		pcap_dump((u_char *)h->dumper, &pcap_pkt.pcap_header, (u_char *)&pcap_pkt.le_ll_header);
309*8f3e7eeaSChristopher Kilgour 		return 0;
310*8f3e7eeaSChristopher Kilgour 	}
311*8f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
312*8f3e7eeaSChristopher Kilgour }
313*8f3e7eeaSChristopher Kilgour 
314*8f3e7eeaSChristopher Kilgour #define PPI_BTLE 30006
315*8f3e7eeaSChristopher Kilgour 
316*8f3e7eeaSChristopher Kilgour typedef struct __attribute__((packed)) {
317*8f3e7eeaSChristopher Kilgour 	uint8_t pph_version;
318*8f3e7eeaSChristopher Kilgour 	uint8_t pph_flags;
319*8f3e7eeaSChristopher Kilgour 	uint16_t pph_len;
320*8f3e7eeaSChristopher Kilgour 	uint32_t pph_dlt;
321*8f3e7eeaSChristopher Kilgour } ppi_packet_header_t;
322*8f3e7eeaSChristopher Kilgour 
323*8f3e7eeaSChristopher Kilgour typedef struct __attribute__((packed)) {
324*8f3e7eeaSChristopher Kilgour 	uint16_t pfh_type;
325*8f3e7eeaSChristopher Kilgour 	uint16_t pfh_datalen;
326*8f3e7eeaSChristopher Kilgour } ppi_fieldheader_t;
327*8f3e7eeaSChristopher Kilgour 
328*8f3e7eeaSChristopher Kilgour typedef struct __attribute__((packed)) {
329*8f3e7eeaSChristopher Kilgour 	uint8_t btle_version;
330*8f3e7eeaSChristopher Kilgour 	uint16_t btle_channel;
331*8f3e7eeaSChristopher Kilgour 	uint8_t btle_clkn_high;
332*8f3e7eeaSChristopher Kilgour 	uint32_t btle_clk100ns;
333*8f3e7eeaSChristopher Kilgour 	int8_t rssi_max;
334*8f3e7eeaSChristopher Kilgour 	int8_t rssi_min;
335*8f3e7eeaSChristopher Kilgour 	int8_t rssi_avg;
336*8f3e7eeaSChristopher Kilgour 	uint8_t rssi_count;
337*8f3e7eeaSChristopher Kilgour } ppi_btle_t;
338*8f3e7eeaSChristopher Kilgour 
339*8f3e7eeaSChristopher Kilgour typedef struct __attribute__((packed)) {
340*8f3e7eeaSChristopher Kilgour 	struct pcap_pkthdr pcap_header;
341*8f3e7eeaSChristopher Kilgour         ppi_packet_header_t ppi_packet_header;
342*8f3e7eeaSChristopher Kilgour 	ppi_fieldheader_t ppi_fieldheader;
343*8f3e7eeaSChristopher Kilgour 	ppi_btle_t le_ll_ppi_header;
344*8f3e7eeaSChristopher Kilgour 	uint8_t le_packet[48];
345*8f3e7eeaSChristopher Kilgour } pcap_ppi_le_packet;
346*8f3e7eeaSChristopher Kilgour 
347*8f3e7eeaSChristopher Kilgour int
348*8f3e7eeaSChristopher Kilgour lell_pcap_append_ppi_packet(lell_pcap_handle * h, const uint64_t ns,
349*8f3e7eeaSChristopher Kilgour 			    const uint8_t clkn_high,
350*8f3e7eeaSChristopher Kilgour 			    const int8_t rssi_min, const int8_t rssi_max,
351*8f3e7eeaSChristopher Kilgour 			    const int8_t rssi_avg, const uint8_t rssi_count,
352*8f3e7eeaSChristopher Kilgour 			    const lell_packet *pkt)
353*8f3e7eeaSChristopher Kilgour {
354*8f3e7eeaSChristopher Kilgour 	const ppi_packet_header_sz = sizeof(ppi_packet_header_t);
355*8f3e7eeaSChristopher Kilgour 	const ppi_fieldheader_sz = sizeof(ppi_fieldheader_t);
356*8f3e7eeaSChristopher Kilgour 	const le_ll_ppi_header_sz = sizeof(ppi_btle_t);
357*8f3e7eeaSChristopher Kilgour 
358*8f3e7eeaSChristopher Kilgour 	if (h && h->dumper &&
359*8f3e7eeaSChristopher Kilgour 	    (h->dlt == DLT_PPI)) {
360*8f3e7eeaSChristopher Kilgour 		pcap_ppi_le_packet pcap_pkt;
361*8f3e7eeaSChristopher Kilgour 		uint32_t pcap_caplen =
362*8f3e7eeaSChristopher Kilgour 			ppi_packet_header_sz+ppi_fieldheader_sz+le_ll_ppi_header_sz+pkt->length;
363*8f3e7eeaSChristopher Kilgour 		uint16_t MHz = 2402 + 2*lell_get_channel_k(pkt);
364*8f3e7eeaSChristopher Kilgour 
365*8f3e7eeaSChristopher Kilgour 		pcap_pkt.pcap_header.ts.tv_sec  = ns / 1000000000ull;
366*8f3e7eeaSChristopher Kilgour 		pcap_pkt.pcap_header.ts.tv_usec = ns % 1000000000ull;
367*8f3e7eeaSChristopher Kilgour 		pcap_pkt.pcap_header.caplen = pcap_caplen;
368*8f3e7eeaSChristopher Kilgour 		pcap_pkt.pcap_header.len = pcap_caplen;
369*8f3e7eeaSChristopher Kilgour 
370*8f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_packet_header.pph_version = 0;
371*8f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_packet_header.pph_flags = 0;
372*8f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_packet_header.pph_len = htole16(ppi_packet_header_sz+ppi_fieldheader_sz+le_ll_ppi_header_sz);
373*8f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_packet_header.pph_dlt = htole32(DLT_USER0);
374*8f3e7eeaSChristopher Kilgour 
375*8f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_fieldheader.pfh_type = htole16(PPI_BTLE);
376*8f3e7eeaSChristopher Kilgour 		pcap_pkt.ppi_fieldheader.pfh_datalen = htole16(le_ll_ppi_header_sz);
377*8f3e7eeaSChristopher Kilgour 
378*8f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.btle_version = h->btle_ppi_version;
379*8f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.btle_channel = htole16(MHz);
380*8f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.btle_clkn_high = clkn_high;
381*8f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.btle_clk100ns = htole32(pkt->clk100ns);
382*8f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.rssi_max = rssi_max;
383*8f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.rssi_min = rssi_min;
384*8f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.rssi_avg = rssi_avg;
385*8f3e7eeaSChristopher Kilgour 		pcap_pkt.le_ll_ppi_header.rssi_count = rssi_count;
386*8f3e7eeaSChristopher Kilgour 		(void) memcpy( &pcap_pkt.le_packet[0], &pkt->symbols[0], pcap_caplen );
387*8f3e7eeaSChristopher Kilgour 		pcap_dump((u_char *)h->dumper, &pcap_pkt.pcap_header, (u_char *)&pcap_pkt.le_ll_ppi_header);
388*8f3e7eeaSChristopher Kilgour 		return 0;
389*8f3e7eeaSChristopher Kilgour 	}
390*8f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
391*8f3e7eeaSChristopher Kilgour }
392*8f3e7eeaSChristopher Kilgour 
393*8f3e7eeaSChristopher Kilgour int
394*8f3e7eeaSChristopher Kilgour lell_pcap_close(lell_pcap_handle *h)
395*8f3e7eeaSChristopher Kilgour {
396*8f3e7eeaSChristopher Kilgour 	if (h && h->dumper) {
397*8f3e7eeaSChristopher Kilgour 		pcap_dump_close(h->dumper);
398*8f3e7eeaSChristopher Kilgour 	}
399*8f3e7eeaSChristopher Kilgour 	if (h && h->pcap) {
400*8f3e7eeaSChristopher Kilgour 		pcap_close(h->pcap);
401*8f3e7eeaSChristopher Kilgour 	}
402*8f3e7eeaSChristopher Kilgour 	if (h) {
403*8f3e7eeaSChristopher Kilgour 		free(h);
404*8f3e7eeaSChristopher Kilgour 		return 0;
405*8f3e7eeaSChristopher Kilgour 	}
406*8f3e7eeaSChristopher Kilgour 	return -PCAP_INVALID_HANDLE;
407*8f3e7eeaSChristopher Kilgour }
408*8f3e7eeaSChristopher Kilgour 
409*8f3e7eeaSChristopher Kilgour #endif /* USE_PCAP */
410