source: lib/format_linux_int.c @ 95ca714

cachetimestampsdeveloprc-4.0.4ringdecrementfixringperformance
Last change on this file since 95ca714 was 32ee9b2, checked in by Shane Alcock <salcock@…>, 3 years ago

Add new trace_flush_output() to public API

Can be used to force a libtrace output to dump any buffered output
to disk immediately.

Note that if the file is compressed or the output trace format
requires a trailer, the flushed file will still not be properly
readable afterwards as this will not result in any trailers
being written. You'll still have to close the file for that.

Mainly this is useful for ensuring that output file sizes grow
over time in situations where the amount of output is relatively
small, rather than staying stuck at 0 bytes until we either reach
1MB of output or the file is closed. For instance, you could have
a timer that calls trace_flush_output() every 30 seconds so that
the output file size will grow if any packets were written in the
last 30 seconds.

  • 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        NULL,                           /* flush_output */
499        linuxnative_get_link_type,      /* get_link_type */
500        linuxnative_get_direction,      /* get_direction */
501        linuxnative_set_direction,      /* set_direction */
502        NULL,                           /* get_erf_timestamp */
503        linuxnative_get_timeval,        /* get_timeval */
504        linuxnative_get_timespec,       /* get_timespec */
505        NULL,                           /* get_seconds */
506        NULL,                           /* seek_erf */
507        NULL,                           /* seek_timeval */
508        NULL,                           /* seek_seconds */
509        linuxnative_get_capture_length, /* get_capture_length */
510        linuxnative_get_wire_length,    /* get_wire_length */
511        linuxnative_get_framing_length, /* get_framing_length */
512        linuxnative_set_capture_length, /* set_capture_length */
513        NULL,                           /* get_received_packets */
514        NULL,                           /* get_filtered_packets */
515        NULL,                           /* get_dropped_packets */
516        linuxcommon_get_statistics,     /* get_statistics */
517        linuxcommon_get_fd,             /* get_fd */
518        trace_event_device,             /* trace_event */
519        linuxnative_help,               /* help */
520        NULL,                           /* next pointer */
521#ifdef HAVE_PACKET_FANOUT
522        {true, -1},                     /* Live, no thread limit */
523        linuxnative_pstart_input,       /* pstart_input */
524        linuxnative_pread_packets,      /* pread_packets */
525        linuxcommon_pause_input,        /* ppause */
526        linuxcommon_fin_input,          /* p_fin */
527        linuxcommon_pregister_thread,   /* register thread */
528        NULL,                           /* unregister thread */
529        NULL                            /* get thread stats */
530#else
531        NON_PARALLEL(true)
532#endif
533};
534#else
535static void linuxnative_help(void) {
536        printf("linuxnative format module: $Revision: 1793 $\n");
537        printf("Not supported on this host\n");
538}
539
540static struct libtrace_format_t linuxnative = {
541        "int",
542        "$Id$",
543        TRACE_FORMAT_LINUX_NATIVE,
544        NULL,                           /* probe filename */
545        NULL,                           /* probe magic */
546        NULL,                           /* init_input */
547        NULL,                           /* config_input */
548        NULL,                           /* start_input */
549        NULL,                           /* pause_input */
550        NULL,                           /* init_output */
551        NULL,                           /* config_output */
552        NULL,                           /* start_ouput */
553        NULL,                           /* fin_input */
554        NULL,                           /* fin_output */
555        NULL,                           /* read_packet */
556        linuxnative_prepare_packet,     /* prepare_packet */
557        NULL,                           /* fin_packet */
558        NULL,                           /* write_packet */
559        NULL,                           /* flush_output */
560        linuxnative_get_link_type,      /* get_link_type */
561        linuxnative_get_direction,      /* get_direction */
562        linuxnative_set_direction,      /* set_direction */
563        NULL,                           /* get_erf_timestamp */
564        linuxnative_get_timeval,        /* get_timeval */
565        linuxnative_get_timespec,       /* get_timespec */
566        NULL,                           /* get_seconds */
567        NULL,                           /* seek_erf */
568        NULL,                           /* seek_timeval */
569        NULL,                           /* seek_seconds */
570        linuxnative_get_capture_length, /* get_capture_length */
571        linuxnative_get_wire_length,    /* get_wire_length */
572        linuxnative_get_framing_length, /* get_framing_length */
573        linuxnative_set_capture_length, /* set_capture_length */
574        NULL,                           /* get_received_packets */
575        NULL,                           /* get_filtered_packets */
576        NULL,                           /* get_dropped_packets */
577        linuxcommon_get_statistics,     /* get_statistics */
578        NULL,                           /* get_fd */
579        NULL,                           /* trace_event */
580        linuxnative_help,               /* help */
581        NULL,                   /* next pointer */
582        NON_PARALLEL(true)
583};
584#endif /* HAVE_NETPACKET_PACKET_H */
585
586void linuxnative_constructor(void) {
587        register_format(&linuxnative);
588}
Note: See TracBrowser for help on using the repository browser.