source: lib/format_helper.c @ 4649fea

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivelibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 4649fea was 4649fea, checked in by Shane Alcock <salcock@…>, 7 years ago

Fix trace_event errors with pcapint: and filters

If a packet arrives between the time a live pcap capture
is created and the time the filter is set, the pcap fd acts
as though there is a packet available even if pcap_next_ex will
time out due to no packets matching the filter.

This can wreak havoc with trace_event, which relies on select
to know whether it will be able to call trace_read_packet
without blocking. If the filter doesn't match any packets,
trace_read_packet will block forever -- defeat the purpose of
using trace_event in the first place.

The solution is to always try to consume a packet immediately after
calling pcap_setfilter. Any "bad" packets will be removed from the
queue until a packet that matches the filter is hit or pcap_next_ex
times out. Either way, select should work properly again, at the
cost of possible one legit packet being consumed during startup.

Thanks to Mike Schiffman for reporting this bug (and the previous
one re: inconsistent behaviour when the filter is bogus).

  • Property mode set to 100644
File size: 9.0 KB
Line 
1/*
2 * This file is part of libtrace
3 *
4 * Copyright (c) 2007,2008,2009,2010 The University of Waikato, Hamilton,
5 * New Zealand.
6 *
7 * Authors: Daniel Lawson
8 *          Perry Lorier
9 *          Shane Alcock
10 *         
11 * All rights reserved.
12 *
13 * This code has been developed by the University of Waikato WAND
14 * research group. For further information please see http://www.wand.net.nz/
15 *
16 * libtrace is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * libtrace is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with libtrace; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29 *
30 * $Id$
31 *
32 */
33
34#include "config.h"
35#include <sys/types.h>
36#include <fcntl.h> /* for O_LARGEFILE */
37#include <math.h>
38#include "libtrace.h"
39#include "libtrace_int.h"
40#include "wandio.h"
41
42#include <stdlib.h>
43#include <stdio.h>
44#include <string.h>
45#include <errno.h>
46#include <time.h>
47#include "format_helper.h"
48
49#include <assert.h>
50#include <stdarg.h>
51
52#ifdef WIN32
53#  include <io.h>
54#  include <share.h>
55#  include <sys/timeb.h>
56
57struct libtrace_eventobj_t trace_event_device(struct libtrace_t *trace, struct libtrace_packet_t *packet) {
58    struct libtrace_eventobj_t event = {0,0,0.0,0};
59
60    trace_set_err(trace,TRACE_ERR_OPTION_UNAVAIL, "trace_event() is not "
61            "supported on devices under windows in this version");
62
63    event.type = TRACE_EVENT_TERMINATE;
64    return event;
65}
66#else
67#  include <sys/ioctl.h>
68
69/* Generic event function for live capture devices / interfaces */
70struct libtrace_eventobj_t trace_event_device(struct libtrace_t *trace, 
71                                        struct libtrace_packet_t *packet) {
72        struct libtrace_eventobj_t event = {0,0,0.0,0};
73
74        fd_set rfds, rfds_param;
75        int ret;
76        int max_fd;
77        struct timeval tv;
78
79        assert(trace != NULL);
80        assert(packet != NULL);
81       
82        FD_ZERO(&rfds);
83        FD_ZERO(&rfds_param);
84
85        if (trace->format->get_fd) {
86                event.fd = trace->format->get_fd(trace);
87                FD_SET(event.fd, &rfds);
88                max_fd = event.fd;
89        } else {
90                event.fd = 0;
91                max_fd = -1;
92        }
93
94        /* Use select() to perform a quick poll to check that there is data
95         * available - we used to use FIONREAD here but that does not work
96         * for mmapped pcap sockets. As recent pcap on linux (e.g. Ubuntu 9.04)
97         * uses mmapped sockets by default, I've switched over to this
98         * solution. */
99
100        do {
101                tv.tv_sec = 0;
102                tv.tv_usec = 0;
103                rfds_param = rfds;
104
105                ret = select(max_fd + 1, &rfds_param, NULL, NULL, &tv);
106                if (ret == -1 && errno != EINTR) {
107                        event.type = TRACE_EVENT_TERMINATE;
108                        return event;
109                }
110        } while (ret == -1);
111
112        if (FD_ISSET(event.fd, &rfds_param)) {
113                event.size = trace_read_packet(trace,packet);
114                if (event.size < 1) {
115                        /* Covers error and EOF events - terminate rather
116                         * than report a packet as available */
117                        if (trace_is_err(trace)) {
118                                trace_perror(trace, "read packet");
119                        }
120                        event.type = TRACE_EVENT_TERMINATE;
121                } else {
122
123                        event.type = TRACE_EVENT_PACKET;
124                }
125                return event;
126        }
127        event.type= TRACE_EVENT_IOWAIT;
128        return event;
129}
130#endif
131
132/* Generic event function for trace files */ 
133struct libtrace_eventobj_t trace_event_trace(struct libtrace_t *trace, struct libtrace_packet_t *packet) {
134        struct libtrace_eventobj_t event = {0,0,0.0,0};
135        double ts;
136        double now;
137#ifdef WIN32
138        struct __timeb64 tstruct;
139#else
140        struct timeval stv;
141#endif
142
143        if (!trace->event.packet) {
144                trace->event.packet = trace_create_packet();
145        }
146
147        if (!trace->event.waiting) {
148                /* There is no packet event waiting for us, so create a new
149                 * libtrace packet in the event structure and read the next
150                 * packet into that.
151                 *
152                 * If a SLEEP event is reported this time around, the read
153                 * packet can therefore be saved until the next time this
154                 * function is called. */
155
156                trace->event.psize=
157                        trace_read_packet(trace,trace->event.packet);
158                if (trace->event.psize<1) {
159                        /* Return here, the test for event.size will sort out
160                         * the error  */
161                        if (trace_is_err(trace)) {
162                                trace_perror(trace, "read packet");
163                        }
164                        event.type = TRACE_EVENT_TERMINATE;
165                        trace_destroy_packet(trace->event.packet);
166                        trace->event.packet = NULL;
167                        packet->buffer = NULL;
168                        packet->header = NULL;
169                        packet->payload = NULL;
170                        packet->buf_control = TRACE_CTRL_EXTERNAL;
171                        return event;
172                }
173        }
174
175        /* The goal here is to replicate the inter-packet gaps that are
176         * present in the trace. */
177
178        ts=trace_get_seconds(trace->event.packet);
179
180        /* Get the current walltime */
181#ifdef WIN32
182        _ftime64(&tstruct);
183        now = tstruct.time + 
184                ((double)tstruct.millitm / 1000.0);
185#else
186        gettimeofday(&stv, NULL);
187        now = stv.tv_sec + 
188                ((double)stv.tv_usec / 1000000.0);
189#endif
190
191       
192        if (fabs(trace->event.tdelta)>1e-9) {
193                /* Subtract the tdelta from the walltime to get a suitable
194                 * "relative" time */
195                now -= trace->event.tdelta; 
196
197                /* If the trace timestamp is still in the future, return a
198                 * SLEEP event, otherwise return the packet */
199                if (ts > now) {
200                        event.seconds = ts - 
201                                trace->event.trace_last_ts;
202                        trace->event.trace_last_ts = ts;
203                        event.type = TRACE_EVENT_SLEEP;
204                        trace->event.waiting = true;
205                        return event;
206                }
207        } else {
208                /* Work out the difference between the walltime at the start
209                 * of the trace replay and the timestamp of the first packet
210                 * in the trace. This will be used to convert the walltime
211                 * into a timeline that is relative to the timestamps in the
212                 * trace file.
213                 */
214                trace->event.tdelta = now - ts;
215        }
216
217        /* The packet that we had read earlier is now ready to be returned
218         * to the user - switch all the pointers etc. over */   
219        packet->type = trace->event.packet->type;
220        packet->trace = trace->event.packet->trace;
221        packet->header = trace->event.packet->header;
222        packet->payload = trace->event.packet->payload;
223       
224        packet->buffer = trace->event.packet->buffer;
225        packet->buf_control = trace->event.packet->buf_control;
226
227        event.type = TRACE_EVENT_PACKET;
228
229        trace->event.trace_last_ts = ts;
230        trace->event.waiting = false;
231
232        return event;
233}
234
235/* Catch undefined O_LARGEFILE on *BSD etc */
236#ifndef O_LARGEFILE
237#  define O_LARGEFILE 0
238#endif
239
240/* Catching O_BINARY on all sane OS's */
241#ifndef O_BINARY
242#  define O_BINARY 0
243#endif
244
245/* Open a file for reading using the new Libtrace IO system */
246io_t *trace_open_file(libtrace_t *trace)
247{
248        io_t *io=wandio_create(trace->uridata);
249
250        if (!io) {
251                if (errno != 0) {
252                        trace_set_err(trace,errno,"Unable to open %s",trace->uridata);
253                } else {
254                        trace_set_err(trace,TRACE_ERR_UNSUPPORTED_COMPRESS,"Unsupported compression error: %s", trace->uridata);
255                }
256        }
257        return io;
258}
259
260/* Open a file for writing using the new Libtrace IO system */ 
261iow_t *trace_open_file_out(libtrace_out_t *trace, int compress_type, int level, int fileflag)
262{
263        iow_t *io = NULL;
264
265        if (level < 0 || level > 9) {
266                trace_set_err_out(trace, TRACE_ERR_UNSUPPORTED_COMPRESS, 
267                                "Compression level %d is invalid, must be between 0 and 9 inclusive", 
268                                level);
269                return NULL;
270        }
271
272        if (compress_type < 0 || 
273                        compress_type >= TRACE_OPTION_COMPRESSTYPE_LAST) {
274                trace_set_err_out(trace, TRACE_ERR_UNSUPPORTED_COMPRESS,
275                                "Invalid compression type %d", compress_type);
276                return NULL;
277        }
278
279        io = wandio_wcreate(trace->uridata, compress_type, level, fileflag);
280
281        if (!io) {
282                trace_set_err_out(trace, errno, "Unable to create output file %s", trace->uridata);
283        }
284        return io;
285}
286
287
288/** Sets the error status for an input trace
289 * @param errcode either an Econstant from libc, or a LIBTRACE_ERROR
290 * @param msg a plaintext error message
291 * @internal
292 */
293void trace_set_err(libtrace_t *trace,int errcode,const char *msg,...)
294{
295        char buf[256];
296        va_list va;
297        va_start(va,msg);
298        assert(errcode != 0 && "An error occurred, but it is unknown what it is");
299        trace->err.err_num=errcode;
300        if (errcode>0) {
301                vsnprintf(buf,sizeof(buf),msg,va);
302                snprintf(trace->err.problem,sizeof(trace->err.problem),
303                                "%s: %s",buf,strerror(errcode));
304        } else {
305                vsnprintf(trace->err.problem,sizeof(trace->err.problem),
306                                msg,va);
307        }
308        va_end(va);
309}
310
311/** Sets the error status for an output trace
312 * @param errcode either an Econstant from libc, or a LIBTRACE_ERROR
313 * @param msg a plaintext error message
314 * @internal
315 */
316void trace_set_err_out(libtrace_out_t *trace,int errcode,const char *msg,...)
317{
318        char buf[256];
319        va_list va;
320        va_start(va,msg);
321        assert(errcode != 0 && "An error occurred, but it is unknown what it is");
322        trace->err.err_num=errcode;
323        if (errcode>0) {
324                vsnprintf(buf,sizeof(buf),msg,va);
325                snprintf(trace->err.problem,sizeof(trace->err.problem),
326                                "%s: %s",buf,strerror(errno));
327        } else {
328                vsnprintf(trace->err.problem,sizeof(trace->err.problem),
329                                msg,va);
330        }
331        va_end(va);
332}
Note: See TracBrowser for help on using the repository browser.