source: lib/format_dag24.c @ 2725318

develop
Last change on this file since 2725318 was 2725318, checked in by Jacob Van Walraven <jcv9@…>, 2 years ago

Cleanup some of the assertions

  • Property mode set to 100644
File size: 18.6 KB
Line 
1/*
2 *
3 * Copyright (c) 2007-2016 The University of Waikato, Hamilton, New Zealand.
4 * All rights reserved.
5 *
6 * This file is part of libtrace.
7 *
8 * This code has been developed by the University of Waikato WAND
9 * research group. For further information please see http://www.wand.net.nz/
10 *
11 * libtrace is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * libtrace is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 * GNU Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 *
24 *
25 */
26#define _GNU_SOURCE
27
28#include "config.h"
29#include "common.h"
30#include "libtrace.h"
31#include "libtrace_int.h"
32#include "format_helper.h"
33#include "format_erf.h"
34
35#include <assert.h>
36#include <errno.h>
37#include <fcntl.h>
38#include <stdio.h>
39#include <string.h>
40#include <stdlib.h>
41#include <sys/stat.h>
42
43#include <sys/mman.h>
44#ifdef WIN32
45#  include <io.h>
46#  include <share.h>
47#  define PATH_MAX _MAX_PATH
48#  define snprintf sprintf_s
49#else
50#  include <netdb.h>
51#  ifndef PATH_MAX
52#       define PATH_MAX 4096
53#  endif
54#  include <sys/ioctl.h>
55#endif
56
57/* This format deals with DAG cards that are using drivers from the 2.4.X
58 * versions.
59 *
60 * DAG is a LIVE capture format.
61 *
62 * We do not support writing using this format, as transmit support was not
63 * added until a subsequent version of the DAG software (see format_dag25.c).
64 * Instead, you should write the packets read using this format as ERF traces.
65 */
66
67static struct libtrace_format_t dag;
68
69#define DATA(x) ((struct dag_format_data_t *)x->format_data)
70#define DUCK DATA(libtrace)->duck
71#define FORMAT_DATA DATA(libtrace)
72
73/* "Global" data that is stored for each DAG input trace */
74struct dag_format_data_t {
75
76        /* Data required for regular DUCK reporting */
77        struct {
78                /* Timestamp of the last DUCK report */
79                uint32_t last_duck;
80                /* The number of seconds between each DUCK report */
81                uint32_t duck_freq;
82                /* Timestamp of the last packet read from the DAG card */
83                uint32_t last_pkt;
84                /* Dummy trace to ensure DUCK packets are dealt with using
85                 * the DUCK format functions */
86                libtrace_t *dummy_duck;
87        } duck; 
88       
89        /* File descriptor for the DAG card */
90        int fd;
91        /* Pointer to DAG memory hole */
92        void *buf;
93        /* Difference between the top and bottom pointers in the DAG memory
94         * hole, i.e. the amount of available data to read */
95        uint32_t diff;
96        /* The amount of data read thus far from the start of the bottom
97         * pointer */
98        uint32_t offset;
99        /* The offset for the first unread byte in the DAG memory hole */
100        uint32_t bottom;
101        /* The offset for the last unread byte in the DAG memory hole */
102        uint32_t top;
103        /* The number of packets that have been dropped */
104        uint64_t drops;
105};
106
107/* Determines if a given filename refers to a DAG device */
108static void dag_probe_filename(const char *filename) 
109{
110        struct stat statbuf;
111        /* Can we stat the file? */
112        if (stat(filename, &statbuf) != 0) {
113                return 0;
114        }
115        /* Is it a character device? */
116        if (!S_ISCHR(statbuf.st_mode)) {
117                return 0;
118        }
119        /* Yeah, it's probably us. */
120        return 1;
121}
122
123/* Initialises the DAG "global" variables */
124static void dag_init_format_data(libtrace_t *libtrace) {
125        libtrace->format_data = (struct dag_format_data_t *)
126                malloc(sizeof(struct dag_format_data_t));
127
128        DUCK.last_duck = 0;
129        DUCK.duck_freq = 0;
130        DUCK.last_pkt = 0;
131        DUCK.dummy_duck = NULL;
132        FORMAT_DATA->drops = 0;
133        FORMAT_DATA->top = 0;
134        FORMAT_DATA->bottom = 0;
135        FORMAT_DATA->buf = NULL;
136        FORMAT_DATA->fd = -1;
137        FORMAT_DATA->offset = 0;
138        FORMAT_DATA->diff = 0;
139}
140
141/* Determines how much data is available for reading on the DAG card and
142 * updates the various offsets accordingly */
143static int dag_available(libtrace_t *libtrace) {
144
145        if (FORMAT_DATA->diff > 0)
146                return FORMAT_DATA->diff;
147
148        FORMAT_DATA->bottom = FORMAT_DATA->top;
149        FORMAT_DATA->top = dag_offset(
150                        FORMAT_DATA->fd,
151                        &(FORMAT_DATA->bottom),
152                        DAGF_NONBLOCK);
153        FORMAT_DATA->diff = FORMAT_DATA->top - FORMAT_DATA->bottom;
154        FORMAT_DATA->offset = 0;
155        return FORMAT_DATA->diff;
156}
157
158/* Initialises a DAG input trace */
159static int dag_init_input(libtrace_t *libtrace) {
160        struct stat buf;
161        char *dag_dev_name = NULL;
162        char *scan = NULL;
163
164        /* Since DAG 2.5 has been changed to support a slightly different URI
165         * format, it's probably a good idea to deal with URIs specified in
166         * such a fashion even if we just end up ignoring the stream number */
167        if ((scan = strchr(libtrace->uridata,',')) == NULL) {
168                dag_dev_name = strdup(libtrace->uridata);
169        } else {
170                dag_dev_name = (char *)strndup(libtrace->uridata,
171                                (size_t)(scan - libtrace->uridata));
172        }
173
174
175        /* Make sure a DAG device with the right name exists */ 
176        if (stat(dag_dev_name, &buf) == -1) {
177                trace_set_err(libtrace,errno,"stat(%s)",dag_dev_name);
178                free(dag_dev_name);
179                return -1;
180        }
181
182        dag_init_format_data(libtrace);
183        if (S_ISCHR(buf.st_mode)) {
184                /* DEVICE */
185                if((FORMAT_DATA->fd = dag_open(dag_dev_name)) < 0) {
186                        trace_set_err(libtrace,errno,"Cannot open DAG %s",
187                                        dag_dev_name);
188                        free(dag_dev_name);
189                        return -1;
190                }
191
192                /* Memory-map ourselves a pointer to the DAG memory hole */
193                if((FORMAT_DATA->buf = (void *)dag_mmap(FORMAT_DATA->fd)) == MAP_FAILED) {
194                        trace_set_err(libtrace,errno,"Cannot mmap DAG %s",
195                                        dag_dev_name);
196                        free(dag_dev_name);
197                        return -1;
198                }
199        } else {
200                trace_set_err(libtrace,errno,"Not a valid dag device: %s",
201                                dag_dev_name);
202                free(dag_dev_name);
203                return -1;
204        }
205
206        free(dag_dev_name);
207
208        return 0;
209}
210
211/* Configures a DAG input trace */
212static int dag_config_input(libtrace_t *libtrace, trace_option_t option,
213                                void *data) {
214        switch(option) {
215                case TRACE_OPTION_META_FREQ:
216                        /* We use this option to specify the frequency of
217                         * DUCK updates */
218                        DUCK.duck_freq = *(int *)data;
219                        return 0;
220                case TRACE_OPTION_SNAPLEN:
221                        /* Surely we can set this?? Fall through for now*/
222                        return -1;
223                case TRACE_OPTION_PROMISC:
224                        /* DAG already operates in a promisc fashion */
225                        return -1;
226                case TRACE_OPTION_FILTER:
227                        /* Cards that use the older drivers don't do
228                         * filtering */
229                        return -1;
230                case TRACE_OPTION_EVENT_REALTIME:
231                        /* Live capture is always going to be realtime */
232                        return -1;
233        }
234        return -1;
235}
236
237/* Starts a DAG input trace */
238static int dag_start_input(libtrace_t *libtrace) {     
239        if(dag_start(FORMAT_DATA->fd) < 0) {
240                trace_set_err(libtrace,errno,"Cannot start DAG %s",
241                                libtrace->uridata);
242                return -1;
243        }
244
245        /* Flush the memory hole */
246        while(dag_available(libtrace) != 0)
247                FORMAT_DATA->diff = 0;
248        FORMAT_DATA->drops = 0;
249        return 0;
250}
251
252/* Pauses a DAG input trace */
253static int dag_pause_input(libtrace_t *libtrace) {
254        dag_stop(FORMAT_DATA->fd);
255        return 0;
256}
257
258/* Destroys a DAG input trace */
259static int dag_fin_input(libtrace_t *libtrace) {
260        dag_close(FORMAT_DATA->fd);
261        if (DUCK.dummy_duck)
262                trace_destroy_dead(DUCK.dummy_duck);
263        free(libtrace->format_data);
264        return 0; /* success */
265}
266
267/* Extracts DUCK information from the DAG card and produces a DUCK packet */
268static int dag_get_duckinfo(libtrace_t *libtrace,
269                                libtrace_packet_t *packet) {
270        dag_inf lt_dag_inf;
271
272        /* Allocate memory for the DUCK data */
273        if (packet->buf_control == TRACE_CTRL_EXTERNAL ||
274                        !packet->buffer) {
275                packet->buffer = malloc(LIBTRACE_PACKET_BUFSIZE);
276                packet->buf_control = TRACE_CTRL_PACKET;
277                if (!packet->buffer) {
278                        trace_set_err(libtrace, errno,
279                                        "Cannot allocate packet buffer");
280                        return -1;
281                }
282        }
283
284        /* DUCK doesn't actually have a format header, as such */
285        packet->header = 0;
286        packet->payload = packet->buffer;
287
288        /* Check that the DAG card supports DUCK */
289        if ((ioctl(FORMAT_DATA->fd, DAG_IOINF, &lt_dag_inf) < 0)) {
290                trace_set_err(libtrace, errno,
291                                "Error using DAG_IOINF");
292                return -1;
293        }
294        if (!IsDUCK(&lt_dag_inf)) {
295                printf("WARNING: %s does not have modern clock support - No DUCK information will be gathered\n", libtrace->uridata);
296                return 0;
297        }
298
299        /* Get the DUCK information from the card */
300        if ((ioctl(FORMAT_DATA->fd, DAG_IOGETDUCK, (duck_inf *)packet->payload)
301                                < 0)) {
302                trace_set_err(libtrace, errno, "Error using DAG_IOGETDUCK");
303                return -1;
304        }
305
306        /* Set the type */
307        packet->type = TRACE_RT_DUCK_2_4;
308
309        /* Set the packet's trace to point at a DUCK trace, so that the
310         * DUCK format functions will be called on the packet rather than the
311         * DAG ones */
312        if (!DUCK.dummy_duck)
313                DUCK.dummy_duck = trace_create_dead("duck:dummy");
314        packet->trace = DUCK.dummy_duck;
315        return sizeof(duck_inf);
316}
317
318/* Reads the next ERF record from the DAG memory hole */
319static dag_record_t *dag_get_record(libtrace_t *libtrace) {
320        dag_record_t *erfptr = NULL;
321        uint16_t size;
322        erfptr = (dag_record_t *) ((char *)FORMAT_DATA->buf + 
323                        (FORMAT_DATA->bottom + FORMAT_DATA->offset));
324
325        if (!erfptr)
326                return NULL;
327        size = ntohs(erfptr->rlen);
328        assert( size >= dag_record_size );
329        FORMAT_DATA->offset += size;
330        FORMAT_DATA->diff -= size;
331        return erfptr;
332}
333
334/* Converts a buffer containing a recently read DAG packet record into a
335 * libtrace packet */
336static int dag_prepare_packet(libtrace_t *libtrace, libtrace_packet_t *packet,
337                void *buffer, libtrace_rt_types_t rt_type, uint32_t flags) {
338
339        dag_record_t *erfptr;
340        /* If the packet previously owned a buffer that is not the buffer
341         * that contains the new packet data, we're going to need to free the
342         * old one to avoid memory leaks */
343        if (packet->buffer != buffer &&
344                        packet->buf_control == TRACE_CTRL_PACKET) {
345                free(packet->buffer);
346        }
347
348        /* Set the buffer owner appropriately */
349        if ((flags & TRACE_PREP_OWN_BUFFER) == TRACE_PREP_OWN_BUFFER) {
350                packet->buf_control = TRACE_CTRL_PACKET;
351        } else 
352                packet->buf_control = TRACE_CTRL_EXTERNAL;
353       
354        /* Update packet pointers and type appropriately */
355        erfptr = (dag_record_t *)buffer;
356        packet->buffer = erfptr;
357        packet->header = erfptr;
358        packet->type = rt_type;
359
360        if (erfptr->flags.rxerror == 1) {
361                /* rxerror means the payload is corrupt - drop the payload
362                 * by tweaking rlen */
363                packet->payload = NULL;
364                erfptr->rlen = htons(erf_get_framing_length(packet));
365        } else {
366                packet->payload = (char*)packet->buffer
367                        + erf_get_framing_length(packet);
368        }
369
370        if (libtrace->format_data == NULL) {
371                dag_init_format_data(libtrace);
372        }
373
374        /* Update the dropped packets counter, using the value of the ERF
375         * loss counter */
376        DATA(libtrace)->drops += ntohs(erfptr->lctr);
377
378        return 0;
379
380}
381
382/* Reads the next available packet from a DAG card, in a BLOCKING fashion
383 *
384 * If DUCK reporting is enabled, the packet returned may be a DUCK update */
385static int dag_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) {
386        int numbytes;
387        int size = 0;
388        uint32_t flags = 0;
389        struct timeval tv;
390        dag_record_t *erfptr = NULL;
391
392        /* Check if we're due for a DUCK report */
393        if (DUCK.last_pkt - DUCK.last_duck > DUCK.duck_freq &&
394                        DUCK.duck_freq != 0) {
395                size = dag_get_duckinfo(libtrace, packet);
396                DUCK.last_duck = DUCK.last_pkt;
397                if (size != 0) {
398                        return size;
399                }
400                /* No DUCK support, so don't waste our time anymore */
401                DUCK.duck_freq = 0;
402        }
403
404        /* Don't let anyone try to free our DAG memory hole */
405        flags |= TRACE_PREP_DO_NOT_OWN_BUFFER;
406       
407        /* If the packet buffer is currently owned by libtrace, free it so
408         * that we can set the packet to point into the DAG memory hole */
409        if (packet->buf_control == TRACE_CTRL_PACKET) {
410                packet->buf_control = TRACE_CTRL_EXTERNAL;
411                free(packet->buffer);
412                packet->buffer = 0;
413        }
414
415        /* Grab a full ERF record */
416        do {
417                numbytes = dag_available(libtrace);
418                if (numbytes < 0)
419                        return numbytes;
420                if (numbytes == 0)
421                        continue;
422                erfptr = dag_get_record(libtrace);
423        } while (erfptr == NULL);
424       
425        /* Prepare the libtrace packet */
426        if (dag_prepare_packet(libtrace, packet, erfptr, TRACE_RT_DATA_ERF, 
427                                flags))
428                return -1;
429       
430        /* Update the DUCK timer */
431        tv = trace_get_timeval(packet);
432        DUCK.last_pkt = tv.tv_sec;
433        return packet->payload ? htons(erfptr->rlen) : erf_get_framing_length(packet);
434}
435
436/* Attempts to read a packet from a DAG card in a NON-BLOCKING fashion. If
437 * a packet is available, we will return a packet event. Otherwise we will
438 * return a SLEEP event (as we cannot select on the DAG file descriptor).
439 */
440static libtrace_eventobj_t trace_event_dag(libtrace_t *trace,
441                                        libtrace_packet_t *packet) {
442        libtrace_eventobj_t event = {0,0,0.0,0};
443        int data;
444
445        do {
446                data = dag_available(trace);
447
448                /* If no data is available, drop out and return a sleep event */
449                if (data <= 0)
450                        break;
451
452                /* Data is available, so we can call the blocking read because
453                 * we know that we will get a packet straight away */
454                event.size = dag_read_packet(trace,packet);
455                //DATA(trace)->dag.diff -= event.size;
456               
457                /* XXX trace_read_packet() normally applies the following
458                 * config options for us, but this function is called via
459                 * trace_event() so we have to do it ourselves */
460
461                /* Check that the packet matches any pre-existing filter */
462                if (trace->filter) {
463                        if (trace_apply_filter(trace->filter, packet)) {
464                                event.type = TRACE_EVENT_PACKET;
465                        } else {
466                                /* Do not sleep - try to read another packet */
467                                trace->filtered_packets ++;
468                                continue;
469                        }
470                } else {
471                        event.type = TRACE_EVENT_PACKET;
472                }
473
474                /* If the user has specified a snap length, apply that too */
475                if (trace->snaplen > 0) {
476                        trace_set_capture_length(packet, trace->snaplen);
477                }
478                trace->accepted_packets ++;
479                return event;
480        } while (1);
481
482
483        /* We only want to sleep for a very short time */
484        assert(data == 0);
485        event.type = TRACE_EVENT_SLEEP;
486        event.seconds = 0.0001;
487        event.size = 0;
488        return event;
489}
490
491/* Gets the number of dropped packets */
492static uint64_t dag_get_dropped_packets(libtrace_t *trace)
493{
494        if (!trace->format_data)
495                return (uint64_t)-1;
496        return DATA(trace)->drops;
497}
498
499/* Prints some semi-useful help text about the DAG format module */
500static void dag_help(void) {
501        printf("dag format module: $Revision: 1715 $\n");
502        printf("Supported input URIs:\n");
503        printf("\tdag:/dev/dagn\n");
504        printf("\n");
505        printf("\te.g.: dag:/dev/dag0\n");
506        printf("\n");
507        printf("Supported output URIs:\n");
508        printf("\tnone\n");
509        printf("\n");
510}
511
512static struct libtrace_format_t dag = {
513        "dag",
514        "$Id$",
515        TRACE_FORMAT_ERF,
516        dag_probe_filename,             /* probe filename */
517        NULL,                           /* probe magic */
518        dag_init_input,                 /* init_input */
519        dag_config_input,               /* config_input */
520        dag_start_input,                /* start_input */
521        dag_pause_input,                /* pause_input */
522        NULL,                           /* init_output */
523        NULL,                           /* config_output */
524        NULL,                           /* start_output */
525        dag_fin_input,                  /* fin_input */
526        NULL,                           /* fin_output */
527        dag_read_packet,                /* read_packet */
528        dag_prepare_packet,             /* prepare_packet */
529        NULL,                           /* fin_packet */
530        NULL,                           /* write_packet */
531        NULL,                           /* flush_output */
532        erf_get_link_type,              /* get_link_type */
533        erf_get_direction,              /* get_direction */
534        erf_set_direction,              /* set_direction */
535        erf_get_erf_timestamp,          /* get_erf_timestamp */
536        NULL,                           /* get_timeval */
537        NULL,                           /* get_timespec */
538        NULL,                           /* get_seconds */
539        NULL,                           /* seek_erf */
540        NULL,                           /* seek_timeval */
541        NULL,                           /* seek_seconds */
542        erf_get_capture_length,         /* get_capture_length */
543        erf_get_wire_length,            /* get_wire_length */
544        erf_get_framing_length,         /* get_framing_length */
545        erf_set_capture_length,         /* set_capture_length */
546        NULL,                           /* get_received_packets */
547        NULL,                           /* get_filtered_packets */
548        dag_get_dropped_packets,        /* get_dropped_packets */
549        NULL,                           /* get_statistics */
550        NULL,                           /* get_fd */
551        trace_event_dag,                /* trace_event */
552        dag_help,                       /* help */
553        NULL,                            /* next pointer */
554    NON_PARALLEL(true)
555};
556
557void dag_constructor(void) {
558        register_format(&dag);
559}
Note: See TracBrowser for help on using the repository browser.