source: lib/format_linux_int.c @ e9fe6ac

develop
Last change on this file since e9fe6ac was e9fe6ac, checked in by Shane Alcock <salcock@…>, 22 months ago

Use a #defined constant for SLL header size

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