source: lib/format_bpf.c @ f00c146

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