source: lib/format_bpf.c @ a181666

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since a181666 was a181666, checked in by Shane Alcock <salcock@…>, 8 years ago

Possible fix for the BPF header contains a struct timeval bug

  • Property mode set to 100644
File size: 18.5 KB
Line 
1/*
2 * This file is part of libtrace
3 *
4 * Copyright (c) 2007,2008,2009,2010 The University of Waikato, Hamilton,
5 * New Zealand.
6 *
7 * Authors: Daniel Lawson
8 *          Perry Lorier
9 *          Shane Alcock
10 *         
11 * All rights reserved.
12 *
13 * This code has been developed by the University of Waikato WAND
14 * research group. For further information please see http://www.wand.net.nz/
15 *
16 * libtrace is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * libtrace is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with libtrace; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29 *
30 * $Id$
31 *
32 */
33
34#include "libtrace.h"
35#include "libtrace_int.h"
36#include "format_helper.h"
37#include "config.h"
38#include "stdlib.h"
39
40#ifdef HAVE_INTTYPES_H
41#  include <inttypes.h>
42#else
43# error "Can't find inttypes.h"
44#endif
45
46#include <sys/socket.h>
47
48#include <sys/types.h>
49#include <sys/time.h>
50#include <sys/ioctl.h>
51
52#include <string.h>
53#include <net/if.h>
54#include <sys/ioctl.h>
55#include <errno.h>
56#include <fcntl.h>
57#include <unistd.h>
58
59/* This format deals with the BSD Native capture format, perhaps better
60 * known as BPF, which is the equivalent of the Linux Native format for
61 * *BSD systems.
62 *
63 * This is a LIVE capture format - we're always dealing with reading from an
64 * interface.
65 *
66 * This format does not support writing, but BPF packet records can be easily
67 * converted to PCAP or ERF.
68 */ 
69
70struct bpf_timeval {
71        uint32_t tv_sec;
72        uint32_t tv_usec;
73};
74
75struct libtrace_bpf_hdr {
76        struct bpf_timeval bh_tstamp;   /* timestamp */
77        uint32_t bh_caplen;             /* capture length */
78        uint32_t bh_datalen;            /* wire length */
79        uint16_t bh_hdrlen;             /* header length (incl padding) */
80};
81
82struct local_bpf_hdr {
83        struct timeval bh_tstamp;
84        uint32_t bh_caplen;
85        uint32_t bh_datalen;
86        uint16_t bh_hdrlen;
87};
88
89#define BPFHDR(x) ((struct libtrace_bpf_hdr *)((x)->header))
90
91#if HAVE_DECL_BIOCSETIF
92/* "Global" data that is stored for each BPF input trace */
93struct libtrace_format_data_t {
94        /* The file descriptor that is being captured from */
95        int fd; 
96        /* The snap length for the capture */
97        int snaplen;
98        /* A boolean flag indicating whether the capture interface should be
99         * in promiscuous mode */ 
100        int promisc;
101        /* A buffer to write captured data into */
102        void *buffer;
103        /* The current read location in the capture buffer */
104        void *bufptr;
105        /* The total size of the capture buffer */
106        unsigned int buffersize;
107        /* The amount of space remaining before the capture buffer is full */
108        int remaining;
109        /* The linktype of the capture interface */ 
110        unsigned int linktype;
111        /* Statistics about how many packets have been dropped, received etc. */
112        struct bpf_stat stats;
113        /* A boolean flag indicating whether the statistics are up-to-date */
114        int stats_valid;
115};
116
117#define FORMATIN(x) ((struct libtrace_format_data_t*)((x->format_data)))
118
119
120/* Attempts to determine if a given filename could refer to a BPF interface */
121static int bpf_probe_filename(const char *filename)
122{
123        return (if_nametoindex(filename) != 0);
124}
125
126/* Initialises a BPF input trace */
127static int bpf_init_input(libtrace_t *libtrace) 
128{
129        libtrace->format_data = (struct libtrace_format_data_t *)
130                malloc(sizeof(struct libtrace_format_data_t));
131       
132        /* Throw some default values into the format data */
133        FORMATIN(libtrace)->fd = -1;
134        FORMATIN(libtrace)->promisc = 0;
135        FORMATIN(libtrace)->snaplen = 65536;
136        FORMATIN(libtrace)->stats_valid = 0;
137
138        return 0;
139}
140
141/* Starts a BPF input trace */
142static int bpf_start_input(libtrace_t *libtrace)
143{
144        int bpfid=0;
145        struct bpf_version bv;
146        struct ifreq ifr;
147        unsigned int v;
148
149        /* Find and open a bpf device */
150        do {
151                char buffer[64];
152                snprintf(buffer,sizeof(buffer),"/dev/bpf%d", bpfid);
153                bpfid++;
154               
155                FORMATIN(libtrace)->fd = open(buffer, O_RDONLY);
156        } while(FORMATIN(libtrace)->fd == -1 && errno == EBUSY);
157
158        if (FORMATIN(libtrace)->fd == -1) {
159                trace_set_err(libtrace,TRACE_ERR_INIT_FAILED,
160                                "No free bpf devices");
161                return -1;
162        }
163
164        /* Check the BPF Version is ok */
165        if (ioctl(FORMATIN(libtrace)->fd, BIOCVERSION, &bv) == -1) {
166                trace_set_err(libtrace,errno,
167                                "Failed to read the bpf version");
168                close(FORMATIN(libtrace)->fd);
169                return -1;
170        }
171
172        if (bv.bv_major != BPF_MAJOR_VERSION) {
173                trace_set_err(libtrace,errno, 
174                        "Unknown kernel BPF version (%d.%d, libtrace requires at least %d.%d)",
175                        bv.bv_major,
176                        bv.bv_minor,
177                        BPF_MAJOR_VERSION,
178                        BPF_MINOR_VERSION);
179                close(FORMATIN(libtrace)->fd);
180                return -1;
181        }
182
183        if (bv.bv_minor < BPF_MINOR_VERSION) {
184                trace_set_err(libtrace,errno, "Kernel version too old (%d.%d, libtrace requires at least %d.%d)",
185                        bv.bv_major,
186                        bv.bv_minor,
187                        BPF_MAJOR_VERSION,
188                        BPF_MINOR_VERSION);
189                close(FORMATIN(libtrace)->fd);
190                return -1;
191        }
192
193        /* We assume the default kernel buffer size is sufficient. */
194        if (ioctl(FORMATIN(libtrace)->fd, BIOCGBLEN,
195                        &FORMATIN(libtrace)->buffersize)==-1) {
196                trace_set_err(libtrace,errno,"Failed to find buffer length");
197                close(FORMATIN(libtrace)->fd);
198                return -1;
199        }
200
201        FORMATIN(libtrace)->buffer = malloc(FORMATIN(libtrace)->buffersize);
202        FORMATIN(libtrace)->bufptr = FORMATIN(libtrace)->buffer;
203        FORMATIN(libtrace)->remaining = 0;
204
205        /* Attach to the device */
206        strncpy(ifr.ifr_name, libtrace->uridata, sizeof(ifr.ifr_name));
207        if (ioctl(FORMATIN(libtrace)->fd, BIOCSETIF, &ifr) == -1) {
208                trace_set_err(libtrace,errno,"Failed to attach");
209                close(FORMATIN(libtrace)->fd);
210                return -1;
211        }
212
213        /* Set the link type */
214        if (ioctl(FORMATIN(libtrace)->fd, BIOCGDLT,
215                         &FORMATIN(libtrace)->linktype) == -1) {
216                trace_set_err(libtrace,errno,"Failed to retrieve link type");
217                close(FORMATIN(libtrace)->fd);
218                return -1;
219        }
220       
221        /* TODO: If BIOCGDLTLIST exists then we should perhaps do something
222         *       with it.  We don't have the same concept of multiple DLT's
223         *       as pcap does.  We grab the rawest possible thing and then
224         *       decode packets by understanding the protocols.  So perhaps
225         *       we should setup a rating of DLT's that we'll prefer in order.
226         *       For example we should try and get 802.11 frames rather than
227         *       802.3 frames.  The general rule should be "whatever actually
228         *       went over the air", although of course if we don't support
229         *       what went over the air we should fall back to something we
230         *       /do/ support.
231         */
232       
233        /* Using timeouts seems sucky.  We'll always use immediate mode.  We
234         * pray the kernel is smart enough that if a another packet arrives
235         * while we're processing this one that it will buffer them into it's
236         * kernel buffer so we can receive packets later. (It'll need to do this
237         * to deal with us spending time processing the last 'n' packets anyway)
238         */
239       
240        v=1;
241        if (ioctl(FORMATIN(libtrace)->fd, BIOCIMMEDIATE, &v) == -1) {
242                trace_set_err(libtrace,errno,"Failed to set immediate mode");
243                close(FORMATIN(libtrace)->fd);
244                return -1;
245        }
246
247        /* Set promiscous mode, if the user has asked us to do so */
248        if (FORMATIN(libtrace)->promisc) {
249                if (ioctl(FORMATIN(libtrace)->fd, BIOCPROMISC, NULL) == -1) {
250                        trace_set_err(libtrace,errno,
251                                "Failed to set promisc mode");
252                        close(FORMATIN(libtrace)->fd);
253                        return -1;
254
255                }
256        }
257
258        FORMATIN(libtrace)->stats_valid = 0;
259
260        /* TODO: we should always set a bpf filter for snapping */
261
262        /* We're done! */
263        return 0;
264}
265
266/* Gets a count of the number of packets received on the BPF interface */
267static uint64_t bpf_get_received_packets(libtrace_t *trace)
268{
269        if (trace->format_data == NULL)
270                return (uint64_t)-1;
271
272        if (FORMATIN(trace)->fd == -1) {
273                /* Almost certainly a 'dead' trace so there is no socket
274                 * for us to query */
275                return (uint64_t) -1;
276        }
277        /* If we're called with stats_valid == 0, or we're called again
278         * then refresh the stats.  Don't refresh the stats if we're called
279         * immediately after get_dropped_packets
280         */
281        if ((FORMATIN(trace)->stats_valid & 1)
282                || (FORMATIN(trace)->stats_valid == 0)) {
283                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
284                FORMATIN(trace)->stats_valid |= 1;
285        }
286
287        return FORMATIN(trace)->stats.bs_recv;
288}
289
290/* Gets a count of the number of packets dropped on the BPF interface */
291static uint64_t bpf_get_dropped_packets(libtrace_t *trace)
292{
293        if (trace->format_data == NULL)
294                return (uint64_t)-1;
295
296        if (FORMATIN(trace)->fd == -1) {
297                /* Almost certainly a 'dead' trace so there is no socket
298                 * for us to query */
299                return (uint64_t) -1;
300        }
301        /* If we're called with stats_valid == 0, or we're called again
302         * then refresh the stats.  Don't refresh the stats if we're called
303         * immediately after get_received_packets
304         */
305        if ((FORMATIN(trace)->stats_valid & 2) 
306                || (FORMATIN(trace)->stats_valid == 0)) {
307                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
308                FORMATIN(trace)->stats_valid |= 2;
309        }
310
311        return FORMATIN(trace)->stats.bs_drop;
312}
313
314/* Pauses a BPF input trace */
315static int bpf_pause_input(libtrace_t *libtrace)
316{
317        close(FORMATIN(libtrace)->fd);
318        FORMATIN(libtrace)->fd=-1;
319
320        return 0;
321}
322
323/* Closes a BPF input trace */
324static int bpf_fin_input(libtrace_t *libtrace) 
325{
326        free(libtrace->format_data);
327        return 0;
328}
329
330/* Configures a BPF input trace */
331static int bpf_config_input(libtrace_t *libtrace,
332                trace_option_t option,
333                void *data)
334{
335        switch(option) {
336                case TRACE_OPTION_SNAPLEN:
337                        FORMATIN(libtrace)->snaplen=*(int*)data;
338                        return 0;
339                case TRACE_OPTION_PROMISC:
340                        FORMATIN(libtrace)->promisc=*(int*)data;
341                        return 0;
342                case TRACE_OPTION_FILTER:
343                        /* We don't support bpf filters in any special way
344                         * so return an error and let libtrace deal with
345                         * emulating it
346                         */
347                        break;
348                case TRACE_OPTION_META_FREQ:
349                        /* No meta-data for this format */
350                        break;
351                case TRACE_OPTION_EVENT_REALTIME:
352                        /* Captures are always realtime */
353                        break;
354
355                /* Avoid default: so that future options will cause a warning
356                 * here to remind us to implement it, or flag it as
357                 * unimplementable
358                 */
359        }
360        return -1;
361}
362
363#endif  /* HAVE_DECL_BIOCSETIF */
364
365/* Converts a buffer containing a recently read BPF packet record into a
366 * libtrace packet */
367static int bpf_prepare_packet(libtrace_t *libtrace UNUSED, 
368                libtrace_packet_t *packet,
369                void *buffer, libtrace_rt_types_t rt_type, uint32_t flags) {
370       
371        struct local_bpf_hdr orig;
372        struct local_bpf_hdr *ptr;
373        struct libtrace_bpf_hdr *replace;
374
375        /* If the packet previously owned a buffer that is not the buffer
376         * that contains the new packet data, we're going to need to free the
377         * old one to avoid memory leaks */
378        if (packet->buffer != buffer &&
379                        packet->buf_control == TRACE_CTRL_PACKET) {
380                free(packet->buffer);
381        }
382
383        /* Set the buffer owner appropriately */
384        if ((flags & TRACE_PREP_OWN_BUFFER) == TRACE_PREP_OWN_BUFFER) {
385                packet->buf_control = TRACE_CTRL_PACKET;
386        } else
387                packet->buf_control = TRACE_CTRL_EXTERNAL;
388
389        /* Update the packet pointers and type appropriately */
390        packet->buffer = buffer;
391        packet->header = buffer;
392        packet->type = rt_type;
393
394        /* FreeBSD is stupid and uses a timeval in the bpf header
395         * structure. This means that sometimes our timestamp consumes
396         * 8 bytes and sometimes it consumes 16 bytes.
397         *
398         * Let's try to standardise our header a bit, hopefully without
399         * overwriting anything else important */
400
401        ptr = ((struct local_bpf_hdr *)(packet->header));
402        replace = ((struct libtrace_bpf_hdr *)(packet->header));
403        orig = *ptr;
404       
405        replace->bh_tstamp.tv_sec = (uint32_t) (orig.bh_tstamp.tv_sec & 0xffffffff);
406        replace->bh_tstamp.tv_usec = (uint32_t) (orig.bh_tstamp.tv_usec & 0xffffffff);
407        replace->bh_caplen = orig.bh_caplen;
408        replace->bh_datalen = orig.bh_datalen;
409        replace->bh_hdrlen = orig.bh_hdrlen;
410               
411
412        /* Find the payload */
413        /* TODO: Pcap deals with a padded FDDI linktype here */
414        packet->payload=(char *)buffer + BPFHDR(packet)->bh_hdrlen;
415
416        /*
417        if (libtrace->format_data == NULL) {
418                if (bpf_init_input(libtrace))
419                        return -1;
420        }
421        */
422
423        return 0;
424}
425
426#if HAVE_DECL_BIOCSETIF
427
428/* Reads the next packet record from a BPF interface and writes it into a
429 * libtrace packet */   
430static int bpf_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) 
431{
432        uint32_t flags = 0;
433
434        packet->type = bpf_linktype_to_rt(FORMATIN(libtrace)->linktype);
435       
436        /* Read from the BPF interface into our capture buffer */
437        if (FORMATIN(libtrace)->remaining<=0) {
438                int ret;
439
440                ret=read(FORMATIN(libtrace)->fd,
441                        FORMATIN(libtrace)->buffer,
442                        FORMATIN(libtrace)->buffersize);
443
444                if (ret == -1) {
445                        trace_set_err(libtrace,errno,"Failed to read");
446                        return -1;
447                }
448
449                if (ret == 0) {
450                        /* EOF */
451                        return 0;
452                }
453
454                FORMATIN(libtrace)->remaining=ret;
455                FORMATIN(libtrace)->bufptr=
456                                FORMATIN(libtrace)->buffer;
457        }
458
459        /* We do NOT want anything trying to free the memory the packet is
460         * stored in */
461        flags |= TRACE_PREP_DO_NOT_OWN_BUFFER;
462
463        if (packet->buf_control == TRACE_CTRL_PACKET)
464                free(packet->buffer);
465
466        /* Update 'packet' to point to the first packet in our capture
467         * buffer */
468        if (bpf_prepare_packet(libtrace, packet, FORMATIN(libtrace)->bufptr,
469                        packet->type, flags)) {
470                return -1;
471        }
472       
473
474        /* Skip past the packet record we're going to return, making sure
475         * that we deal with padding correctly */
476        FORMATIN(libtrace)->bufptr+=
477                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
478                +BPFHDR(packet)->bh_caplen);
479        FORMATIN(libtrace)->remaining-=
480                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
481                +BPFHDR(packet)->bh_caplen);
482
483        return BPFHDR(packet)->bh_datalen+BPFHDR(packet)->bh_hdrlen;
484}
485
486#endif  /* HAVE_DECL_BIOCSETIF */
487
488/* Returns the linktype for the interface that we are capturing from */
489static libtrace_linktype_t bpf_get_link_type(const libtrace_packet_t *packet) {
490        /* Convert the linktype that we recorded when we started the trace
491         * into a suitable libtrace linktype */
492        return pcap_linktype_to_libtrace(rt_to_pcap_linktype(packet->type));
493}
494
495/* Returns the direction for a given BPF packet record */
496static libtrace_direction_t bpf_get_direction(const libtrace_packet_t *packet UNUSED) {
497        /* BPF sadly can't do direction tagging */
498        return ~0;
499}
500
501/* Returns the timestamp for a given BPF packet record, in the form of a
502 * struct timeval */
503static struct timeval bpf_get_timeval(const libtrace_packet_t *packet) 
504{
505        struct timeval tv;
506        /* OpenBSD uses a bpf_timeval rather than a timeval so we must copy
507         * each timeval element individually rather than doing a structure
508         * assignment */
509        tv.tv_sec = BPFHDR(packet)->bh_tstamp.tv_sec;
510        tv.tv_usec = BPFHDR(packet)->bh_tstamp.tv_usec;
511
512        return tv;
513}
514
515/* Returns the capture length for a given BPF packet record */
516static int bpf_get_capture_length(const libtrace_packet_t *packet)
517{
518        /* BPF doesn't include the FCS in its caplen field, but libtrace
519         * does so we need to add this extra 4 bytes */
520        return BPFHDR(packet)->bh_caplen+4;
521}
522
523/* Returns the wire length for a given BPF packet record */
524static int bpf_get_wire_length(const libtrace_packet_t *packet) 
525{
526
527        /* BPF doesn't include the FCS in its datalen field, but libtrace
528         * does so we need to add this extra 4 bytes */
529        return BPFHDR(packet)->bh_datalen+4;
530}
531
532/* Returns the framing length for a given BPF packet record */
533static int bpf_get_framing_length(UNUSED
534                const libtrace_packet_t *packet) 
535{
536        return BPFHDR(packet)->bh_hdrlen;
537}
538
539#if HAVE_DECL_BIOCSETIF
540/* Returns the file descriptor that the capture interface is operating on */
541static int bpf_get_fd(const libtrace_t *trace) {
542        return FORMATIN(trace)->fd;
543}
544
545/* Prints some slightly useful help text for the BPF capture format */
546static void bpf_help() {
547        printf("bpf format module: $Revision: 1782 $\n");
548        printf("Supported input URIs:\n");
549        printf("\tbpf:\n");
550        printf("\n");
551        return;
552}
553static struct libtrace_format_t bpf = {
554        "bpf",
555        "$Id$",
556        TRACE_FORMAT_BPF,
557        bpf_probe_filename,     /* probe filename */
558        NULL,                   /* probe magic */
559        bpf_init_input,         /* init_input */
560        bpf_config_input,       /* config_input */
561        bpf_start_input,        /* start_input */
562        bpf_pause_input,        /* pause_input */
563        NULL,                   /* init_output */
564        NULL,                   /* config_output */
565        NULL,                   /* start_ouput */
566        bpf_fin_input,          /* fin_input */
567        NULL,                   /* fin_output */
568        bpf_read_packet,        /* read_packet */
569        bpf_prepare_packet,     /* prepare_packet */
570        NULL,                   /* fin_packet */
571        NULL,                   /* write_packet */
572        bpf_get_link_type,      /* get_link_type */
573        bpf_get_direction,      /* get_direction */
574        NULL,                   /* set_direction */
575        NULL,                   /* get_erf_timestamp */
576        bpf_get_timeval,        /* get_timeval */
577        NULL,                   /* get_timespec */
578        NULL,                   /* get_seconds */
579        NULL,                   /* seek_erf */
580        NULL,                   /* seek_timeval */
581        NULL,                   /* seek_seconds */
582        bpf_get_capture_length, /* get_capture_length */
583        bpf_get_wire_length,    /* get_wire_length */
584        bpf_get_framing_length, /* get_framing_length */
585        NULL,                   /* set_capture_length */
586        bpf_get_received_packets,/* get_received_packets */
587        NULL,                   /* get_filtered_packets */
588        bpf_get_dropped_packets,/* get_dropped_packets */
589        NULL,                   /* get_captured_packets */
590        bpf_get_fd,             /* get_fd */
591        trace_event_device,     /* trace_event */
592        bpf_help,               /* help */
593        NULL
594};
595#else   /* HAVE_DECL_BIOCSETIF */
596/* Prints some slightly useful help text for the BPF capture format */
597static void bpf_help() {
598        printf("bpf format module: $Revision: 1782 $\n");
599        printf("Not supported on this host\n");
600        return;
601}
602static struct libtrace_format_t bpf = {
603        "bpf",
604        "$Id$",
605        TRACE_FORMAT_BPF,
606        NULL,                   /* probe filename */
607        NULL,                   /* probe magic */
608        NULL,                   /* init_input */
609        NULL,                   /* config_input */
610        NULL,                   /* start_input */
611        NULL,                   /* pause_input */
612        NULL,                   /* init_output */
613        NULL,                   /* config_output */
614        NULL,                   /* start_ouput */
615        NULL,                   /* fin_input */
616        NULL,                   /* fin_output */
617        NULL,                   /* read_packet */
618        bpf_prepare_packet,     /* prepare_packet */
619        NULL,                   /* fin_packet */
620        NULL,                   /* write_packet */
621        bpf_get_link_type,      /* get_link_type */
622        bpf_get_direction,      /* get_direction */
623        NULL,                   /* set_direction */
624        NULL,                   /* get_erf_timestamp */
625        bpf_get_timeval,        /* get_timeval */
626        NULL,                   /* get_timespec */
627        NULL,                   /* get_seconds */
628        NULL,                   /* seek_erf */
629        NULL,                   /* seek_timeval */
630        NULL,                   /* seek_seconds */
631        bpf_get_capture_length, /* get_capture_length */
632        bpf_get_wire_length,    /* get_wire_length */
633        bpf_get_framing_length, /* get_framing_length */
634        NULL,                   /* set_capture_length */
635        NULL,/* get_received_packets */
636        NULL,                   /* get_filtered_packets */
637        NULL,/* get_dropped_packets */
638        NULL,                   /* get_captured_packets */
639        NULL,                   /* get_fd */
640        NULL,                   /* trace_event */
641        bpf_help,               /* help */
642        NULL
643};
644#endif  /* HAVE_DECL_BIOCSETIF */
645
646void bpf_constructor() {
647        register_format(&bpf);
648}
Note: See TracBrowser for help on using the repository browser.