source: lib/format_linux_int.c @ 5e3f16c

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivendag_formatrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 5e3f16c was 5e3f16c, checked in by Richard Sanger <rsanger@…>, 4 years ago

Fix for issue #39 - ring and int pstop() fails on older kernels when using threads

The problem here is that on old kernels without PACKET_FANOUT support
(added in v3.1) will only include the single threaded versions of int
and ring. When used with multiple threads the libtrace API will
fallback to using read rather than pread which does not check message
queues.

To fix this issue, in any format without pread support:

  • We check for new messages with each loop around read_packet as we fill the burst
  • Within read_packet we update the halt to include the pausing state
  • Use a seperate lock to the main lock when reading a burst of packets, otherwise trace_ppause has to wait for a burst to read.

This is not 100% perfect as a single packet might still need to be received
before a generic message can be received.
A proper fix in the future would be to move all format internals purely to the
parallel API.

  • Property mode set to 100644
File size: 16.1 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        /* Buffer contains all of our packet (including our custom header) so
289         * we just need to get prepare_packet to set all our packet pointers
290         * appropriately */
291        packet->trace = libtrace;
292        if (linuxnative_prepare_packet(libtrace, packet, packet->buffer,
293                                packet->type, flags))
294                return -1;
295       
296        return hdr->wirelen+sizeof(*hdr);
297}
298
299static int linuxnative_read_packet(libtrace_t *libtrace, libtrace_packet_t *packet) 
300{
301        return linuxnative_read_stream(libtrace, packet, FORMAT_DATA_FIRST, NULL);
302}
303
304#ifdef HAVE_PACKET_FANOUT
305static int linuxnative_pread_packets(libtrace_t *libtrace,
306                                     libtrace_thread_t *t,
307                                     libtrace_packet_t *packets[],
308                                     UNUSED size_t nb_packets) {
309        /* For now just read one packet */
310        packets[0]->error = linuxnative_read_stream(libtrace, packets[0],
311                                                       t->format_data, &t->messages);
312        if (packets[0]->error >= 1)
313                return 1;
314        else
315                return packets[0]->error;
316}
317#endif
318
319static int linuxnative_write_packet(libtrace_out_t *libtrace,
320                libtrace_packet_t *packet) 
321{
322        struct sockaddr_ll hdr;
323        int ret = 0;
324
325        if (trace_get_link_type(packet) == TRACE_TYPE_NONDATA)
326                return 0;
327
328        hdr.sll_family = AF_PACKET;
329        hdr.sll_protocol = 0;
330        hdr.sll_ifindex = if_nametoindex(libtrace->uridata);
331        hdr.sll_hatype = 0;
332        hdr.sll_pkttype = 0;
333        hdr.sll_halen = htons(6); /* FIXME */
334        memcpy(hdr.sll_addr,packet->payload,(size_t)ntohs(hdr.sll_halen));
335
336        /* This is pretty easy, just send the payload using sendto() (after
337         * setting up the sll header properly, of course) */
338        ret = sendto(FORMAT_DATA_OUT->fd,
339                        packet->payload,
340                        trace_get_capture_length(packet),
341                        0,
342                        (struct sockaddr*)&hdr, (socklen_t)sizeof(hdr));
343
344        if (ret < 0) {
345                trace_set_err_out(libtrace, errno, "sendto failed");
346        }
347
348        return ret;
349}
350#endif /* HAVE_NETPACKET_PACKET_H */
351
352
353static libtrace_linktype_t linuxnative_get_link_type(const struct libtrace_packet_t *packet) {
354        uint16_t linktype=(((struct libtrace_linuxnative_header*)(packet->buffer))
355                                ->hdr.sll_hatype);
356        return linuxcommon_get_link_type(linktype);
357}
358
359static libtrace_direction_t linuxnative_get_direction(const struct libtrace_packet_t *packet) {
360        return linuxcommon_get_direction(((struct libtrace_linuxnative_header*)(packet->buffer))->hdr.sll_pkttype);
361}
362
363static libtrace_direction_t linuxnative_set_direction(
364                libtrace_packet_t *packet,
365                libtrace_direction_t direction) {
366        return linuxcommon_set_direction(&((struct libtrace_linuxnative_header*)(packet->buffer))->hdr, direction);
367}
368
369static struct timespec linuxnative_get_timespec(const libtrace_packet_t *packet) 
370{
371        struct libtrace_linuxnative_header *hdr = 
372                (struct libtrace_linuxnative_header*) packet->buffer;
373        /* We have to upconvert from timeval to timespec */
374        if (hdr->timestamptype == TS_TIMEVAL) {
375                struct timespec ts;
376                ts.tv_sec = hdr->tv.tv_sec;
377                ts.tv_nsec = hdr->tv.tv_usec*1000;
378                return ts;
379        }
380        else {
381                struct timespec ts;
382                ts.tv_sec = hdr->ts.tv_sec;
383                ts.tv_nsec = hdr->ts.tv_nsec;
384                return ts;
385        }
386}
387
388static struct timeval linuxnative_get_timeval(const libtrace_packet_t *packet) 
389{
390        struct libtrace_linuxnative_header *hdr = 
391                (struct libtrace_linuxnative_header*) packet->buffer;
392        /* We have to downconvert from timespec to timeval */
393        if (hdr->timestamptype == TS_TIMESPEC) {
394                struct timeval tv;
395                tv.tv_sec = hdr->ts.tv_sec;
396                tv.tv_usec = hdr->ts.tv_nsec/1000;
397                return tv;
398        }
399        else {
400                struct timeval tv;
401                tv.tv_sec = hdr->tv.tv_sec;
402                tv.tv_usec = hdr->tv.tv_usec;
403                return tv;
404        }
405}
406
407static int linuxnative_get_capture_length(const libtrace_packet_t *packet)
408{
409        return ((struct libtrace_linuxnative_header*)(packet->buffer))->caplen;
410}
411
412
413static int linuxnative_get_wire_length(const libtrace_packet_t *packet) 
414{
415
416        int wirelen = ((struct libtrace_linuxnative_header*)(packet->buffer))->wirelen;
417
418        /* Include the missing FCS */
419        if (trace_get_link_type(packet) == TRACE_TYPE_ETH)
420                wirelen += 4;
421
422        return wirelen;
423}
424
425
426static int linuxnative_get_framing_length(UNUSED
427                const libtrace_packet_t *packet) 
428{
429        return sizeof(struct libtrace_linuxnative_header);
430}
431
432static size_t linuxnative_set_capture_length(libtrace_packet_t *packet, 
433                size_t size) {
434
435        struct libtrace_linuxnative_header *linux_hdr = NULL;
436        assert(packet);
437        if (size > trace_get_capture_length(packet)) {
438                /* We should avoid making a packet larger */
439                return trace_get_capture_length(packet);
440        }
441       
442        /* Reset the cached capture length */
443        packet->capture_length = -1;
444
445        linux_hdr = (struct libtrace_linuxnative_header *)packet->header;
446        linux_hdr->caplen = size;
447        return trace_get_capture_length(packet);
448}
449
450#ifdef HAVE_NETPACKET_PACKET_H
451static void linuxnative_help(void) {
452        printf("linuxnative format module: $Revision: 1793 $\n");
453        printf("Supported input URIs:\n");
454        printf("\tint:eth0\n");
455        printf("\n");
456        printf("Supported output URIs:\n");
457        printf("\tint:eth0\n");
458        printf("\n");
459        return;
460}
461
462static struct libtrace_format_t linuxnative = {
463        "int",
464        "$Id$",
465        TRACE_FORMAT_LINUX_NATIVE,
466        linuxcommon_probe_filename,     /* probe filename */
467        NULL,                           /* probe magic */
468        linuxcommon_init_input,         /* init_input */
469        linuxcommon_config_input,       /* config_input */
470        linuxnative_start_input,        /* start_input */
471        linuxcommon_pause_input,        /* pause_input */
472        linuxcommon_init_output,        /* init_output */
473        NULL,                           /* config_output */
474        linuxnative_start_output,       /* start_ouput */
475        linuxcommon_fin_input,          /* fin_input */
476        linuxnative_fin_output,         /* fin_output */
477        linuxnative_read_packet,        /* read_packet */
478        linuxnative_prepare_packet,     /* prepare_packet */
479        NULL,                           /* fin_packet */
480        linuxnative_write_packet,       /* write_packet */
481        linuxnative_get_link_type,      /* get_link_type */
482        linuxnative_get_direction,      /* get_direction */
483        linuxnative_set_direction,      /* set_direction */
484        NULL,                           /* get_erf_timestamp */
485        linuxnative_get_timeval,        /* get_timeval */
486        linuxnative_get_timespec,       /* get_timespec */
487        NULL,                           /* get_seconds */
488        NULL,                           /* seek_erf */
489        NULL,                           /* seek_timeval */
490        NULL,                           /* seek_seconds */
491        linuxnative_get_capture_length, /* get_capture_length */
492        linuxnative_get_wire_length,    /* get_wire_length */
493        linuxnative_get_framing_length, /* get_framing_length */
494        linuxnative_set_capture_length, /* set_capture_length */
495        NULL,                           /* get_received_packets */
496        NULL,                           /* get_filtered_packets */
497        NULL,                           /* get_dropped_packets */
498        linuxcommon_get_statistics,     /* get_statistics */
499        linuxcommon_get_fd,             /* get_fd */
500        trace_event_device,             /* trace_event */
501        linuxnative_help,               /* help */
502        NULL,                           /* next pointer */
503#ifdef HAVE_PACKET_FANOUT
504        {true, -1},                     /* Live, no thread limit */
505        linuxnative_pstart_input,       /* pstart_input */
506        linuxnative_pread_packets,      /* pread_packets */
507        linuxcommon_pause_input,        /* ppause */
508        linuxcommon_fin_input,          /* p_fin */
509        linuxcommon_pregister_thread,   /* register thread */
510        NULL,                           /* unregister thread */
511        NULL                            /* get thread stats */
512#else
513        NON_PARALLEL(true)
514#endif
515};
516#else
517static void linuxnative_help(void) {
518        printf("linuxnative format module: $Revision: 1793 $\n");
519        printf("Not supported on this host\n");
520}
521
522static struct libtrace_format_t linuxnative = {
523        "int",
524        "$Id$",
525        TRACE_FORMAT_LINUX_NATIVE,
526        NULL,                           /* probe filename */
527        NULL,                           /* probe magic */
528        NULL,                           /* init_input */
529        NULL,                           /* config_input */
530        NULL,                           /* start_input */
531        NULL,                           /* pause_input */
532        NULL,                           /* init_output */
533        NULL,                           /* config_output */
534        NULL,                           /* start_ouput */
535        NULL,                           /* fin_input */
536        NULL,                           /* fin_output */
537        NULL,                           /* read_packet */
538        linuxnative_prepare_packet,     /* prepare_packet */
539        NULL,                           /* fin_packet */
540        NULL,                           /* write_packet */
541        linuxnative_get_link_type,      /* get_link_type */
542        linuxnative_get_direction,      /* get_direction */
543        linuxnative_set_direction,      /* set_direction */
544        NULL,                           /* get_erf_timestamp */
545        linuxnative_get_timeval,        /* get_timeval */
546        linuxnative_get_timespec,       /* get_timespec */
547        NULL,                           /* get_seconds */
548        NULL,                           /* seek_erf */
549        NULL,                           /* seek_timeval */
550        NULL,                           /* seek_seconds */
551        linuxnative_get_capture_length, /* get_capture_length */
552        linuxnative_get_wire_length,    /* get_wire_length */
553        linuxnative_get_framing_length, /* get_framing_length */
554        linuxnative_set_capture_length, /* set_capture_length */
555        NULL,                           /* get_received_packets */
556        NULL,                           /* get_filtered_packets */
557        NULL,                           /* get_dropped_packets */
558        linuxcommon_get_statistics,     /* get_statistics */
559        NULL,                           /* get_fd */
560        NULL,                           /* trace_event */
561        linuxnative_help,               /* help */
562        NULL,                   /* next pointer */
563        NON_PARALLEL(true)
564};
565#endif /* HAVE_NETPACKET_PACKET_H */
566
567void linuxnative_constructor(void) {
568        register_format(&linuxnative);
569}
Note: See TracBrowser for help on using the repository browser.