source: lib/format_linux_int.c @ 5ab626a

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivelibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 5ab626a was 5ab626a, checked in by Richard Sanger <rsangerarj@…>, 7 years ago

Deprecate trace_get_filtered/accepted/recevied/dropped() in favour of a single function

Adds the single trace_get_statistics function. This allows the structure to be filled
at a point in time, rather than making multiple calls to the library during which state
might have changed.

This has been designed such that the structure can be added to in the future without
breaking old code.

The old internal get_captured_packets was removed from the formats as it was never used.
Eventually we should completely remove get_filtered and received from the formats and replace
them with get_statistics.

In additon some extra fields have added, such as error and captured and the pre-existing
fields are better defined.

The linux formats have been updated to use this new API, which combined with reading
/proc/net/dev returns a full set of statistics.

  • Property mode set to 100644
File size: 16.4 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 *          Richard Sanger
11 *         
12 * All rights reserved.
13 *
14 * This code has been developed by the University of Waikato WAND
15 * research group. For further information please see http://www.wand.net.nz/
16 *
17 * libtrace is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * libtrace is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with libtrace; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
30 *
31 * $Id$
32 *
33 */
34
35/* This format module deals with using the Linux Native capture format.
36 *
37 * Linux Native is a LIVE capture format.
38 *
39 * This format also supports writing which will write packets out to the
40 * network as a form of packet replay. This should not be confused with the
41 * RT protocol which is intended to transfer captured packet records between
42 * RT-speaking programs.
43 */
44
45#include "config.h"
46#include "libtrace.h"
47#include "libtrace_int.h"
48#include "format_helper.h"
49#include "libtrace_arphrd.h"
50#include <stdlib.h>
51#include <errno.h>
52#include <unistd.h>
53#include <string.h>
54#include <assert.h>
55
56#ifdef HAVE_INTTYPES_H
57#  include <inttypes.h>
58#else
59# error "Can't find inttypes.h"
60#endif
61
62#include "format_linux_common.h"
63
64
65#ifdef HAVE_NETPACKET_PACKET_H
66
67
68static int linuxnative_start_input(libtrace_t *libtrace)
69{
70        int ret = linuxcommon_start_input_stream(libtrace, FORMAT_DATA_FIRST);
71        if (ret != 0) {
72                libtrace_list_deinit(FORMAT_DATA->per_stream);
73                free(libtrace->format_data);
74                libtrace->format_data = NULL;
75        }
76        return ret;
77}
78
79static int linuxnative_pstart_input(libtrace_t *libtrace) {
80        return linuxcommon_pstart_input(libtrace, linuxcommon_start_input_stream);
81}
82
83static int linuxnative_start_output(libtrace_out_t *libtrace)
84{
85        FORMAT_DATA_OUT->fd = socket(PF_PACKET, SOCK_RAW, 0);
86        if (FORMAT_DATA_OUT->fd==-1) {
87                free(FORMAT_DATA_OUT);
88                trace_set_err_out(libtrace, errno, "Failed to create raw socket");
89                return -1;
90        }
91
92        return 0;
93}
94
95static int linuxnative_fin_output(libtrace_out_t *libtrace)
96{
97        close(FORMAT_DATA_OUT->fd);
98        FORMAT_DATA_OUT->fd=-1;
99        free(libtrace->format_data);
100        return 0;
101}
102#endif /* HAVE_NETPACKET_PACKET_H */
103
104static int linuxnative_prepare_packet(libtrace_t *libtrace UNUSED, 
105                libtrace_packet_t *packet, void *buffer, 
106                libtrace_rt_types_t rt_type, uint32_t flags) {
107
108        if (packet->buffer != buffer &&
109            packet->buf_control == TRACE_CTRL_PACKET) {
110                free(packet->buffer);
111        }
112
113        if ((flags & TRACE_PREP_OWN_BUFFER) == TRACE_PREP_OWN_BUFFER) {
114                packet->buf_control = TRACE_CTRL_PACKET;
115        } else
116                packet->buf_control = TRACE_CTRL_EXTERNAL;
117
118
119        packet->buffer = buffer;
120        packet->header = buffer;
121        packet->payload = (char *)buffer + 
122                sizeof(struct libtrace_linuxnative_header);
123        packet->type = rt_type;
124
125        /*
126        if (libtrace->format_data == NULL) {
127                if (linuxnative_init_input(libtrace))
128                        return -1;
129        }
130        */
131        return 0;
132       
133}
134
135
136#define LIBTRACE_MIN(a,b) ((a)<(b) ? (a) : (b))
137
138/* 20 isn't enough on x86_64 */
139#define CMSG_BUF_SIZE 128
140
141#ifdef HAVE_NETPACKET_PACKET_H
142inline static int linuxnative_read_stream(libtrace_t *libtrace,
143                                          libtrace_packet_t *packet,
144                                          struct linux_per_stream_t *stream,
145                                          libtrace_message_queue_t *queue)
146{
147        struct libtrace_linuxnative_header *hdr;
148        struct msghdr msghdr;
149        struct iovec iovec;
150        unsigned char controlbuf[CMSG_BUF_SIZE];
151        struct cmsghdr *cmsg;
152        int snaplen;
153
154        uint32_t flags = 0;
155        fd_set readfds;
156        struct timeval tout;
157        int ret;
158       
159        if (!packet->buffer || packet->buf_control == TRACE_CTRL_EXTERNAL) {
160                packet->buffer = malloc((size_t)LIBTRACE_PACKET_BUFSIZE);
161                if (!packet->buffer) {
162                        perror("Cannot allocate buffer");
163                }
164        }
165
166        flags |= TRACE_PREP_OWN_BUFFER;
167       
168        packet->type = TRACE_RT_DATA_LINUX_NATIVE;
169
170        hdr=(struct libtrace_linuxnative_header*)packet->buffer;
171        snaplen=LIBTRACE_MIN(
172                        (int)LIBTRACE_PACKET_BUFSIZE-(int)sizeof(*hdr),
173                        (int)FORMAT_DATA->snaplen);
174        /* Prepare the msghdr and iovec for the kernel to write the
175         * captured packet into. The msghdr will point to the part of our
176         * buffer reserved for sll header, while the iovec will point at
177         * the buffer following the sll header. */
178
179        msghdr.msg_name = &hdr->hdr;
180        msghdr.msg_namelen = sizeof(struct sockaddr_ll);
181
182        msghdr.msg_iov = &iovec;
183        msghdr.msg_iovlen = 1;
184
185        msghdr.msg_control = &controlbuf;
186        msghdr.msg_controllen = CMSG_BUF_SIZE;
187        msghdr.msg_flags = 0;
188
189        iovec.iov_base = (void*)(packet->buffer+sizeof(*hdr));
190        iovec.iov_len = snaplen;
191
192        // Check for a packet - TODO only Linux has MSG_DONTWAIT should use fctl O_NONBLOCK
193        /* Try check ahead this should be fast if something is waiting  */
194        hdr->wirelen = recvmsg(stream->fd, &msghdr, MSG_DONTWAIT | MSG_TRUNC);
195
196        /* No data was waiting */
197        if ((int) hdr->wirelen == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
198                /* Do message queue check or select */
199                int message_fd;
200                int largestfd = stream->fd;
201
202                /* Also check the message queue */
203                if (queue) {
204                        message_fd = libtrace_message_queue_get_fd(queue);
205                        if (message_fd > largestfd)
206                                largestfd = message_fd;
207                }
208                do {
209                        /* Use select to allow us to time out occasionally to check if someone
210                         * has hit Ctrl-C or otherwise wants us to stop reading and return
211                         * so they can exit their program.
212                         */
213                        tout.tv_sec = 0;
214                        tout.tv_usec = 500000;
215                        /* Make sure we reset these each loop */
216                        FD_ZERO(&readfds);
217                        FD_SET(stream->fd, &readfds);
218                        if (queue)
219                                FD_SET(message_fd, &readfds);
220
221                        ret = select(largestfd+1, &readfds, NULL, NULL, &tout);
222                        if (ret >= 1) {
223                                /* A file descriptor triggered */
224                                break;
225                        } else if (ret < 0 && errno != EINTR) {
226                                trace_set_err(libtrace, errno, "select");
227                                return -1;
228                        } else {
229                                if (libtrace_halt)
230                                        return READ_EOF;
231                        }
232                }
233                while (ret <= 0);
234
235                /* Message waiting? */
236                if (queue && FD_ISSET(message_fd, &readfds))
237                        return READ_MESSAGE;
238
239                /* We must have a packet */
240                hdr->wirelen = recvmsg(stream->fd, &msghdr, MSG_TRUNC);
241        }
242
243        if (hdr->wirelen==~0U) {
244                trace_set_err(libtrace,errno,"recvmsg");
245                return -1;
246        }
247
248        hdr->caplen=LIBTRACE_MIN((unsigned int)snaplen,(unsigned int)hdr->wirelen);
249
250        /* Extract the timestamps from the msghdr and store them in our
251         * linux native encapsulation, so that we can preserve the formatting
252         * across multiple architectures */
253
254        for (cmsg = CMSG_FIRSTHDR(&msghdr);
255                        cmsg != NULL;
256                        cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
257                if (cmsg->cmsg_level == SOL_SOCKET
258                        && cmsg->cmsg_type == SO_TIMESTAMP
259                        && cmsg->cmsg_len <= CMSG_LEN(sizeof(struct timeval))) {
260                       
261                        struct timeval *tv;
262                        tv = (struct timeval *)CMSG_DATA(cmsg);
263                       
264                       
265                        hdr->tv.tv_sec = tv->tv_sec;
266                        hdr->tv.tv_usec = tv->tv_usec;
267                        hdr->timestamptype = TS_TIMEVAL;
268                        break;
269                } 
270#ifdef SO_TIMESTAMPNS
271                else if (cmsg->cmsg_level == SOL_SOCKET
272                        && cmsg->cmsg_type == SO_TIMESTAMPNS
273                        && cmsg->cmsg_len <= CMSG_LEN(sizeof(struct timespec))) {
274
275                        struct timespec *tv;
276                        tv = (struct timespec *)CMSG_DATA(cmsg);
277
278                        hdr->ts.tv_sec = tv->tv_sec;
279                        hdr->ts.tv_nsec = tv->tv_nsec;
280                        hdr->timestamptype = TS_TIMESPEC;
281                        break;
282                }
283#endif
284        }
285
286        /* Did we not get given a timestamp? Try to get one from the
287         * file descriptor directly */
288        if (cmsg == NULL) {
289                struct timeval tv;
290                if (ioctl(stream->fd, SIOCGSTAMP,&tv)==0) {
291                        hdr->tv.tv_sec = tv.tv_sec;
292                        hdr->tv.tv_usec = tv.tv_usec;
293                        hdr->timestamptype = TS_TIMEVAL;
294                }
295                else {
296                        hdr->timestamptype = TS_NONE;
297                }
298        }
299
300        /* Buffer contains all of our packet (including our custom header) so
301         * we just need to get prepare_packet to set all our packet pointers
302         * appropriately */
303       
304        if (linuxnative_prepare_packet(libtrace, packet, packet->buffer,
305                                packet->type, flags))
306                return -1;
307       
308        return hdr->wirelen+sizeof(*hdr);
309}
310
311static int linuxnative_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) 
312{
313        return linuxnative_read_stream(libtrace, packet, FORMAT_DATA_FIRST, NULL);
314}
315
316static int linuxnative_pread_packets(libtrace_t *libtrace,
317                                     libtrace_thread_t *t,
318                                     libtrace_packet_t *packets[],
319                                     UNUSED size_t nb_packets) {
320        /* For now just read one packet */
321        packets[0]->error = linuxnative_read_stream(libtrace, packets[0],
322                                                       t->format_data, &t->messages);
323        if (packets[0]->error >= 1)
324                return 1;
325        else
326                return packets[0]->error;
327}
328
329static int linuxnative_write_packet(libtrace_out_t *libtrace,
330                libtrace_packet_t *packet) 
331{
332        struct sockaddr_ll hdr;
333        int ret = 0;
334
335        if (trace_get_link_type(packet) == TRACE_TYPE_NONDATA)
336                return 0;
337
338        hdr.sll_family = AF_PACKET;
339        hdr.sll_protocol = 0;
340        hdr.sll_ifindex = if_nametoindex(libtrace->uridata);
341        hdr.sll_hatype = 0;
342        hdr.sll_pkttype = 0;
343        hdr.sll_halen = htons(6); /* FIXME */
344        memcpy(hdr.sll_addr,packet->payload,(size_t)ntohs(hdr.sll_halen));
345
346        /* This is pretty easy, just send the payload using sendto() (after
347         * setting up the sll header properly, of course) */
348        ret = sendto(FORMAT_DATA_OUT->fd,
349                        packet->payload,
350                        trace_get_capture_length(packet),
351                        0,
352                        (struct sockaddr*)&hdr, (socklen_t)sizeof(hdr));
353
354        if (ret < 0) {
355                trace_set_err_out(libtrace, errno, "sendto failed");
356        }
357
358        return ret;
359}
360#endif /* HAVE_NETPACKET_PACKET_H */
361
362
363static libtrace_linktype_t linuxnative_get_link_type(const struct libtrace_packet_t *packet) {
364        uint16_t linktype=(((struct libtrace_linuxnative_header*)(packet->buffer))
365                                ->hdr.sll_hatype);
366        return linuxcommon_get_link_type(linktype);
367}
368
369static libtrace_direction_t linuxnative_get_direction(const struct libtrace_packet_t *packet) {
370        return linuxcommon_get_direction(((struct libtrace_linuxnative_header*)(packet->buffer))->hdr.sll_pkttype);
371}
372
373static libtrace_direction_t linuxnative_set_direction(
374                libtrace_packet_t *packet,
375                libtrace_direction_t direction) {
376        return linuxcommon_set_direction(&((struct libtrace_linuxnative_header*)(packet->buffer))->hdr, direction);
377}
378
379static struct timespec linuxnative_get_timespec(const libtrace_packet_t *packet) 
380{
381        struct libtrace_linuxnative_header *hdr = 
382                (struct libtrace_linuxnative_header*) packet->buffer;
383        /* We have to upconvert from timeval to timespec */
384        if (hdr->timestamptype == TS_TIMEVAL) {
385                struct timespec ts;
386                ts.tv_sec = hdr->tv.tv_sec;
387                ts.tv_nsec = hdr->tv.tv_usec*1000;
388                return ts;
389        }
390        else {
391                struct timespec ts;
392                ts.tv_sec = hdr->ts.tv_sec;
393                ts.tv_nsec = hdr->ts.tv_nsec;
394                return ts;
395        }
396}
397
398static struct timeval linuxnative_get_timeval(const libtrace_packet_t *packet) 
399{
400        struct libtrace_linuxnative_header *hdr = 
401                (struct libtrace_linuxnative_header*) packet->buffer;
402        /* We have to downconvert from timespec to timeval */
403        if (hdr->timestamptype == TS_TIMESPEC) {
404                struct timeval tv;
405                tv.tv_sec = hdr->ts.tv_sec;
406                tv.tv_usec = hdr->ts.tv_nsec/1000;
407                return tv;
408        }
409        else {
410                struct timeval tv;
411                tv.tv_sec = hdr->tv.tv_sec;
412                tv.tv_usec = hdr->tv.tv_usec;
413                return tv;
414        }
415}
416
417static int linuxnative_get_capture_length(const libtrace_packet_t *packet)
418{
419        return ((struct libtrace_linuxnative_header*)(packet->buffer))->caplen;
420}
421
422
423static int linuxnative_get_wire_length(const libtrace_packet_t *packet) 
424{
425
426        int wirelen = ((struct libtrace_linuxnative_header*)(packet->buffer))->wirelen;
427
428        /* Include the missing FCS */
429        if (trace_get_link_type(packet) == TRACE_TYPE_ETH)
430                wirelen += 4;
431
432        return wirelen;
433}
434
435
436static int linuxnative_get_framing_length(UNUSED
437                const libtrace_packet_t *packet) 
438{
439        return sizeof(struct libtrace_linuxnative_header);
440}
441
442static size_t linuxnative_set_capture_length(libtrace_packet_t *packet, 
443                size_t size) {
444
445        struct libtrace_linuxnative_header *linux_hdr = NULL;
446        assert(packet);
447        if (size > trace_get_capture_length(packet)) {
448                /* We should avoid making a packet larger */
449                return trace_get_capture_length(packet);
450        }
451       
452        /* Reset the cached capture length */
453        packet->capture_length = -1;
454
455        linux_hdr = (struct libtrace_linuxnative_header *)packet->header;
456        linux_hdr->caplen = size;
457        return trace_get_capture_length(packet);
458}
459
460
461
462
463
464#ifdef HAVE_NETPACKET_PACKET_H
465static void linuxnative_help(void) {
466        printf("linuxnative format module: $Revision: 1793 $\n");
467        printf("Supported input URIs:\n");
468        printf("\tint:eth0\n");
469        printf("\n");
470        printf("Supported output URIs:\n");
471        printf("\tint:eth0\n");
472        printf("\n");
473        return;
474}
475
476static struct libtrace_format_t linuxnative = {
477        "int",
478        "$Id$",
479        TRACE_FORMAT_LINUX_NATIVE,
480        linuxcommon_probe_filename,     /* probe filename */
481        NULL,                           /* probe magic */
482        linuxcommon_init_input,         /* init_input */
483        linuxcommon_config_input,       /* config_input */
484        linuxnative_start_input,        /* start_input */
485        linuxcommon_pause_input,        /* pause_input */
486        linuxcommon_init_output,        /* init_output */
487        NULL,                           /* config_output */
488        linuxnative_start_output,       /* start_ouput */
489        linuxcommon_fin_input,          /* fin_input */
490        linuxnative_fin_output,         /* fin_output */
491        linuxnative_read_packet,        /* read_packet */
492        linuxnative_prepare_packet,     /* prepare_packet */
493        NULL,                           /* fin_packet */
494        linuxnative_write_packet,       /* write_packet */
495        linuxnative_get_link_type,      /* get_link_type */
496        linuxnative_get_direction,      /* get_direction */
497        linuxnative_set_direction,      /* set_direction */
498        NULL,                           /* get_erf_timestamp */
499        linuxnative_get_timeval,        /* get_timeval */
500        linuxnative_get_timespec,       /* get_timespec */
501        NULL,                           /* get_seconds */
502        NULL,                           /* seek_erf */
503        NULL,                           /* seek_timeval */
504        NULL,                           /* seek_seconds */
505        linuxnative_get_capture_length, /* get_capture_length */
506        linuxnative_get_wire_length,    /* get_wire_length */
507        linuxnative_get_framing_length, /* get_framing_length */
508        linuxnative_set_capture_length, /* set_capture_length */
509        NULL,                           /* get_received_packets */
510        NULL,                           /* get_filtered_packets */
511        NULL,                           /* get_dropped_packets */
512        linuxcommon_get_statistics,     /* get_statistics */
513        linuxcommon_get_fd,             /* get_fd */
514        trace_event_device,             /* trace_event */
515        linuxnative_help,               /* help */
516        NULL,                           /* next pointer */
517        {true, -1},                     /* Live, no thread limit */
518        linuxnative_pstart_input,       /* pstart_input */
519        linuxnative_pread_packets,      /* pread_packets */
520        linuxcommon_pause_input,        /* ppause */
521        linuxcommon_fin_input,          /* p_fin */
522        linuxcommon_pconfig_input,      /* pconfig input */
523        linuxcommon_pregister_thread,   /* register thread */
524        NULL,                           /* unregister thread */
525        NULL                            /* get thread stats */
526};
527#else
528static void linuxnative_help(void) {
529        printf("linuxnative format module: $Revision: 1793 $\n");
530        printf("Not supported on this host\n");
531}
532
533static struct libtrace_format_t linuxnative = {
534        "int",
535        "$Id$",
536        TRACE_FORMAT_LINUX_NATIVE,
537        NULL,                           /* probe filename */
538        NULL,                           /* probe magic */
539        NULL,                           /* init_input */
540        NULL,                           /* config_input */
541        NULL,                           /* start_input */
542        NULL,                           /* pause_input */
543        NULL,                           /* init_output */
544        NULL,                           /* config_output */
545        NULL,                           /* start_ouput */
546        NULL,                           /* fin_input */
547        NULL,                           /* fin_output */
548        NULL,                           /* read_packet */
549        linuxnative_prepare_packet,     /* prepare_packet */
550        NULL,                           /* fin_packet */
551        NULL,                           /* write_packet */
552        linuxnative_get_link_type,      /* get_link_type */
553        linuxnative_get_direction,      /* get_direction */
554        linuxnative_set_direction,      /* set_direction */
555        NULL,                           /* get_erf_timestamp */
556        linuxnative_get_timeval,        /* get_timeval */
557        linuxnative_get_timespec,       /* get_timespec */
558        NULL,                           /* get_seconds */
559        NULL,                           /* seek_erf */
560        NULL,                           /* seek_timeval */
561        NULL,                           /* seek_seconds */
562        linuxnative_get_capture_length, /* get_capture_length */
563        linuxnative_get_wire_length,    /* get_wire_length */
564        linuxnative_get_framing_length, /* get_framing_length */
565        linuxnative_set_capture_length, /* set_capture_length */
566        NULL,                           /* get_received_packets */
567        NULL,                           /* get_filtered_packets */
568        NULL,                           /* get_dropped_packets */
569        linuxcommon_get_statistics,     /* get_statistics */
570        linuxnative_get_fd,             /* get_fd */
571        trace_event_device,             /* trace_event */
572        linuxnative_help,               /* help */
573        NULL,                   /* next pointer */
574        NON_PARALLEL(true)
575};
576#endif /* HAVE_NETPACKET_PACKET_H */
577
578void linuxnative_constructor(void) {
579        register_format(&linuxnative);
580}
Note: See TracBrowser for help on using the repository browser.