source: lib/format_bpf.c @ 95fee28

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

Fix issue where if people called one loss counter function,
but then repeditively called another then the cache was never
refreshed.

  • Property mode set to 100644
File size: 11.1 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 we're called with stats_valid == 0, or we're called again
209         * then refresh the stats.  Don't refresh the stats if we're called
210         * immediately after get_dropped_packets
211         */
212        if ((FORMATIN(trace)->stats_valid & 1)
213                || (FORMATIN(trace)->stats_valid == 0)) {
214                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
215                FORMATIN(trace)->stats_valid |= 1;
216        }
217
218        return FORMATIN(trace)->stats.bs_recv;
219}
220
221static uint64_t bpf_get_dropped_packets(libtrace_t *trace)
222{
223        /* If we're called with stats_valid == 0, or we're called again
224         * then refresh the stats.  Don't refresh the stats if we're called
225         * immediately after get_received_packets
226         */
227        if ((FORMATIN(trace)->stats_valid & 2) 
228                || (FORMATIN(trace)->stats_valid == 0)) {
229                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
230                FORMATIN(trace)->stats_valid |= 2;
231        }
232
233        return FORMATIN(trace)->stats.bs_drop;
234}
235
236static int bpf_pause_input(libtrace_t *libtrace)
237{
238        close(FORMATIN(libtrace)->fd);
239        FORMATIN(libtrace)->fd=-1;
240
241        return 0;
242}
243
244static int bpf_fin_input(libtrace_t *libtrace) 
245{
246        free(libtrace->format_data);
247        return 0;
248}
249
250static int bpf_config_input(libtrace_t *libtrace,
251                trace_option_t option,
252                void *data)
253{
254        switch(option) {
255                case TRACE_OPTION_SNAPLEN:
256                        FORMATIN(libtrace)->snaplen=*(int*)data;
257                        return 0;
258                case TRACE_OPTION_PROMISC:
259                        FORMATIN(libtrace)->promisc=*(int*)data;
260                        return 0;
261                case TRACE_OPTION_FILTER:
262                        /* We don't support bpf filters in any special way
263                         * so return an error and let libtrace deal with
264                         * emulating it
265                         */
266                        break;
267                case TRACE_OPTION_META_FREQ:
268                        /* No meta-data for this format */
269                        break;
270                case TRACE_OPTION_EVENT_REALTIME:
271                        /* captures are always realtime */
272                        break;
273
274                /* Avoid default: so that future options will cause a warning
275                 * here to remind us to implement it, or flag it as
276                 * unimplementable
277                 */
278        }
279        trace_set_err(libtrace,TRACE_ERR_UNKNOWN_OPTION,
280                        "Unknown option %i", option);
281        return -1;
282}
283
284static int bpf_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) 
285{
286        /* Fill the buffer */
287        if (FORMATIN(libtrace)->remaining<=0) {
288                int ret;
289
290                ret=read(FORMATIN(libtrace)->fd,
291                        FORMATIN(libtrace)->buffer,
292                        FORMATIN(libtrace)->buffersize);
293
294                if (ret == -1) {
295                        trace_set_err(libtrace,errno,"Failed to read");
296                        return -1;
297                }
298
299                if (ret == 0) {
300                        /* EOF */
301                        return 0;
302                }
303
304                FORMATIN(libtrace)->remaining=ret;
305                FORMATIN(libtrace)->bufptr=
306                                FORMATIN(libtrace)->buffer;
307        }
308
309        /* Read one packet out */
310       
311        if (packet->buf_control == TRACE_CTRL_PACKET)
312                free(packet->buffer);
313        packet->buf_control = TRACE_CTRL_EXTERNAL;
314
315        /* Find the bpf header */
316        packet->header=FORMATIN(libtrace)->bufptr;
317
318        /* Find the payload */
319        /* TODO: Pcap deals with a padded FDDI linktype here */
320        packet->payload=FORMATIN(libtrace)->bufptr+BPFHDR(packet)->bh_hdrlen;
321
322        /* Now deal with any padding */
323        FORMATIN(libtrace)->bufptr+=
324                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
325                +BPFHDR(packet)->bh_caplen);
326        FORMATIN(libtrace)->remaining-=
327                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
328                +BPFHDR(packet)->bh_caplen);
329
330        return BPFHDR(packet)->bh_datalen+BPFHDR(packet)->bh_hdrlen;
331}
332
333static libtrace_linktype_t bpf_get_link_type(const libtrace_packet_t *packet) {
334        return pcap_linktype_to_libtrace(FORMATIN(packet->trace)->linktype);
335}
336
337static libtrace_direction_t bpf_get_direction(const libtrace_packet_t *packet) {
338        /* BPF Sadly can't do direction tagging */
339        return ~0;
340}
341
342static struct timeval bpf_get_timeval(const libtrace_packet_t *packet) 
343{
344        struct timeval tv;
345        tv=BPFHDR(packet)->bh_tstamp;
346        return tv;
347}
348
349static int bpf_get_capture_length(const libtrace_packet_t *packet)
350{
351        /* BPF Doesn't include the FCS, we do. */
352        return BPFHDR(packet)->bh_caplen+4;
353}
354
355static int bpf_get_wire_length(const libtrace_packet_t *packet) 
356{
357        return BPFHDR(packet)->bh_datalen+4;
358}
359
360static int bpf_get_framing_length(UNUSED
361                const libtrace_packet_t *packet) 
362{
363        return BPFHDR(packet)->bh_hdrlen;
364}
365
366static int bpf_get_fd(const libtrace_t *trace) {
367        return FORMATIN(trace)->fd;
368}
369
370static void bpf_help() {
371        printf("bpf format module: $Revision$\n");
372        printf("Supported input URIs:\n");
373        printf("\tbpf:\n");
374        printf("\n");
375        return;
376}
377static struct libtrace_format_t bpf = {
378        "bpf",
379        "$Id$",
380        TRACE_FORMAT_BPF,
381        bpf_init_input,         /* init_input */
382        bpf_config_input,       /* config_input */
383        bpf_start_input,        /* start_input */
384        bpf_pause_input,        /* pause_input */
385        NULL,                   /* init_output */
386        NULL,                   /* config_output */
387        NULL,                   /* start_ouput */
388        bpf_fin_input,          /* fin_input */
389        NULL,                   /* fin_output */
390        bpf_read_packet,        /* read_packet */
391        NULL,                   /* fin_packet */
392        NULL,                   /* write_packet */
393        bpf_get_link_type,      /* get_link_type */
394        bpf_get_direction,      /* get_direction */
395        NULL,                   /* set_direction */
396        NULL,                   /* get_erf_timestamp */
397        bpf_get_timeval,        /* get_timeval */
398        NULL,                   /* get_seconds */
399        NULL,                   /* seek_erf */
400        NULL,                   /* seek_timeval */
401        NULL,                   /* seek_seconds */
402        bpf_get_capture_length, /* get_capture_length */
403        bpf_get_wire_length,    /* get_wire_length */
404        bpf_get_framing_length, /* get_framing_length */
405        NULL,                   /* set_capture_length */
406        bpf_get_received_packets,/* get_received_packets */
407        NULL,                   /* get_filtered_packets */
408        bpf_get_dropped_packets,/* get_dropped_packets */
409        NULL,                   /* get_captured_packets */
410        bpf_get_fd,             /* get_fd */
411        trace_event_device,     /* trace_event */
412        bpf_help,               /* help */
413        NULL
414};
415
416void bpf_constructor() {
417        register_format(&bpf);
418}
Note: See TracBrowser for help on using the repository browser.