source: lib/format_linux_int.c @ eb70703

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivendag_formatrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since eb70703 was db84bb2, checked in by Shane Alcock <salcock@…>, 4 years ago

Ensure packet->order is always strictly incrementing

We cannot equate timestamp with packet->order, as some timestamp
methods are not strictly monotonic (ring: and int:).

Each format is now responsible for determining packet->order
during pread, so that the format can detect and correct such
inaccuracies.

More specifically, ring: and int: will cache the last reported
timestamp per thread and if time goes backwards, the order will
be set to last+1, otherwise the timestamp will be used.

DAG and DPDK still use the timestamp for ordering, since there
have been no issues with the timestamp ordering for these formats
(thus far!).

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