source: lib/format_linux_int.c @ 254c926

develop
Last change on this file since 254c926 was 254c926, checked in by Jacob Van Walraven <jcv9@…>, 22 months ago

Cleanup some duplicate code, Added datatype/option_name for libtrace_meta_t structure

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