source: lib/format_bpf.c @ 5952ff0

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 5952ff0 was 5952ff0, checked in by Shane Alcock <salcock@…>, 11 years ago
  • Updated internal documentation and licensing for several format modules
  • Property mode set to 100644
File size: 16.0 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
58/* This format deals with the BSD Native capture format, perhaps better
59 * known as BPF, which is the equivalent of the Linux Native format for
60 * *BSD systems.
61 *
62 * This is a LIVE capture format - we're always dealing with reading from an
63 * interface.
64 *
65 * This format does not support writing, but BPF packet records can be easily
66 * converted to PCAP or ERF.
67 */ 
68
69/* "Global" data that is stored for each BPF input trace */
70struct libtrace_format_data_t {
71        /* The file descriptor that is being captured from */
72        int fd; 
73        /* The snap length for the capture */
74        int snaplen;
75        /* A boolean flag indicating whether the capture interface should be
76         * in promiscuous mode */ 
77        int promisc;
78        /* A buffer to write captured data into */
79        void *buffer;
80        /* The current read location in the capture buffer */
81        void *bufptr;
82        /* The total size of the capture buffer */
83        unsigned int buffersize;
84        /* The amount of space remaining before the capture buffer is full */
85        int remaining;
86        /* The linktype of the capture interface */ 
87        unsigned int linktype;
88        /* Statistics about how many packets have been dropped, received etc. */
89        struct bpf_stat stats;
90        /* A boolean flag indicating whether the statistics are up-to-date */
91        int stats_valid;
92};
93
94#define FORMATIN(x) ((struct libtrace_format_data_t*)((x->format_data)))
95
96#define BPFHDR(x) ((struct bpf_hdr *)((x)->header))
97
98/* Attempts to determine if a given filename could refer to a BPF interface */
99static int bpf_probe_filename(const char *filename)
100{
101        return (if_nametoindex(filename) != 0);
102}
103
104/* XXX This function doesn't appear to be used anywhere - nor does it do
105 * anything particularly useful :) */
106static int bpf_start_filename(const char *filename)
107{
108        return 0;
109}
110
111/* Initialises a BPF input trace */
112static int bpf_init_input(libtrace_t *libtrace) 
113{
114        libtrace->format_data = (struct libtrace_format_data_t *)
115                malloc(sizeof(struct libtrace_format_data_t));
116       
117        /* Throw some default values into the format data */
118        FORMATIN(libtrace)->fd = -1;
119        FORMATIN(libtrace)->promisc = 0;
120        FORMATIN(libtrace)->snaplen = 65536;
121        FORMATIN(libtrace)->stats_valid = 0;
122
123        return 0;
124}
125
126/* Starts a BPF input trace */
127static int bpf_start_input(libtrace_t *libtrace)
128{
129        int bpfid=0;
130        struct bpf_version bv;
131        struct ifreq ifr;
132        unsigned int v;
133
134        /* Find and open a bpf device */
135        do {
136                char buffer[64];
137                snprintf(buffer,sizeof(buffer),"/dev/bpf%d", bpfid);
138                bpfid++;
139               
140                FORMATIN(libtrace)->fd = open(buffer, O_RDONLY);
141        } while(FORMATIN(libtrace)->fd == -1 && errno == EBUSY);
142
143        if (FORMATIN(libtrace)->fd == -1) {
144                trace_set_err(libtrace,TRACE_ERR_INIT_FAILED,
145                                "No free bpf devices");
146                return -1;
147        }
148
149        /* Check the BPF Version is ok */
150        if (ioctl(FORMATIN(libtrace)->fd, BIOCVERSION, &bv) == -1) {
151                trace_set_err(libtrace,errno,
152                                "Failed to read the bpf version");
153                close(FORMATIN(libtrace)->fd);
154                return -1;
155        }
156
157        if (bv.bv_major != BPF_MAJOR_VERSION) {
158                trace_set_err(libtrace,errno, 
159                        "Unknown kernel BPF version (%d.%d, libtrace requires at least %d.%d)",
160                        bv.bv_major,
161                        bv.bv_minor,
162                        BPF_MAJOR_VERSION,
163                        BPF_MINOR_VERSION);
164                close(FORMATIN(libtrace)->fd);
165                return -1;
166        }
167
168        if (bv.bv_minor < BPF_MINOR_VERSION) {
169                trace_set_err(libtrace,errno, "Kernel version too old (%d.%d, libtrace requires at least %d.%d)",
170                        bv.bv_major,
171                        bv.bv_minor,
172                        BPF_MAJOR_VERSION,
173                        BPF_MINOR_VERSION);
174                close(FORMATIN(libtrace)->fd);
175                return -1;
176        }
177
178        /* We assume the default kernel buffer size is sufficient. */
179        if (ioctl(FORMATIN(libtrace)->fd, BIOCGBLEN,
180                        &FORMATIN(libtrace)->buffersize)==-1) {
181                trace_set_err(libtrace,errno,"Failed to find buffer length");
182                close(FORMATIN(libtrace)->fd);
183                return -1;
184        }
185
186        FORMATIN(libtrace)->buffer = malloc(FORMATIN(libtrace)->buffersize);
187        FORMATIN(libtrace)->bufptr = FORMATIN(libtrace)->buffer;
188        FORMATIN(libtrace)->remaining = 0;
189
190        /* Attach to the device */
191        strncpy(ifr.ifr_name, libtrace->uridata, sizeof(ifr.ifr_name));
192        if (ioctl(FORMATIN(libtrace)->fd, BIOCSETIF, &ifr) == -1) {
193                trace_set_err(libtrace,errno,"Failed to attach");
194                close(FORMATIN(libtrace)->fd);
195                return -1;
196        }
197
198        /* Set the link type */
199        if (ioctl(FORMATIN(libtrace)->fd, BIOCGDLT,
200                         &FORMATIN(libtrace)->linktype) == -1) {
201                trace_set_err(libtrace,errno,"Failed to retrieve link type");
202                close(FORMATIN(libtrace)->fd);
203                return -1;
204        }
205       
206        /* TODO: If BIOCGDLTLIST exists then we should perhaps do something
207         *       with it.  We don't have the same concept of multiple DLT's
208         *       as pcap does.  We grab the rawest possible thing and then
209         *       decode packets by understanding the protocols.  So perhaps
210         *       we should setup a rating of DLT's that we'll prefer in order.
211         *       For example we should try and get 802.11 frames rather than
212         *       802.3 frames.  The general rule should be "whatever actually
213         *       went over the air", although of course if we don't support
214         *       what went over the air we should fall back to something we
215         *       /do/ support.
216         */
217       
218        /* Using timeouts seems sucky.  We'll always use immediate mode.  We
219         * pray the kernel is smart enough that if a another packet arrives
220         * while we're processing this one that it will buffer them into it's
221         * kernel buffer so we can receive packets later. (It'll need to do this
222         * to deal with us spending time processing the last 'n' packets anyway)
223         */
224       
225        v=1;
226        if (ioctl(FORMATIN(libtrace)->fd, BIOCIMMEDIATE, &v) == -1) {
227                trace_set_err(libtrace,errno,"Failed to set immediate mode");
228                close(FORMATIN(libtrace)->fd);
229                return -1;
230        }
231
232        /* Set promiscous mode, if the user has asked us to do so */
233        if (FORMATIN(libtrace)->promisc) {
234                if (ioctl(FORMATIN(libtrace)->fd, BIOCPROMISC, NULL) == -1) {
235                        trace_set_err(libtrace,errno,
236                                "Failed to set promisc mode");
237                        close(FORMATIN(libtrace)->fd);
238                        return -1;
239
240                }
241        }
242
243        FORMATIN(libtrace)->stats_valid = 0;
244
245        /* TODO: we should always set a bpf filter for snapping */
246
247        /* We're done! */
248        return 0;
249}
250
251/* Gets a count of the number of packets received on the BPF interface */
252static uint64_t bpf_get_received_packets(libtrace_t *trace)
253{
254        if (trace->format_data == NULL)
255                return (uint64_t)-1;
256
257        if (FORMATIN(trace)->fd == -1) {
258                /* Almost certainly a 'dead' trace so there is no socket
259                 * for us to query */
260                return (uint64_t) -1;
261        }
262        /* If we're called with stats_valid == 0, or we're called again
263         * then refresh the stats.  Don't refresh the stats if we're called
264         * immediately after get_dropped_packets
265         */
266        if ((FORMATIN(trace)->stats_valid & 1)
267                || (FORMATIN(trace)->stats_valid == 0)) {
268                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
269                FORMATIN(trace)->stats_valid |= 1;
270        }
271
272        return FORMATIN(trace)->stats.bs_recv;
273}
274
275/* Gets a count of the number of packets dropped on the BPF interface */
276static uint64_t bpf_get_dropped_packets(libtrace_t *trace)
277{
278        if (trace->format_data == NULL)
279                return (uint64_t)-1;
280
281        if (FORMATIN(trace)->fd == -1) {
282                /* Almost certainly a 'dead' trace so there is no socket
283                 * for us to query */
284                return (uint64_t) -1;
285        }
286        /* If we're called with stats_valid == 0, or we're called again
287         * then refresh the stats.  Don't refresh the stats if we're called
288         * immediately after get_received_packets
289         */
290        if ((FORMATIN(trace)->stats_valid & 2) 
291                || (FORMATIN(trace)->stats_valid == 0)) {
292                ioctl(FORMATIN(trace)->fd, BIOCGSTATS, &FORMATIN(trace)->stats);
293                FORMATIN(trace)->stats_valid |= 2;
294        }
295
296        return FORMATIN(trace)->stats.bs_drop;
297}
298
299/* Pauses a BPF input trace */
300static int bpf_pause_input(libtrace_t *libtrace)
301{
302        close(FORMATIN(libtrace)->fd);
303        FORMATIN(libtrace)->fd=-1;
304
305        return 0;
306}
307
308/* Closes a BPF input trace */
309static int bpf_fin_input(libtrace_t *libtrace) 
310{
311        free(libtrace->format_data);
312        return 0;
313}
314
315/* Configures a BPF input trace */
316static int bpf_config_input(libtrace_t *libtrace,
317                trace_option_t option,
318                void *data)
319{
320        switch(option) {
321                case TRACE_OPTION_SNAPLEN:
322                        FORMATIN(libtrace)->snaplen=*(int*)data;
323                        return 0;
324                case TRACE_OPTION_PROMISC:
325                        FORMATIN(libtrace)->promisc=*(int*)data;
326                        return 0;
327                case TRACE_OPTION_FILTER:
328                        /* We don't support bpf filters in any special way
329                         * so return an error and let libtrace deal with
330                         * emulating it
331                         */
332                        break;
333                case TRACE_OPTION_META_FREQ:
334                        /* No meta-data for this format */
335                        break;
336                case TRACE_OPTION_EVENT_REALTIME:
337                        /* Captures are always realtime */
338                        break;
339
340                /* Avoid default: so that future options will cause a warning
341                 * here to remind us to implement it, or flag it as
342                 * unimplementable
343                 */
344        }
345        trace_set_err(libtrace,TRACE_ERR_UNKNOWN_OPTION,
346                        "Unknown option %i", option);
347        return -1;
348}
349
350/* Converts a buffer containing a recently read BPF packet record into a
351 * libtrace packet */
352static int bpf_prepare_packet(libtrace_t *libtrace, libtrace_packet_t *packet,
353                void *buffer, libtrace_rt_types_t rt_type, uint32_t flags) {
354       
355        /* If the packet previously owned a buffer that is not the buffer
356         * that contains the new packet data, we're going to need to free the
357         * old one to avoid memory leaks */
358        if (packet->buffer != buffer &&
359                        packet->buf_control == TRACE_CTRL_PACKET) {
360                free(packet->buffer);
361        }
362
363        /* Set the buffer owner appropriately */
364        if ((flags & TRACE_PREP_OWN_BUFFER) == TRACE_PREP_OWN_BUFFER) {
365                packet->buf_control = TRACE_CTRL_PACKET;
366        } else
367                packet->buf_control = TRACE_CTRL_EXTERNAL;
368
369        /* Update the packet pointers and type appropriately */
370        packet->buffer = buffer;
371        packet->header = buffer;
372        packet->type = rt_type;
373
374        /* Find the payload */
375        /* TODO: Pcap deals with a padded FDDI linktype here */
376        packet->payload=(char *)buffer + BPFHDR(packet)->bh_hdrlen;
377
378        if (libtrace->format_data == NULL) {
379                if (bpf_init_input(libtrace))
380                        return -1;
381        }
382
383        return 0;
384}
385
386/* Reads the next packet record from a BPF interface and writes it into a
387 * libtrace packet */   
388static int bpf_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) 
389{
390        uint32_t flags = 0;
391       
392        /* Read from the BPF interface into our capture buffer */
393        if (FORMATIN(libtrace)->remaining<=0) {
394                int ret;
395
396                ret=read(FORMATIN(libtrace)->fd,
397                        FORMATIN(libtrace)->buffer,
398                        FORMATIN(libtrace)->buffersize);
399
400                if (ret == -1) {
401                        trace_set_err(libtrace,errno,"Failed to read");
402                        return -1;
403                }
404
405                if (ret == 0) {
406                        /* EOF */
407                        return 0;
408                }
409
410                FORMATIN(libtrace)->remaining=ret;
411                FORMATIN(libtrace)->bufptr=
412                                FORMATIN(libtrace)->buffer;
413        }
414
415        /* We do NOT want anything trying to free the memory the packet is
416         * stored in */
417        flags |= TRACE_PREP_DO_NOT_OWN_BUFFER;
418
419        if (packet->buf_control == TRACE_CTRL_PACKET)
420                free(packet->buffer);
421
422        /* Update 'packet' to point to the first packet in our capture
423         * buffer */
424        if (bpf_prepare_packet(libtrace, packet, FORMATIN(libtrace)->bufptr,
425                TRACE_RT_DATA_BPF, flags)) {
426                return -1;
427        }
428       
429
430        /* Skip past the packet record we're going to return, making sure
431         * that we deal with padding correctly */
432        FORMATIN(libtrace)->bufptr+=
433                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
434                +BPFHDR(packet)->bh_caplen);
435        FORMATIN(libtrace)->remaining-=
436                BPF_WORDALIGN(BPFHDR(packet)->bh_hdrlen
437                +BPFHDR(packet)->bh_caplen);
438
439        return BPFHDR(packet)->bh_datalen+BPFHDR(packet)->bh_hdrlen;
440}
441
442/* Returns the linktype for the interface that we are capturing from */
443static libtrace_linktype_t bpf_get_link_type(const libtrace_packet_t *packet) {
444        /* Convert the linktype that we recorded when we started the trace
445         * into a suitable libtrace linktype */
446        return pcap_linktype_to_libtrace(FORMATIN(packet->trace)->linktype);
447}
448
449/* Returns the direction for a given BPF packet record */
450static libtrace_direction_t bpf_get_direction(const libtrace_packet_t *packet) {
451        /* BPF sadly can't do direction tagging */
452        return ~0;
453}
454
455/* Returns the timestamp for a given BPF packet record, in the form of a
456 * struct timeval */
457static struct timeval bpf_get_timeval(const libtrace_packet_t *packet) 
458{
459        struct timeval tv;
460        /* OpenBSD uses a bpf_timeval rather than a timeval so we must copy
461         * each timeval element individually rather than doing a structure
462         * assignment */
463        tv.tv_sec = BPFHDR(packet)->bh_tstamp.tv_sec;
464        tv.tv_usec = BPFHDR(packet)->bh_tstamp.tv_usec;
465
466        return tv;
467}
468
469/* Returns the capture length for a given BPF packet record */
470static int bpf_get_capture_length(const libtrace_packet_t *packet)
471{
472        /* BPF doesn't include the FCS in its caplen field, but libtrace
473         * does so we need to add this extra 4 bytes */
474        return BPFHDR(packet)->bh_caplen+4;
475}
476
477/* Returns the wire length for a given BPF packet record */
478static int bpf_get_wire_length(const libtrace_packet_t *packet) 
479{
480
481        /* BPF doesn't include the FCS in its datalen field, but libtrace
482         * does so we need to add this extra 4 bytes */
483        return BPFHDR(packet)->bh_datalen+4;
484}
485
486/* Returns the framing length for a given BPF packet record */
487static int bpf_get_framing_length(UNUSED
488                const libtrace_packet_t *packet) 
489{
490        return BPFHDR(packet)->bh_hdrlen;
491}
492
493/* Returns the file descriptor that the capture interface is operating on */
494static int bpf_get_fd(const libtrace_t *trace) {
495        return FORMATIN(trace)->fd;
496}
497
498/* Prints some slightly useful help text for the BPF capture format */
499static void bpf_help() {
500        printf("bpf format module: $Revision$\n");
501        printf("Supported input URIs:\n");
502        printf("\tbpf:\n");
503        printf("\n");
504        return;
505}
506static struct libtrace_format_t bpf = {
507        "bpf",
508        "$Id$",
509        TRACE_FORMAT_BPF,
510        //bpf_probe_filename,   /* probe filename */
511        NULL,
512        NULL,                   /* probe magic */
513        bpf_init_input,         /* init_input */
514        bpf_config_input,       /* config_input */
515        bpf_start_input,        /* start_input */
516        bpf_pause_input,        /* pause_input */
517        NULL,                   /* init_output */
518        NULL,                   /* config_output */
519        NULL,                   /* start_ouput */
520        bpf_fin_input,          /* fin_input */
521        NULL,                   /* fin_output */
522        bpf_read_packet,        /* read_packet */
523        bpf_prepare_packet,     /* prepare_packet */
524        NULL,                   /* fin_packet */
525        NULL,                   /* write_packet */
526        bpf_get_link_type,      /* get_link_type */
527        bpf_get_direction,      /* get_direction */
528        NULL,                   /* set_direction */
529        NULL,                   /* get_erf_timestamp */
530        bpf_get_timeval,        /* get_timeval */
531        NULL,                   /* get_timespec */
532        NULL,                   /* get_seconds */
533        NULL,                   /* seek_erf */
534        NULL,                   /* seek_timeval */
535        NULL,                   /* seek_seconds */
536        bpf_get_capture_length, /* get_capture_length */
537        bpf_get_wire_length,    /* get_wire_length */
538        bpf_get_framing_length, /* get_framing_length */
539        NULL,                   /* set_capture_length */
540        bpf_get_received_packets,/* get_received_packets */
541        NULL,                   /* get_filtered_packets */
542        bpf_get_dropped_packets,/* get_dropped_packets */
543        NULL,                   /* get_captured_packets */
544        bpf_get_fd,             /* get_fd */
545        trace_event_device,     /* trace_event */
546        bpf_help,               /* help */
547        NULL
548};
549
550void bpf_constructor() {
551        register_format(&bpf);
552}
Note: See TracBrowser for help on using the repository browser.