source: lib/format_bpf.c @ 1aa4bf7

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 1aa4bf7 was 1aa4bf7, checked in by Perry Lorier <perry@…>, 13 years ago

Support using timespec's for dealing with traces

  • Property mode set to 100644
File size: 12.7 KB
Line 
1/*
2 * This file is part of libtrace
3 *
4 * Copyright (c) 2007,2008 The University of Waikato, Hamilton, New Zealand.
5 * Authors: Perry Lorier
6 *         
7 * All rights reserved.
8 *
9 * This code has been developed by the University of Waikato WAND
10 * research group. For further information please see http://www.wand.net.nz/
11 *
12 * libtrace is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * libtrace is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with libtrace; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 *
26 * $Id$
27 *
28 */
29
30#include "libtrace.h"
31#include "libtrace_int.h"
32#include "format_helper.h"
33#include "config.h"
34#include "stdlib.h"
35
36#ifdef HAVE_INTTYPES_H
37#  include <inttypes.h>
38#else
39# error "Can't find inttypes.h"
40#endif
41
42#include <sys/socket.h>
43
44#include <sys/types.h>
45#include <sys/time.h>
46#include <sys/ioctl.h>
47
48#include <string.h>
49#include <net/if.h>
50#include <sys/ioctl.h>
51#include <errno.h>
52#include <fcntl.h>
53
54
55struct libtrace_format_data_t {
56        int fd;
57        int snaplen;
58        int promisc;
59        void *buffer;
60        void *bufptr;
61        unsigned int buffersize;
62        int remaining;
63        unsigned int linktype;
64        struct bpf_stat stats;
65        int stats_valid;
66};
67
68#define FORMATIN(x) ((struct libtrace_format_data_t*)((x->format_data)))
69
70#define BPFHDR(x) ((struct bpf_hdr *)((x)->header))
71
72static int bpf_probe_filename(const char *filename)
73{
74        return (if_nametoindex(filename) != 0);
75}
76
77static int bpf_start_filename(const char *filename)
78{
79        return 0;
80}
81
82static int bpf_init_input(libtrace_t *libtrace) 
83{
84        libtrace->format_data = (struct libtrace_format_data_t *)
85                malloc(sizeof(struct libtrace_format_data_t));
86        FORMATIN(libtrace)->fd = -1;
87        FORMATIN(libtrace)->promisc = 0;
88        FORMATIN(libtrace)->snaplen = 65536;
89        FORMATIN(libtrace)->stats_valid = 0;
90
91        return 0;
92}
93
94static int bpf_start_input(libtrace_t *libtrace)
95{
96        int bpfid=0;
97        struct bpf_version bv;
98        struct ifreq ifr;
99        unsigned int v;
100
101        /* Find and open a bpf device */
102        do {
103                char buffer[64];
104                snprintf(buffer,sizeof(buffer),"/dev/bpf%d", bpfid);
105                bpfid++;
106               
107                FORMATIN(libtrace)->fd = open(buffer, O_RDONLY);
108        } while(FORMATIN(libtrace)->fd == -1 && errno == EBUSY);
109
110        if (FORMATIN(libtrace)->fd == -1) {
111                trace_set_err(libtrace,TRACE_ERR_INIT_FAILED,
112                                "No free bpf devices");
113                return -1;
114        }
115
116        /* Check the BPF Version is ok */
117        if (ioctl(FORMATIN(libtrace)->fd, BIOCVERSION, &bv) == -1) {
118                trace_set_err(libtrace,errno,
119                                "Failed to read the bpf version");
120                close(FORMATIN(libtrace)->fd);
121                return -1;
122        }
123
124        if (bv.bv_major != BPF_MAJOR_VERSION) {
125                trace_set_err(libtrace,errno, 
126                        "Unknown kernel BPF version (%d.%d, libtrace requires at least %d.%d)",
127                        bv.bv_major,
128                        bv.bv_minor,
129                        BPF_MAJOR_VERSION,
130                        BPF_MINOR_VERSION);
131                close(FORMATIN(libtrace)->fd);
132                return -1;
133        }
134
135        if (bv.bv_minor < BPF_MINOR_VERSION) {
136                trace_set_err(libtrace,errno, "Kernel version too old (%d.%d, libtrace requires at least %d.%d)",
137                        bv.bv_major,
138                        bv.bv_minor,
139                        BPF_MAJOR_VERSION,
140                        BPF_MINOR_VERSION);
141                close(FORMATIN(libtrace)->fd);
142                return -1;
143        }
144
145        /* We assume the default kernel buffer size is sufficient. */
146        if (ioctl(FORMATIN(libtrace)->fd, BIOCGBLEN,
147                        &FORMATIN(libtrace)->buffersize)==-1) {
148                trace_set_err(libtrace,errno,"Failed to find buffer length");
149                close(FORMATIN(libtrace)->fd);
150                return -1;
151        }
152
153        FORMATIN(libtrace)->buffer = malloc(FORMATIN(libtrace)->buffersize);
154        FORMATIN(libtrace)->bufptr = FORMATIN(libtrace)->buffer;
155        FORMATIN(libtrace)->remaining = 0;
156
157        /* attach to the device */
158        strncpy(ifr.ifr_name, libtrace->uridata, sizeof(ifr.ifr_name));
159        if (ioctl(FORMATIN(libtrace)->fd, BIOCSETIF, &ifr) == -1) {
160                trace_set_err(libtrace,errno,"Failed to attach");
161                close(FORMATIN(libtrace)->fd);
162                return -1;
163        }
164
165        if (ioctl(FORMATIN(libtrace)->fd, BIOCGDLT,
166                         &FORMATIN(libtrace)->linktype) == -1) {
167                trace_set_err(libtrace,errno,"Failed to retrieve link type");
168                close(FORMATIN(libtrace)->fd);
169                return -1;
170        }
171       
172        /* TODO: If BIOCGDLTLIST exists then we should perhaps do something
173         *       with it.  We don't have the same concept of multiple DLT's
174         *       as pcap does.  We grab the rawest possible thing and then
175         *       decode packets by understanding the protocols.  So perhaps
176         *       we should setup a rating of DLT's that we'll prefer in order.
177         *       For example we should try and get 802.11 frames rather than
178         *       802.3 frames.  The general rule should be "whatever actually
179         *       went over the air", although of course if we don't support
180         *       what went over the air we should fall back to something we
181         *       /do/ support.
182         */
183       
184        /* Using timeouts seems sucky.  We'll always use immediate mode.  We
185         * pray the kernel is smart enough that if a another packet arrives
186         * while we're processing this one that it will buffer them into it's
187         * kernel buffer so we can recieve packets later. (It'll need to do this
188         * to deal with us spending time processing the last 'n' packets anyway)
189         */
190       
191        v=1;
192        if (ioctl(FORMATIN(libtrace)->fd, BIOCIMMEDIATE, &v) == -1) {
193                trace_set_err(libtrace,errno,"Failed to set immediate mode");
194                close(FORMATIN(libtrace)->fd);
195                return -1;
196        }
197
198        if (FORMATIN(libtrace)->promisc) {
199                if (ioctl(FORMATIN(libtrace)->fd, BIOCPROMISC, NULL) == -1) {
200                        trace_set_err(libtrace,errno,
201                                "Failed to set promisc mode");
202                        close(FORMATIN(libtrace)->fd);
203                        return -1;
204
205                }
206        }
207
208        FORMATIN(libtrace)->stats_valid = 0;
209
210        /* TODO: we should always set a bpf filter for snapping */
211
212        /* We're done! */
213        return 0;
214}
215
216static uint64_t bpf_get_received_packets(libtrace_t *trace)
217{
218        if (trace->format_data == NULL)
219                return (uint64_t)-1;
220
221        if (FORMATIN(trace)->fd == -1) {
222                /* Almost certainly a 'dead' trace so there is no socket
223                 * for us to query */
224                return (uint64_t) -1;
225        }
226        /* If we're called with stats_valid == 0, or we're called again
227         * then refresh the stats.  Don't refresh the stats if we're called
228         * immediately after get_dropped_packets
229         */
230        if ((FORMATIN(trace)->stats_valid & 1)
231                || (FORMATIN(trace)->stats_valid == 0)) {
232                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
233                FORMATIN(trace)->stats_valid |= 1;
234        }
235
236        return FORMATIN(trace)->stats.bs_recv;
237}
238
239static uint64_t bpf_get_dropped_packets(libtrace_t *trace)
240{
241        if (trace->format_data == NULL)
242                return (uint64_t)-1;
243
244        if (FORMATIN(trace)->fd == -1) {
245                /* Almost certainly a 'dead' trace so there is no socket
246                 * for us to query */
247                return (uint64_t) -1;
248        }
249        /* If we're called with stats_valid == 0, or we're called again
250         * then refresh the stats.  Don't refresh the stats if we're called
251         * immediately after get_received_packets
252         */
253        if ((FORMATIN(trace)->stats_valid & 2) 
254                || (FORMATIN(trace)->stats_valid == 0)) {
255                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
256                FORMATIN(trace)->stats_valid |= 2;
257        }
258
259        return FORMATIN(trace)->stats.bs_drop;
260}
261
262static int bpf_pause_input(libtrace_t *libtrace)
263{
264        close(FORMATIN(libtrace)->fd);
265        FORMATIN(libtrace)->fd=-1;
266
267        return 0;
268}
269
270static int bpf_fin_input(libtrace_t *libtrace) 
271{
272        free(libtrace->format_data);
273        return 0;
274}
275
276static int bpf_config_input(libtrace_t *libtrace,
277                trace_option_t option,
278                void *data)
279{
280        switch(option) {
281                case TRACE_OPTION_SNAPLEN:
282                        FORMATIN(libtrace)->snaplen=*(int*)data;
283                        return 0;
284                case TRACE_OPTION_PROMISC:
285                        FORMATIN(libtrace)->promisc=*(int*)data;
286                        return 0;
287                case TRACE_OPTION_FILTER:
288                        /* We don't support bpf filters in any special way
289                         * so return an error and let libtrace deal with
290                         * emulating it
291                         */
292                        break;
293                case TRACE_OPTION_META_FREQ:
294                        /* No meta-data for this format */
295                        break;
296                case TRACE_OPTION_EVENT_REALTIME:
297                        /* captures are always realtime */
298                        break;
299
300                /* Avoid default: so that future options will cause a warning
301                 * here to remind us to implement it, or flag it as
302                 * unimplementable
303                 */
304        }
305        trace_set_err(libtrace,TRACE_ERR_UNKNOWN_OPTION,
306                        "Unknown option %i", option);
307        return -1;
308}
309
310static int bpf_prepare_packet(libtrace_t *libtrace, libtrace_packet_t *packet,
311                void *buffer, libtrace_rt_types_t rt_type, uint32_t flags) {
312        if (packet->buffer != buffer &&
313                        packet->buf_control == TRACE_CTRL_PACKET) {
314                free(packet->buffer);
315        }
316
317        if ((flags & TRACE_PREP_OWN_BUFFER) == TRACE_PREP_OWN_BUFFER) {
318                packet->buf_control = TRACE_CTRL_PACKET;
319        } else
320                packet->buf_control = TRACE_CTRL_EXTERNAL;
321
322
323        packet->buffer = buffer;
324        packet->header = buffer;
325        packet->type = rt_type;
326
327        /* Find the payload */
328        /* TODO: Pcap deals with a padded FDDI linktype here */
329        packet->payload=(char *)buffer + BPFHDR(packet)->bh_hdrlen;
330
331        if (libtrace->format_data == NULL) {
332                if (bpf_init_input(libtrace))
333                        return -1;
334        }
335
336        return 0;
337}
338       
339static int bpf_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) 
340{
341        uint32_t flags = 0;
342       
343        /* Fill the buffer */
344        if (FORMATIN(libtrace)->remaining<=0) {
345                int ret;
346
347                ret=read(FORMATIN(libtrace)->fd,
348                        FORMATIN(libtrace)->buffer,
349                        FORMATIN(libtrace)->buffersize);
350
351                if (ret == -1) {
352                        trace_set_err(libtrace,errno,"Failed to read");
353                        return -1;
354                }
355
356                if (ret == 0) {
357                        /* EOF */
358                        return 0;
359                }
360
361                FORMATIN(libtrace)->remaining=ret;
362                FORMATIN(libtrace)->bufptr=
363                                FORMATIN(libtrace)->buffer;
364        }
365        flags |= TRACE_PREP_DO_NOT_OWN_BUFFER;
366        /* Read one packet out */
367       
368        if (packet->buf_control == TRACE_CTRL_PACKET)
369                free(packet->buffer);
370
371        if (bpf_prepare_packet(libtrace, packet, FORMATIN(libtrace)->bufptr,
372                TRACE_RT_DATA_BPF, flags)) {
373                return -1;
374        }
375       
376
377        /* Now deal with any padding */
378        FORMATIN(libtrace)->bufptr+=
379                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
380                +BPFHDR(packet)->bh_caplen);
381        FORMATIN(libtrace)->remaining-=
382                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
383                +BPFHDR(packet)->bh_caplen);
384
385        return BPFHDR(packet)->bh_datalen+BPFHDR(packet)->bh_hdrlen;
386}
387
388static libtrace_linktype_t bpf_get_link_type(const libtrace_packet_t *packet) {
389        return pcap_linktype_to_libtrace(FORMATIN(packet->trace)->linktype);
390}
391
392static libtrace_direction_t bpf_get_direction(const libtrace_packet_t *packet) {
393        /* BPF Sadly can't do direction tagging */
394        return ~0;
395}
396
397static struct timeval bpf_get_timeval(const libtrace_packet_t *packet) 
398{
399        struct timeval tv;
400        /* OpenBSD uses a bpf_timeval rather than a timeval so we must copy
401         * each timeval element individually rather than doing a structure
402         * assignment */
403        tv.tv_sec = BPFHDR(packet)->bh_tstamp.tv_sec;
404        tv.tv_usec = BPFHDR(packet)->bh_tstamp.tv_usec;
405
406        return tv;
407}
408
409static int bpf_get_capture_length(const libtrace_packet_t *packet)
410{
411        /* BPF Doesn't include the FCS, we do. */
412        return BPFHDR(packet)->bh_caplen+4;
413}
414
415static int bpf_get_wire_length(const libtrace_packet_t *packet) 
416{
417        return BPFHDR(packet)->bh_datalen+4;
418}
419
420static int bpf_get_framing_length(UNUSED
421                const libtrace_packet_t *packet) 
422{
423        return BPFHDR(packet)->bh_hdrlen;
424}
425
426static int bpf_get_fd(const libtrace_t *trace) {
427        return FORMATIN(trace)->fd;
428}
429
430static void bpf_help() {
431        printf("bpf format module: $Revision$\n");
432        printf("Supported input URIs:\n");
433        printf("\tbpf:\n");
434        printf("\n");
435        return;
436}
437static struct libtrace_format_t bpf = {
438        "bpf",
439        "$Id$",
440        TRACE_FORMAT_BPF,
441        bpf_probe_filename,     /* probe filename */
442        NULL,                   /* probe magic */
443        bpf_init_input,         /* init_input */
444        bpf_config_input,       /* config_input */
445        bpf_start_input,        /* start_input */
446        bpf_pause_input,        /* pause_input */
447        NULL,                   /* init_output */
448        NULL,                   /* config_output */
449        NULL,                   /* start_ouput */
450        bpf_fin_input,          /* fin_input */
451        NULL,                   /* fin_output */
452        bpf_read_packet,        /* read_packet */
453        bpf_prepare_packet,     /* prepare_packet */
454        NULL,                   /* fin_packet */
455        NULL,                   /* write_packet */
456        bpf_get_link_type,      /* get_link_type */
457        bpf_get_direction,      /* get_direction */
458        NULL,                   /* set_direction */
459        NULL,                   /* get_erf_timestamp */
460        bpf_get_timeval,        /* get_timeval */
461        NULL,                   /* get_timespec */
462        NULL,                   /* get_seconds */
463        NULL,                   /* seek_erf */
464        NULL,                   /* seek_timeval */
465        NULL,                   /* seek_seconds */
466        bpf_get_capture_length, /* get_capture_length */
467        bpf_get_wire_length,    /* get_wire_length */
468        bpf_get_framing_length, /* get_framing_length */
469        NULL,                   /* set_capture_length */
470        bpf_get_received_packets,/* get_received_packets */
471        NULL,                   /* get_filtered_packets */
472        bpf_get_dropped_packets,/* get_dropped_packets */
473        NULL,                   /* get_captured_packets */
474        bpf_get_fd,             /* get_fd */
475        trace_event_device,     /* trace_event */
476        bpf_help,               /* help */
477        NULL
478};
479
480void bpf_constructor() {
481        register_format(&bpf);
482}
Note: See TracBrowser for help on using the repository browser.