Line data Source code
1 : #include "fd_pcap.h"
2 : #include "fd_ip4.h"
3 : #include "fd_udp.h"
4 :
5 : #if FD_HAS_HOSTED
6 :
7 : #include <stdio.h> /* TODO: use fd_io instead of stdio here */
8 : #include <errno.h>
9 :
10 : #define FD_PCAP_HDR_NETWORK_ETHERNET (1U)
11 3 : #define FD_PCAP_HDR_NETWORK_LINUX_SLL (113U)
12 :
13 : struct fd_pcap_hdr {
14 : uint magic_number;
15 : ushort version_major;
16 : ushort version_minor;
17 : int thiszone;
18 : uint sigfigs;
19 : uint snaplen;
20 : uint network;
21 : };
22 :
23 : typedef struct fd_pcap_hdr fd_pcap_hdr_t;
24 :
25 : struct fd_pcap_pkt_hdr {
26 : uint sec; /* Host order */
27 : uint usec; /* Host order (code below assumes a ns capture) */
28 : uint incl_len; /* Host order */
29 : uint orig_len; /* Host order */
30 : };
31 :
32 : typedef struct fd_pcap_pkt_hdr fd_pcap_pkt_hdr_t;
33 :
34 : typedef struct {
35 : ushort dir;
36 : ushort ha_type;
37 : ushort ha_len;
38 : uchar ha[8];
39 : ushort net_type;
40 : } fd_pcap_sll_hdr_t;
41 :
42 : fd_pcap_iter_t *
43 3 : fd_pcap_iter_new( void * _file ) {
44 3 : FILE * file = (FILE *)_file;
45 :
46 3 : if( FD_UNLIKELY( !file ) ) {
47 0 : FD_LOG_WARNING(( "NULL file" ));
48 0 : return NULL;
49 0 : }
50 :
51 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)file, 2UL ) ) ) {
52 0 : FD_LOG_WARNING(( "misaligned file" ));
53 0 : return NULL;
54 0 : }
55 :
56 3 : fd_pcap_hdr_t pcap[1];
57 3 : if( FD_UNLIKELY( fread( pcap, sizeof(fd_pcap_hdr_t), 1UL, file ) != 1UL ) ) {
58 0 : FD_LOG_WARNING(( "fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
59 0 : return NULL;
60 0 : }
61 :
62 3 : if( FD_UNLIKELY( !((pcap->magic_number==0xa1b2c3d4U) |
63 3 : (pcap->magic_number==0xa1b23c4dU) ) ) ) {
64 0 : FD_LOG_WARNING(( "not a supported pcap file (bad magic number)" ));
65 0 : return NULL;
66 0 : }
67 :
68 3 : if( FD_UNLIKELY( !( (pcap->network==FD_PCAP_HDR_NETWORK_ETHERNET ) |
69 3 : (pcap->network!=FD_PCAP_HDR_NETWORK_LINUX_SLL) ) ) ) {
70 0 : FD_LOG_WARNING(( "unsupported network type (neither an Ethernet nor a cooked socket pcap)" ));
71 0 : return NULL;
72 0 : }
73 :
74 3 : ulong cooked = (ulong)( pcap->network==FD_PCAP_HDR_NETWORK_LINUX_SLL );
75 :
76 3 : return (fd_pcap_iter_t *)((ulong)file | cooked);
77 3 : }
78 :
79 : ulong
80 : fd_pcap_iter_next( fd_pcap_iter_t * iter,
81 : void * pkt,
82 : ulong pkt_max,
83 1440 : long * _pkt_ts ) {
84 1440 : FILE * file = (FILE *)fd_pcap_iter_file( iter );
85 1440 : int cooked = (int )fd_pcap_iter_type( iter );
86 :
87 1440 : fd_pcap_pkt_hdr_t pcap[1];
88 1440 : if( FD_UNLIKELY( fread( pcap, sizeof(fd_pcap_pkt_hdr_t), 1, file ) != 1 ) ) {
89 0 : if( FD_UNLIKELY( !feof( file ) ) )
90 0 : FD_LOG_WARNING(( "Could not read link header from pcap (%i-%s)", errno, fd_io_strerror( errno ) ));
91 0 : return 0UL;
92 0 : }
93 :
94 1440 : ulong pkt_sz = (ulong)pcap->incl_len;
95 :
96 1440 : if( FD_UNLIKELY( pkt_sz!=pcap->orig_len ) ) {
97 0 : FD_LOG_WARNING(( "Read a truncated packet (%lu bytes to %u bytes), run tcpdump with '-s0' option to capture everything",
98 0 : pkt_sz, pcap->orig_len ));
99 0 : return 0UL;
100 0 : }
101 :
102 1440 : ulong pcap_hdr_sz = fd_ulong_if( cooked, sizeof(fd_pcap_sll_hdr_t), sizeof(fd_eth_hdr_t) );
103 1440 : if( FD_UNLIKELY( pkt_sz<pcap_hdr_sz ) ) {
104 0 : FD_LOG_WARNING(( "Corrupt incl_len in cooked pcap file %lu", pkt_sz ));
105 0 : return 0UL;
106 0 : }
107 :
108 1440 : if( FD_UNLIKELY( pkt_sz>pkt_max ) ) {
109 0 : FD_LOG_WARNING(( "Too large packet detected in pcap (%lu bytes with %lu max)", pkt_sz, pkt_max ));
110 0 : return 0UL;
111 0 : }
112 :
113 1440 : fd_eth_hdr_t * hdr = (fd_eth_hdr_t *)pkt;
114 1440 : if( FD_UNLIKELY( cooked ) ) {
115 :
116 0 : fd_pcap_sll_hdr_t sll[1];
117 0 : if( FD_UNLIKELY( fread( sll, sizeof(fd_pcap_sll_hdr_t), 1, file ) != 1 ) ) {
118 0 : if( FD_UNLIKELY( !feof( file ) ) )
119 0 : FD_LOG_WARNING(( "packet sll header fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
120 0 : else FD_LOG_WARNING(( "packet sll header fread failed (truncated pcap file?)" ));
121 0 : return 0UL;
122 0 : }
123 :
124 : /* Construct an ethernet compatible header that encodes the sll
125 : header info in a reasonable way */
126 :
127 0 : hdr->dst[0] = (uchar)(sll->dir ); hdr->dst[1] = (uchar)(sll->dir >> 8);
128 0 : hdr->dst[2] = (uchar)(sll->ha_type); hdr->dst[3] = (uchar)(sll->ha_type >> 8);
129 0 : hdr->dst[4] = (uchar)(sll->ha_len ); hdr->dst[5] = (uchar)(sll->ha_len >> 8);
130 0 : hdr->src[0] = sll->ha[0]; hdr->src[1] = sll->ha[1];
131 0 : hdr->src[2] = sll->ha[2]; hdr->src[3] = sll->ha[3];
132 0 : hdr->src[4] = sll->ha[4]; hdr->src[5] = sll->ha[5];
133 0 : hdr->net_type = sll->net_type;
134 :
135 0 : hdr->dst[0] = (uchar)(((ulong)hdr->dst[0] & ~3UL) | 2UL); /* Mark as a local admin unicast MAC */
136 0 : hdr->src[0] = (uchar)(((ulong)hdr->src[0] & ~3UL) | 2UL); /* " */
137 : /* FIXME: ENCODE LOST BITS TOO? */
138 :
139 0 : pkt_sz -= sizeof(fd_pcap_sll_hdr_t);
140 0 : pkt_sz += sizeof(fd_eth_hdr_t);
141 :
142 1440 : } else {
143 :
144 1440 : if( FD_UNLIKELY( fread( hdr, sizeof(fd_eth_hdr_t), 1, file )!= 1 ) ) {
145 0 : if( FD_UNLIKELY( !feof( file ) ) )
146 0 : FD_LOG_WARNING(( "packet eth header fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
147 0 : else
148 0 : FD_LOG_WARNING(( "packet eth header fread failed (truncated pcap file?)" ));
149 0 : return 0UL;
150 0 : }
151 :
152 1440 : }
153 :
154 1440 : if( FD_UNLIKELY( fread( hdr+1, pkt_sz-sizeof(fd_eth_hdr_t), 1, file )!=1 ) ) {
155 0 : if( FD_UNLIKELY( !feof( file ) ) )
156 0 : FD_LOG_WARNING(( "packet payload fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
157 0 : else
158 0 : FD_LOG_WARNING(( "packet payload fread failed (truncated pcap file?)" ));
159 0 : }
160 :
161 1440 : *_pkt_ts = ((long)pcap->usec) + 1000000000L*((long)pcap->sec); /* Note: assumes ns resolution capture */
162 1440 : return pkt_sz;
163 1440 : }
164 :
165 : int
166 : fd_pcap_iter_next_split( fd_pcap_iter_t * iter,
167 : void * hdr_buf,
168 : ulong * hdr_sz,
169 : void * pld_buf,
170 : ulong * pld_sz,
171 0 : long * _pkt_ts ) {
172 0 : FILE * file = (FILE *)fd_pcap_iter_file( iter );
173 0 : int cooked = (int )fd_pcap_iter_type( iter );
174 :
175 0 : ulong pld_rem = *pld_sz;
176 0 : ulong hdr_rem = *hdr_sz;
177 :
178 0 : uchar * _hdr_buf = hdr_buf;
179 :
180 0 : fd_pcap_pkt_hdr_t pcap[1];
181 0 : if( FD_UNLIKELY( fread( pcap, sizeof(fd_pcap_pkt_hdr_t), 1, file ) != 1 ) ) {
182 0 : if( FD_UNLIKELY( !feof( file ) ) )
183 0 : FD_LOG_WARNING(( "Could not read link header from pcap (%i-%s)", errno, fd_io_strerror( errno ) ));
184 0 : return 0;
185 0 : }
186 :
187 0 : ulong pkt_rem = (ulong)pcap->incl_len;
188 :
189 0 : if( FD_UNLIKELY( pkt_rem!=pcap->orig_len ) ) {
190 0 : FD_LOG_WARNING(( "Read a truncated packet (%lu bytes to %u bytes), run tcpdump with '-s0' option to capture everything",
191 0 : pkt_rem, pcap->orig_len ));
192 0 : return 0UL;
193 0 : }
194 :
195 0 : ulong pcap_hdr_sz = fd_ulong_if( cooked, sizeof(fd_pcap_sll_hdr_t), sizeof(fd_eth_hdr_t) );
196 0 : if( FD_UNLIKELY( pkt_rem<pcap_hdr_sz ) ) {
197 0 : FD_LOG_WARNING(( "Corrupt incl_len in cooked pcap file %lu", pkt_rem ));
198 0 : return 0UL;
199 0 : }
200 :
201 0 : if( FD_UNLIKELY( pkt_rem>hdr_rem+pld_rem ) ) {
202 0 : FD_LOG_WARNING(( "Too large packet detected in pcap (%lu bytes with %lu max)", pkt_rem, hdr_rem+pld_rem ));
203 0 : return 0UL;
204 0 : }
205 :
206 0 : if( FD_UNLIKELY( hdr_rem<sizeof(fd_eth_hdr_t) ) ) {
207 0 : FD_LOG_WARNING(( "Header buffer not big enough for an Ethernet header" ));
208 0 : return 0UL;
209 0 : }
210 :
211 0 : fd_eth_hdr_t * hdr = (fd_eth_hdr_t *)_hdr_buf;
212 0 : if( FD_UNLIKELY( cooked ) ) {
213 :
214 0 : fd_pcap_sll_hdr_t sll[1];
215 0 : if( FD_UNLIKELY( fread( sll, sizeof(fd_pcap_sll_hdr_t), 1, file ) != 1 ) ) {
216 0 : if( FD_UNLIKELY( !feof( file ) ) )
217 0 : FD_LOG_WARNING(( "packet sll header fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
218 0 : else
219 0 : FD_LOG_WARNING(( "packet sll header fread failed (truncated pcap file?)" ));
220 0 : return 0UL;
221 0 : }
222 :
223 : /* Construct an ethernet compatible header that encodes the sll
224 : header info in a reasonable way */
225 :
226 0 : hdr->dst[0] = (uchar)(sll->dir ); hdr->dst[1] = (uchar)(sll->dir >> 8);
227 0 : hdr->dst[2] = (uchar)(sll->ha_type); hdr->dst[3] = (uchar)(sll->ha_type >> 8);
228 0 : hdr->dst[4] = (uchar)(sll->ha_len ); hdr->dst[5] = (uchar)(sll->ha_len >> 8);
229 0 : hdr->src[0] = sll->ha[0]; hdr->src[1] = sll->ha[1];
230 0 : hdr->src[2] = sll->ha[2]; hdr->src[3] = sll->ha[3];
231 0 : hdr->src[4] = sll->ha[4]; hdr->src[5] = sll->ha[5];
232 0 : hdr->net_type = sll->net_type;
233 :
234 0 : hdr->dst[0] = (uchar)(((ulong)hdr->dst[0] & ~3UL) | 2UL); /* Mark as a local admin unicast MAC */
235 0 : hdr->src[0] = (uchar)(((ulong)hdr->src[0] & ~3UL) | 2UL); /* " */
236 : /* FIXME: ENCODE LOST BITS TOO? */
237 :
238 0 : pkt_rem -= sizeof(fd_pcap_sll_hdr_t);
239 :
240 0 : } else {
241 :
242 0 : if( FD_UNLIKELY( fread( _hdr_buf, sizeof(fd_eth_hdr_t), 1, file ) != 1 ) ) {
243 0 : if( FD_UNLIKELY( !feof( file ) ) )
244 0 : FD_LOG_WARNING(( "packet eth header fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
245 0 : else FD_LOG_WARNING(( "packet eth header fread failed (truncated pcap file?)" ));
246 0 : return 0UL;
247 0 : }
248 :
249 0 : pkt_rem -= sizeof(fd_eth_hdr_t);
250 0 : }
251 0 : hdr_rem -= sizeof(fd_eth_hdr_t);
252 0 : _hdr_buf += sizeof(fd_eth_hdr_t);
253 :
254 : /* Deal with any VLAN tags */
255 0 : do {
256 0 : ushort net_type = hdr->net_type; /* In network byte order */
257 0 : while( FD_UNLIKELY( net_type == fd_ushort_bswap( FD_ETH_HDR_TYPE_VLAN ) ) ) {
258 0 : if( FD_UNLIKELY( hdr_rem<sizeof(fd_eth_hdr_t) ) ) { FD_LOG_WARNING(( "Header buffer too small for vlan tags" )); return 0; }
259 0 : if( FD_UNLIKELY( fread( _hdr_buf, sizeof(fd_vlan_tag_t), 1, file ) != 1 ) ) {
260 0 : if( FD_UNLIKELY( !feof( file ) ) )
261 0 : FD_LOG_WARNING(( "packet vlan tag fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
262 0 : else
263 0 : FD_LOG_WARNING(( "packet vlan tag fread failed (truncated pcap file?)" ));
264 0 : return 0;
265 0 : }
266 0 : net_type = ((fd_vlan_tag_t *)_hdr_buf)->net_type;
267 0 : _hdr_buf += sizeof(fd_vlan_tag_t);
268 0 : hdr_rem -= sizeof(fd_vlan_tag_t);
269 0 : pkt_rem -= sizeof(fd_vlan_tag_t);
270 0 : }
271 :
272 0 : if( FD_UNLIKELY( net_type != fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) ) ) break;
273 :
274 : /* Deal with IP header */
275 :
276 0 : if( FD_UNLIKELY( hdr_rem<sizeof(fd_ip4_hdr_t) ) ) { FD_LOG_WARNING(( "Header buffer too small for IP header" )); return 0; }
277 :
278 0 : if( FD_UNLIKELY( fread( _hdr_buf, sizeof(fd_ip4_hdr_t), 1, file )!=1 ) ) {
279 0 : if( FD_UNLIKELY( !feof( file ) ) )
280 0 : FD_LOG_WARNING(( "packet ip4 hdr fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
281 0 : else
282 0 : FD_LOG_WARNING(( "packet ip4 hdr fread failed (truncated pcap file?)" ));
283 0 : return 0;
284 0 : }
285 :
286 0 : fd_ip4_hdr_t * ip4 = (fd_ip4_hdr_t *)_hdr_buf;
287 0 : ulong options_len = 4u * ( FD_IP4_GET_IHL(*ip4) - 5u );
288 0 : uchar protocol = ip4->protocol;
289 :
290 0 : _hdr_buf += sizeof(fd_ip4_hdr_t);
291 0 : hdr_rem -= sizeof(fd_ip4_hdr_t);
292 0 : pkt_rem -= sizeof(fd_ip4_hdr_t);
293 :
294 : /* ... and any IP options */
295 :
296 0 : if( FD_UNLIKELY( hdr_rem<options_len ) ) { FD_LOG_WARNING(( "Header buffer too small for IP options" )); return 0; }
297 :
298 0 : if( FD_UNLIKELY( options_len ) ) {
299 0 : if( FD_UNLIKELY( fread( _hdr_buf, options_len, 1, file )!=1 ) ) {
300 0 : if( FD_UNLIKELY( !feof( file ) ) )
301 0 : FD_LOG_WARNING(( "packet ip4 hdr options fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
302 0 : else
303 0 : FD_LOG_WARNING(( "packet ip4 hdr options fread failed (truncated pcap file?)" ));
304 0 : return 0;
305 0 : }
306 :
307 0 : _hdr_buf += options_len;
308 0 : hdr_rem -= options_len;
309 0 : pkt_rem -= options_len;
310 0 : }
311 :
312 0 : if( FD_UNLIKELY( protocol != FD_IP4_HDR_PROTOCOL_UDP ) ) break;
313 :
314 : /* Deal with UDP header */
315 :
316 0 : if( FD_UNLIKELY( hdr_rem<sizeof(fd_udp_hdr_t) ) ) { FD_LOG_WARNING(( "Header buffer too small for UDP hdr" )); return 0; }
317 :
318 0 : if( FD_UNLIKELY( fread( _hdr_buf, sizeof(fd_udp_hdr_t), 1, file ) )!=1 ) {
319 0 : if( FD_UNLIKELY( !feof( file ) ) )
320 0 : FD_LOG_WARNING(( "packet udp hdr fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
321 0 : else
322 0 : FD_LOG_WARNING(( "packet udp hdr fread failed (truncated pcap file?)" ));
323 0 : return 0;
324 0 : }
325 :
326 0 : _hdr_buf += sizeof(fd_udp_hdr_t);
327 0 : hdr_rem -= sizeof(fd_udp_hdr_t);
328 0 : pkt_rem -= sizeof(fd_udp_hdr_t);
329 :
330 0 : } while( 0 );
331 :
332 0 : if( FD_UNLIKELY( pld_rem<pkt_rem ) ) {
333 0 : FD_LOG_WARNING(( "Payload buffer (%lu) too small for payload (%lu)", pld_rem, pkt_rem ));
334 0 : return 0;
335 0 : }
336 :
337 0 : if( FD_UNLIKELY( fread( pld_buf, pkt_rem, 1, file )!=1 ) ) {
338 0 : if( FD_UNLIKELY( !feof( file ) ) )
339 0 : FD_LOG_WARNING(( "packet payload fread failed (%i-%s)", errno, fd_io_strerror( errno ) ));
340 0 : else
341 0 : FD_LOG_WARNING(( "packet payload fread failed (truncated pcap file?)" ));
342 0 : }
343 :
344 0 : *pld_sz = pkt_rem;
345 0 : *hdr_sz = *hdr_sz - hdr_rem;
346 0 : *_pkt_ts = ((long)pcap->usec) + 1000000000L*((long)pcap->sec); /* Note: assumes ns resolution capture */
347 0 : return 1;
348 0 : }
349 :
350 0 : #define FD_PCAP_SNAPLEN (USHORT_MAX + 64UL + 4UL)
351 :
352 : ulong
353 : fd_pcap_fwrite_hdr( void * file,
354 0 : uint link_layer_type ) {
355 0 : fd_pcap_hdr_t hdr[1];
356 0 : hdr->magic_number = 0xa1b23c4dU;
357 0 : hdr->version_major = (ushort)2;
358 0 : hdr->version_minor = (ushort)4;
359 0 : hdr->thiszone = 0;
360 0 : hdr->sigfigs = 0U;
361 0 : hdr->snaplen = (uint)FD_PCAP_SNAPLEN;
362 0 : hdr->network = link_layer_type;
363 0 : return fwrite( hdr, sizeof(fd_pcap_hdr_t), 1UL, (FILE *)file );
364 0 : }
365 :
366 : ulong
367 : fd_pcap_fwrite_pkt( long ts,
368 : void const * _hdr,
369 : ulong hdr_sz,
370 : void const * _payload,
371 : ulong payload_sz,
372 : uint _fcs,
373 0 : void * file ) {
374 :
375 0 : ulong pkt_sz = hdr_sz + payload_sz;
376 0 : if( FD_UNLIKELY( ( (pkt_sz<hdr_sz) /* overflow */ |
377 0 : (pkt_sz>(FD_PCAP_SNAPLEN-sizeof(fd_pcap_pkt_hdr_t)-sizeof(uint))) ) ) ) {
378 0 : FD_LOG_WARNING(( "packet size too large for pcap" ));
379 0 : return 0UL;
380 0 : }
381 0 : pkt_sz += sizeof(fd_pcap_pkt_hdr_t) + sizeof(uint);
382 :
383 0 : uchar pkt[ FD_PCAP_SNAPLEN ];
384 :
385 0 : uchar * p = pkt;
386 0 : fd_pcap_pkt_hdr_t * pcap = (fd_pcap_pkt_hdr_t *)p; p += sizeof(fd_pcap_pkt_hdr_t);
387 0 : uchar * hdr = (uchar * )p; p += hdr_sz;
388 0 : uchar * payload = (uchar * )p; p += payload_sz;
389 0 : uint * fcs = (uint * )p; p += sizeof(uint);
390 :
391 0 : pcap->sec = (uint)(((ulong)ts) / (ulong)1e9);
392 0 : pcap->usec = (uint)(((ulong)ts) % (ulong)1e9); /* Actually nsec */
393 0 : pcap->incl_len = (uint)( pkt_sz - sizeof(fd_pcap_pkt_hdr_t) );
394 0 : pcap->orig_len = (uint)( pkt_sz - sizeof(fd_pcap_pkt_hdr_t) );
395 :
396 0 : fd_memcpy( hdr, _hdr, hdr_sz );
397 0 : fd_memcpy( payload, _payload, payload_sz );
398 0 : fcs[0] = _fcs;
399 :
400 0 : if( FD_UNLIKELY( fwrite( pkt, pkt_sz, 1UL, (FILE *)file )!=1UL ) ) { FD_LOG_WARNING(( "fwrite failed" )); return 0UL; }
401 0 : return 1UL;
402 0 : }
403 :
404 : #else
405 :
406 : /* Implement pcap support for this target */
407 :
408 : #endif
|