source: lib/format_bpf.c @ c0dba7a

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since c0dba7a was c0dba7a, checked in by Shane Alcock <salcock@…>, 13 years ago
  • Fixed compile error on systems where bh_tstamp in the bpf header is a bpf_timeval rather than a timeval, e.g. OpenBSD ( Reported by Niclas Rosell )
  • Updated all formats that require it to use the new "Do not own the buffer" prepare_packet flag
  • Property mode set to 100644
File size: 12.5 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_init_input(libtrace_t *libtrace) 
73{
74        libtrace->format_data = (struct libtrace_format_data_t *)
75                malloc(sizeof(struct libtrace_format_data_t));
76        FORMATIN(libtrace)->fd = -1;
77        FORMATIN(libtrace)->promisc = 0;
78        FORMATIN(libtrace)->snaplen = 65536;
79        FORMATIN(libtrace)->stats_valid = 0;
80
81        return 0;
82}
83
84static int bpf_start_input(libtrace_t *libtrace)
85{
86        int bpfid=0;
87        struct bpf_version bv;
88        struct ifreq ifr;
89        unsigned int v;
90
91        /* Find and open a bpf device */
92        do {
93                char buffer[64];
94                snprintf(buffer,sizeof(buffer),"/dev/bpf%d", bpfid);
95                bpfid++;
96               
97                FORMATIN(libtrace)->fd = open(buffer, O_RDONLY);
98        } while(FORMATIN(libtrace)->fd == -1 && errno == EBUSY);
99
100        if (FORMATIN(libtrace)->fd == -1) {
101                trace_set_err(libtrace,TRACE_ERR_INIT_FAILED,
102                                "No free bpf devices");
103                return -1;
104        }
105
106        /* Check the BPF Version is ok */
107        if (ioctl(FORMATIN(libtrace)->fd, BIOCVERSION, &bv) == -1) {
108                trace_set_err(libtrace,errno,
109                                "Failed to read the bpf version");
110                close(FORMATIN(libtrace)->fd);
111                return -1;
112        }
113
114        if (bv.bv_major != BPF_MAJOR_VERSION) {
115                trace_set_err(libtrace,errno, 
116                        "Unknown kernel BPF version (%d.%d, libtrace requires at least %d.%d)",
117                        bv.bv_major,
118                        bv.bv_minor,
119                        BPF_MAJOR_VERSION,
120                        BPF_MINOR_VERSION);
121                close(FORMATIN(libtrace)->fd);
122                return -1;
123        }
124
125        if (bv.bv_minor < BPF_MINOR_VERSION) {
126                trace_set_err(libtrace,errno, "Kernel version too old (%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        /* We assume the default kernel buffer size is sufficient. */
136        if (ioctl(FORMATIN(libtrace)->fd, BIOCGBLEN,
137                        &FORMATIN(libtrace)->buffersize)==-1) {
138                trace_set_err(libtrace,errno,"Failed to find buffer length");
139                close(FORMATIN(libtrace)->fd);
140                return -1;
141        }
142
143        FORMATIN(libtrace)->buffer = malloc(FORMATIN(libtrace)->buffersize);
144        FORMATIN(libtrace)->bufptr = FORMATIN(libtrace)->buffer;
145        FORMATIN(libtrace)->remaining = 0;
146
147        /* attach to the device */
148        strncpy(ifr.ifr_name, libtrace->uridata, sizeof(ifr.ifr_name));
149        if (ioctl(FORMATIN(libtrace)->fd, BIOCSETIF, &ifr) == -1) {
150                trace_set_err(libtrace,errno,"Failed to attach");
151                close(FORMATIN(libtrace)->fd);
152                return -1;
153        }
154
155        if (ioctl(FORMATIN(libtrace)->fd, BIOCGDLT,
156                         &FORMATIN(libtrace)->linktype) == -1) {
157                trace_set_err(libtrace,errno,"Failed to retrieve link type");
158                close(FORMATIN(libtrace)->fd);
159                return -1;
160        }
161       
162        /* TODO: If BIOCGDLTLIST exists then we should perhaps do something
163         *       with it.  We don't have the same concept of multiple DLT's
164         *       as pcap does.  We grab the rawest possible thing and then
165         *       decode packets by understanding the protocols.  So perhaps
166         *       we should setup a rating of DLT's that we'll prefer in order.
167         *       For example we should try and get 802.11 frames rather than
168         *       802.3 frames.  The general rule should be "whatever actually
169         *       went over the air", although of course if we don't support
170         *       what went over the air we should fall back to something we
171         *       /do/ support.
172         */
173       
174        /* Using timeouts seems sucky.  We'll always use immediate mode.  We
175         * pray the kernel is smart enough that if a another packet arrives
176         * while we're processing this one that it will buffer them into it's
177         * kernel buffer so we can recieve packets later. (It'll need to do this
178         * to deal with us spending time processing the last 'n' packets anyway)
179         */
180       
181        v=1;
182        if (ioctl(FORMATIN(libtrace)->fd, BIOCIMMEDIATE, &v) == -1) {
183                trace_set_err(libtrace,errno,"Failed to set immediate mode");
184                close(FORMATIN(libtrace)->fd);
185                return -1;
186        }
187
188        if (FORMATIN(libtrace)->promisc) {
189                if (ioctl(FORMATIN(libtrace)->fd, BIOCPROMISC, NULL) == -1) {
190                        trace_set_err(libtrace,errno,
191                                "Failed to set promisc mode");
192                        close(FORMATIN(libtrace)->fd);
193                        return -1;
194
195                }
196        }
197
198        FORMATIN(libtrace)->stats_valid = 0;
199
200        /* TODO: we should always set a bpf filter for snapping */
201
202        /* We're done! */
203        return 0;
204}
205
206static uint64_t bpf_get_received_packets(libtrace_t *trace)
207{
208        if (trace->format_data == NULL)
209                return (uint64_t)-1;
210
211        if (FORMATIN(trace)->fd == -1) {
212                /* Almost certainly a 'dead' trace so there is no socket
213                 * for us to query */
214                return (uint64_t) -1;
215        }
216        /* If we're called with stats_valid == 0, or we're called again
217         * then refresh the stats.  Don't refresh the stats if we're called
218         * immediately after get_dropped_packets
219         */
220        if ((FORMATIN(trace)->stats_valid & 1)
221                || (FORMATIN(trace)->stats_valid == 0)) {
222                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
223                FORMATIN(trace)->stats_valid |= 1;
224        }
225
226        return FORMATIN(trace)->stats.bs_recv;
227}
228
229static uint64_t bpf_get_dropped_packets(libtrace_t *trace)
230{
231        if (trace->format_data == NULL)
232                return (uint64_t)-1;
233
234        if (FORMATIN(trace)->fd == -1) {
235                /* Almost certainly a 'dead' trace so there is no socket
236                 * for us to query */
237                return (uint64_t) -1;
238        }
239        /* If we're called with stats_valid == 0, or we're called again
240         * then refresh the stats.  Don't refresh the stats if we're called
241         * immediately after get_received_packets
242         */
243        if ((FORMATIN(trace)->stats_valid & 2) 
244                || (FORMATIN(trace)->stats_valid == 0)) {
245                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
246                FORMATIN(trace)->stats_valid |= 2;
247        }
248
249        return FORMATIN(trace)->stats.bs_drop;
250}
251
252static int bpf_pause_input(libtrace_t *libtrace)
253{
254        close(FORMATIN(libtrace)->fd);
255        FORMATIN(libtrace)->fd=-1;
256
257        return 0;
258}
259
260static int bpf_fin_input(libtrace_t *libtrace) 
261{
262        free(libtrace->format_data);
263        return 0;
264}
265
266static int bpf_config_input(libtrace_t *libtrace,
267                trace_option_t option,
268                void *data)
269{
270        switch(option) {
271                case TRACE_OPTION_SNAPLEN:
272                        FORMATIN(libtrace)->snaplen=*(int*)data;
273                        return 0;
274                case TRACE_OPTION_PROMISC:
275                        FORMATIN(libtrace)->promisc=*(int*)data;
276                        return 0;
277                case TRACE_OPTION_FILTER:
278                        /* We don't support bpf filters in any special way
279                         * so return an error and let libtrace deal with
280                         * emulating it
281                         */
282                        break;
283                case TRACE_OPTION_META_FREQ:
284                        /* No meta-data for this format */
285                        break;
286                case TRACE_OPTION_EVENT_REALTIME:
287                        /* captures are always realtime */
288                        break;
289
290                /* Avoid default: so that future options will cause a warning
291                 * here to remind us to implement it, or flag it as
292                 * unimplementable
293                 */
294        }
295        trace_set_err(libtrace,TRACE_ERR_UNKNOWN_OPTION,
296                        "Unknown option %i", option);
297        return -1;
298}
299
300static int bpf_prepare_packet(libtrace_t *libtrace, libtrace_packet_t *packet,
301                void *buffer, libtrace_rt_types_t rt_type, uint32_t flags) {
302        if (packet->buffer != buffer &&
303                        packet->buf_control == TRACE_CTRL_PACKET) {
304                free(packet->buffer);
305        }
306
307        if ((flags & TRACE_PREP_OWN_BUFFER) == TRACE_PREP_OWN_BUFFER) {
308                packet->buf_control = TRACE_CTRL_PACKET;
309        } else
310                packet->buf_control = TRACE_CTRL_EXTERNAL;
311
312
313        packet->buffer = buffer;
314        packet->header = buffer;
315        packet->type = rt_type;
316
317        /* Find the payload */
318        /* TODO: Pcap deals with a padded FDDI linktype here */
319        packet->payload=(char *)buffer + BPFHDR(packet)->bh_hdrlen;
320
321        if (libtrace->format_data == NULL) {
322                if (bpf_init_input(libtrace))
323                        return -1;
324        }
325
326        return 0;
327}
328       
329static int bpf_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) 
330{
331        uint32_t flags = 0;
332       
333        /* Fill the buffer */
334        if (FORMATIN(libtrace)->remaining<=0) {
335                int ret;
336
337                ret=read(FORMATIN(libtrace)->fd,
338                        FORMATIN(libtrace)->buffer,
339                        FORMATIN(libtrace)->buffersize);
340
341                if (ret == -1) {
342                        trace_set_err(libtrace,errno,"Failed to read");
343                        return -1;
344                }
345
346                if (ret == 0) {
347                        /* EOF */
348                        return 0;
349                }
350
351                FORMATIN(libtrace)->remaining=ret;
352                FORMATIN(libtrace)->bufptr=
353                                FORMATIN(libtrace)->buffer;
354        }
355        flags |= TRACE_PREP_DO_NOT_OWN_BUFFER;
356        /* Read one packet out */
357       
358        if (packet->buf_control == TRACE_CTRL_PACKET)
359                free(packet->buffer);
360
361        if (bpf_prepare_packet(libtrace, packet, FORMATIN(libtrace)->bufptr,
362                TRACE_RT_DATA_BPF, flags)) {
363                return -1;
364        }
365       
366
367        /* Now deal with any padding */
368        FORMATIN(libtrace)->bufptr+=
369                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
370                +BPFHDR(packet)->bh_caplen);
371        FORMATIN(libtrace)->remaining-=
372                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
373                +BPFHDR(packet)->bh_caplen);
374
375        return BPFHDR(packet)->bh_datalen+BPFHDR(packet)->bh_hdrlen;
376}
377
378static libtrace_linktype_t bpf_get_link_type(const libtrace_packet_t *packet) {
379        return pcap_linktype_to_libtrace(FORMATIN(packet->trace)->linktype);
380}
381
382static libtrace_direction_t bpf_get_direction(const libtrace_packet_t *packet) {
383        /* BPF Sadly can't do direction tagging */
384        return ~0;
385}
386
387static struct timeval bpf_get_timeval(const libtrace_packet_t *packet) 
388{
389        struct timeval tv;
390        /* OpenBSD uses a bpf_timeval rather than a timeval so we must copy
391         * each timeval element individually rather than doing a structure
392         * assignment */
393        tv.tv_sec = BPFHDR(packet)->bh_tstamp.tv_sec;
394        tv.tv_usec = BPFHDR(packet)->bh_tstamp.tv_usec;
395
396        return tv;
397}
398
399static int bpf_get_capture_length(const libtrace_packet_t *packet)
400{
401        /* BPF Doesn't include the FCS, we do. */
402        return BPFHDR(packet)->bh_caplen+4;
403}
404
405static int bpf_get_wire_length(const libtrace_packet_t *packet) 
406{
407        return BPFHDR(packet)->bh_datalen+4;
408}
409
410static int bpf_get_framing_length(UNUSED
411                const libtrace_packet_t *packet) 
412{
413        return BPFHDR(packet)->bh_hdrlen;
414}
415
416static int bpf_get_fd(const libtrace_t *trace) {
417        return FORMATIN(trace)->fd;
418}
419
420static void bpf_help() {
421        printf("bpf format module: $Revision$\n");
422        printf("Supported input URIs:\n");
423        printf("\tbpf:\n");
424        printf("\n");
425        return;
426}
427static struct libtrace_format_t bpf = {
428        "bpf",
429        "$Id$",
430        TRACE_FORMAT_BPF,
431        bpf_init_input,         /* init_input */
432        bpf_config_input,       /* config_input */
433        bpf_start_input,        /* start_input */
434        bpf_pause_input,        /* pause_input */
435        NULL,                   /* init_output */
436        NULL,                   /* config_output */
437        NULL,                   /* start_ouput */
438        bpf_fin_input,          /* fin_input */
439        NULL,                   /* fin_output */
440        bpf_read_packet,        /* read_packet */
441        bpf_prepare_packet,     /* prepare_packet */
442        NULL,                   /* fin_packet */
443        NULL,                   /* write_packet */
444        bpf_get_link_type,      /* get_link_type */
445        bpf_get_direction,      /* get_direction */
446        NULL,                   /* set_direction */
447        NULL,                   /* get_erf_timestamp */
448        bpf_get_timeval,        /* get_timeval */
449        NULL,                   /* get_seconds */
450        NULL,                   /* seek_erf */
451        NULL,                   /* seek_timeval */
452        NULL,                   /* seek_seconds */
453        bpf_get_capture_length, /* get_capture_length */
454        bpf_get_wire_length,    /* get_wire_length */
455        bpf_get_framing_length, /* get_framing_length */
456        NULL,                   /* set_capture_length */
457        bpf_get_received_packets,/* get_received_packets */
458        NULL,                   /* get_filtered_packets */
459        bpf_get_dropped_packets,/* get_dropped_packets */
460        NULL,                   /* get_captured_packets */
461        bpf_get_fd,             /* get_fd */
462        trace_event_device,     /* trace_event */
463        bpf_help,               /* help */
464        NULL
465};
466
467void bpf_constructor() {
468        register_format(&bpf);
469}
Note: See TracBrowser for help on using the repository browser.