source: lib/format_linux_int.c @ d439067

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

Move packet cached fields into a distinct structure.

This will help tidy up the packet structure a little, as well as
simplify the cache clearing process (and maybe even speed it up
a little).

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