1 // -*- mode: c++; c-basic-offset: 4 -*-
2 /*
3 * fromdagdump.{cc,hh} -- element reads packets from DAG (Waikato) file
4 * Eddie Kohler
5 *
6 * Copyright (c) 2002 International Computer Science Institute
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, subject to the conditions
11 * listed in the Click LICENSE file. These conditions include: you must
12 * preserve this copyright notice, and you cannot mention the copyright
13 * holders in advertising related to the Software without their permission.
14 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
15 * notice is a summary of the Click LICENSE file; the license in that file is
16 * legally binding.
17 */
18
19 #include <click/config.h>
20 #include "fromdagdump.hh"
21 #include <click/args.hh>
22 #include <click/router.hh>
23 #include <click/standard/scheduleinfo.hh>
24 #include <click/error.hh>
25 #include <click/glue.hh>
26 #include <click/handlercall.hh>
27 #include <click/packet_anno.hh>
28 #include <clicknet/rfc1483.h>
29 #include <click/userutils.hh>
30 #include "fakepcap.hh"
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #ifdef ALLOW_MMAP
36 # include <sys/mman.h>
37 #endif
38 CLICK_DECLS
39
40 #define SWAPLONG(y) \
41 ((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
42 #define SWAPSHORT(y) \
43 ( (((y)&0xff)<<8) | ((u_short)((y)&0xff00)>>8) )
44
FromDAGDump()45 FromDAGDump::FromDAGDump()
46 : _packet(0), _end_h(0), _task(this)
47 {
48 static_assert(sizeof(DAGCell) == 64 && DAGCell::CELL_SIZE == 64, "DAGCell must be exactly 64 bytes long.");
49 }
50
~FromDAGDump()51 FromDAGDump::~FromDAGDump()
52 {
53 delete _end_h;
54 }
55
56 int
configure(Vector<String> & conf,ErrorHandler * errh)57 FromDAGDump::configure(Vector<String> &conf, ErrorHandler *errh)
58 {
59 bool timing = false, stop = false, active = true, force_ip = false;
60 Timestamp first_time, first_time_off, last_time, last_time_off, interval;
61 HandlerCall end_h;
62 String encap;
63 _sampling_prob = (1 << SAMPLING_SHIFT);
64
65 if (_ff.configure_keywords(conf, this, errh) < 0)
66 return -1;
67 if (Args(conf, this, errh)
68 .read_mp("FILENAME", FilenameArg(), _ff.filename())
69 .read("STOP", stop)
70 .read("ACTIVE", active)
71 .read("FORCE_IP", force_ip)
72 .read("START", first_time)
73 .read("START_AFTER", first_time_off)
74 .read("END", last_time)
75 .read("END_AFTER", last_time_off)
76 .read("INTERVAL", interval)
77 .read("END_CALL", HandlerCallArg(HandlerCall::writable), end_h)
78 .read("SAMPLE", FixedPointArg(SAMPLING_SHIFT), _sampling_prob)
79 .read("TIMING", timing)
80 .read("ENCAP", WordArg(), encap)
81 .complete() < 0)
82 return -1;
83
84 // check sampling rate
85 if (_sampling_prob > (1 << SAMPLING_SHIFT)) {
86 errh->warning("SAMPLE probability reduced to 1");
87 _sampling_prob = (1 << SAMPLING_SHIFT);
88 } else if (_sampling_prob == 0)
89 errh->warning("SAMPLE probability is 0; emitting no packets");
90
91 // check times
92 _have_first_time = _have_last_time = true;
93 _first_time_relative = _last_time_relative = _last_time_interval = false;
94
95 if ((bool) first_time + (bool) first_time_off > 1)
96 return errh->error("'START' and 'START_AFTER' are mutually exclusive");
97 else if ((bool) first_time)
98 _first_time = first_time;
99 else if ((bool) first_time_off)
100 _first_time = first_time_off, _first_time_relative = true;
101 else
102 _have_first_time = false, _first_time_relative = true;
103
104 if ((bool) last_time + (bool) last_time_off + (bool) interval > 1)
105 return errh->error("END, END_AFTER, and INTERVAL are mutually exclusive");
106 else if ((bool) last_time)
107 _last_time = last_time;
108 else if ((bool) last_time_off)
109 _last_time = last_time_off, _last_time_relative = true;
110 else if ((bool) interval)
111 _last_time = interval, _last_time_interval = true;
112 else
113 _have_last_time = false;
114
115 if (stop && end_h)
116 return errh->error("END_CALL and STOP are mutually exclusive");
117 else if (end_h)
118 _end_h = new HandlerCall(end_h);
119 else if (stop)
120 _end_h = new HandlerCall(name() + ".stop");
121 else if (_have_last_time)
122 _end_h = new HandlerCall(name() + ".active false");
123
124 // default linktype
125 if (!encap)
126 _base_linktype = FAKE_DLT_NONE;
127 else if ((_base_linktype = fake_pcap_parse_dlt(encap)) < 0
128 || (_base_linktype != FAKE_DLT_SUNATM
129 && _base_linktype != FAKE_DLT_C_HDLC
130 && _base_linktype != FAKE_DLT_EN10MB
131 && _base_linktype != FAKE_DLT_ATM_RFC1483
132 && _base_linktype != FAKE_DLT_PPP
133 && _base_linktype != FAKE_DLT_PPP_HDLC
134 && _base_linktype != FAKE_DLT_RAW))
135 return errh->error("bad encapsulation type");
136
137 // set other variables
138 _have_any_times = false;
139 _timing = timing;
140 _force_ip = force_ip;
141 _linktype = FAKE_DLT_NONE;
142 _active = active;
143 return 0;
144 }
145
146 int
initialize(ErrorHandler * errh)147 FromDAGDump::initialize(ErrorHandler *errh)
148 {
149 if (_ff.initialize(errh) < 0)
150 return -1;
151
152 // if forcing IP packets, check we're not running TIMING
153 if (_force_ip && _timing)
154 return errh->error("FORCE_IP and TIMING options are incompatible");
155
156 // check handler call
157 if (_end_h && _end_h->initialize_write(this, errh) < 0)
158 return -1;
159
160 // try reading a packet
161 if (read_packet(errh))
162 _time_offset = Timestamp::now() - _packet->timestamp_anno();
163
164 if (output_is_push(0))
165 ScheduleInfo::initialize_task(this, &_task, _active, errh);
166 return 0;
167 }
168
169 void
cleanup(CleanupStage)170 FromDAGDump::cleanup(CleanupStage)
171 {
172 _ff.cleanup();
173 if (_packet)
174 _packet->kill();
175 _packet = 0;
176 }
177
178 void
set_active(bool active)179 FromDAGDump::set_active(bool active)
180 {
181 if (_active != active) {
182 _active = active;
183 if (active && output_is_push(0) && !_task.scheduled())
184 _task.reschedule();
185 }
186 }
187
188 static inline uint64_t
swapq(uint64_t q)189 swapq(uint64_t q)
190 {
191 #if CLICK_BYTE_ORDER == CLICK_BIG_ENDIAN
192 return ((q & 0xff00000000000000LL) >> 56)
193 | ((q & 0x00ff000000000000LL) >> 40)
194 | ((q & 0x0000ff0000000000LL) >> 24)
195 | ((q & 0x000000ff00000000LL) >> 8)
196 | ((q & 0x00000000ff000000LL) << 8)
197 | ((q & 0x0000000000ff0000LL) << 24)
198 | ((q & 0x000000000000ff00LL) << 40)
199 | ((q & 0x00000000000000ffLL) << 56);
200 #elif CLICK_BYTE_ORDER == CLICK_LITTLE_ENDIAN
201 return q;
202 #else
203 #error "neither big nor little endian"
204 #endif
205 }
206
207 void
stamp_to_time(uint64_t stamp,Timestamp & tv) const208 FromDAGDump::stamp_to_time(uint64_t stamp, Timestamp &tv) const
209 {
210 uint32_t sec = (uint32_t) (stamp >> 32);
211 // based on a code description in an Endace document
212 stamp = (stamp & 0xFFFFFFFFULL) * 1000000000;
213 stamp += (stamp & 0x80000000ULL) << 1; // rounding
214 uint32_t nsec = (uint32_t) (stamp >> 32);
215 if (nsec >= 1000000000) {
216 nsec -= 1000000000;
217 sec += 1;
218 }
219 tv = Timestamp::make_nsec(sec, nsec);
220 }
221
222 void
prepare_times(const Timestamp & tv)223 FromDAGDump::prepare_times(const Timestamp &tv)
224 {
225 if (_first_time_relative)
226 _first_time += tv;
227 if (_last_time_relative)
228 _last_time += tv;
229 else if (_last_time_interval)
230 _last_time += _first_time;
231 _have_any_times = true;
232 }
233
234 bool
read_packet(ErrorHandler * errh)235 FromDAGDump::read_packet(ErrorHandler *errh)
236 {
237 const DAGCell *cell;
238 static DAGCell static_cell;
239 Timestamp tv;
240 Packet *p;
241 bool more = true;
242 _packet = 0;
243
244 retry:
245 // quit if we sampled or force_ip failed, but we are no longer active
246 if (!more)
247 return false;
248
249 // we may need to read bits of the file
250 cell = reinterpret_cast<const DAGCell *>(_ff.get_aligned(DAGCell::HEADER_SIZE, &static_cell, errh));
251 if (!cell)
252 return false;
253
254 // check times
255 check_times:
256 stamp_to_time(swapq(cell->timestamp), tv);
257 if (!_have_any_times)
258 prepare_times(tv);
259 if (_have_first_time) {
260 if (tv < _first_time)
261 goto retry;
262 else
263 _have_first_time = false;
264 }
265 if (_have_last_time && tv >= _last_time) {
266 _have_last_time = false;
267 (void) _end_h->call_write(errh);
268 if (!_active)
269 more = false;
270 // retry _last_time in case someone changed it
271 goto check_times;
272 }
273
274 // checking sampling probability
275 if (_sampling_prob < (1 << SAMPLING_SHIFT)
276 && (click_random() & ((1<<SAMPLING_SHIFT)-1)) >= _sampling_prob)
277 goto retry;
278
279 // determine read length and wire length
280 uint32_t wire_length = 0;
281 if (cell->type == DAGCell::TYPE_LEGACY || _base_linktype >= 0) {
282 use_base_linktype:
283 _linktype = _base_linktype;
284 switch (_base_linktype) {
285
286 cell:
287 case FAKE_DLT_ATM_RFC1483:
288 case FAKE_DLT_PPP:
289 case FAKE_DLT_PPP_HDLC:
290 p = _ff.get_packet(DAGCell::CELL_SIZE - DAGCell::HEADER_SIZE, tv.sec(), tv.subsec(), errh);
291 break;
292
293 case FAKE_DLT_C_HDLC:
294 wire_length = htons(*(reinterpret_cast<const uint16_t*>(cell) + 5));
295 goto cell;
296
297 case FAKE_DLT_NONE:
298 _linktype = FAKE_DLT_ATM_RFC1483;
299 goto cell;
300
301 case FAKE_DLT_SUNATM:
302 p = _ff.get_packet_from_data(reinterpret_cast<const uint8_t*>(cell) + 12, 4, DAGCell::CELL_SIZE - 12, tv.sec(), tv.subsec(), errh);
303 break;
304
305 case FAKE_DLT_EN10MB:
306 wire_length = htons(*(reinterpret_cast<const uint16_t*>(cell) + 4));
307 p = _ff.get_packet_from_data(reinterpret_cast<const uint8_t*>(cell) + 10, 6, DAGCell::CELL_SIZE - 10, tv.sec(), tv.subsec(), errh);
308 break;
309
310 default:
311 p = _ff.get_packet_from_data(reinterpret_cast<const uint8_t*>(cell) + 8, 8, DAGCell::CELL_SIZE - 8, tv.sec(), tv.subsec(), errh);
312 break;
313 }
314
315 } else {
316 int read_length = htons(cell->rlen);
317 wire_length = htons(cell->wlen);
318 switch (cell->type) {
319 case DAGCell::TYPE_ATM:
320 case DAGCell::TYPE_AAL5:
321 _linktype = FAKE_DLT_SUNATM;
322 break;
323 case DAGCell::TYPE_ETH:
324 _ff.shift_pos(2);
325 read_length -= 2;
326 wire_length -= 4; // XXX DAG 'wlen' includes CRC
327 _linktype = FAKE_DLT_EN10MB;
328 break;
329 case DAGCell::TYPE_HDLC_POS:
330 _linktype = FAKE_DLT_C_HDLC;
331 break;
332 default: // indicates an old-format dump
333 if (_base_linktype == FAKE_DLT_NONE)
334 _base_linktype = FAKE_DLT_ATM_RFC1483;
335 if (errh) {
336 errh->warning("odd DAG cell type %d, assuming old-style ATM encapsulation", cell->type);
337 errh->message("(To avoid this warning, specify an explicit ENCAP.)");
338 } else
339 click_chatter("%p{element}: DAG cell with odd type %d, assuming old-style\n ATM encapsulation for rest of dump. Packets may have been read incorrectly!\n (To avoid this warning, specify an explicit ENCAP.)", this, cell->type);
340 goto use_base_linktype;
341 }
342 if (read_length < DAGCell::HEADER_SIZE)
343 return false;
344 p = _ff.get_packet(read_length - DAGCell::HEADER_SIZE, tv.sec(), tv.subsec(), errh);
345 }
346
347 // check packet
348 if (!p)
349 return false;
350 if (wire_length)
351 SET_EXTRA_LENGTH_ANNO(p, wire_length - p->length());
352
353 if (_force_ip && !fake_pcap_force_ip(p, _linktype)) {
354 checked_output_push(1, p);
355 goto retry;
356 }
357
358 _packet = p;
359 return more;
360 }
361
362 bool
run_task(Task *)363 FromDAGDump::run_task(Task *)
364 {
365 if (!_active)
366 return false;
367
368 bool more;
369 if (_packet || read_packet(0)) {
370 if (_timing
371 && _packet->timestamp_anno() > Timestamp::now() - _time_offset) {
372 _task.fast_reschedule();
373 return false;
374 }
375 output(0).push(_packet);
376 more = read_packet(0);
377 } else
378 more = false;
379
380 if (more)
381 _task.fast_reschedule();
382 else if (_end_h)
383 _end_h->call_write(ErrorHandler::default_handler());
384 return true;
385 }
386
387 Packet *
pull(int)388 FromDAGDump::pull(int)
389 {
390 if (!_active)
391 return 0;
392
393 bool more;
394 Packet *p;
395 if (_packet || read_packet(0)) {
396 if (_timing
397 && _packet->timestamp_anno() > Timestamp::now() - _time_offset)
398 return 0;
399 p = _packet;
400 more = read_packet(0);
401 } else {
402 p = 0;
403 more = false;
404 }
405
406 if (!more && _end_h)
407 _end_h->call_write(ErrorHandler::default_handler());
408 return p;
409 }
410
411 enum {
412 H_SAMPLING_PROB, H_ACTIVE, H_ENCAP, H_STOP, H_EXTEND_INTERVAL
413 };
414
415 String
read_handler(Element * e,void * thunk)416 FromDAGDump::read_handler(Element *e, void *thunk)
417 {
418 FromDAGDump *fd = static_cast<FromDAGDump *>(e);
419 switch ((intptr_t)thunk) {
420 case H_SAMPLING_PROB:
421 return cp_unparse_real2(fd->_sampling_prob, SAMPLING_SHIFT);
422 case H_ENCAP:
423 return String(fake_pcap_unparse_dlt(fd->_linktype));
424 default:
425 return "<error>";
426 }
427 }
428
429 int
write_handler(const String & s_in,Element * e,void * thunk,ErrorHandler * errh)430 FromDAGDump::write_handler(const String &s_in, Element *e, void *thunk, ErrorHandler *errh)
431 {
432 FromDAGDump *fd = static_cast<FromDAGDump *>(e);
433 String s = cp_uncomment(s_in);
434 switch ((intptr_t)thunk) {
435 case H_ACTIVE: {
436 bool active;
437 if (BoolArg().parse(s, active)) {
438 fd->set_active(active);
439 return 0;
440 } else
441 return errh->error("type mismatch");
442 }
443 case H_STOP:
444 fd->set_active(false);
445 fd->router()->please_stop_driver();
446 return 0;
447 case H_EXTEND_INTERVAL: {
448 Timestamp tv;
449 if (cp_time(s, &tv)) {
450 fd->_last_time += tv;
451 if (fd->_end_h)
452 fd->_have_last_time = true, fd->set_active(true);
453 return 0;
454 } else
455 return errh->error("'extend_interval' takes a time interval");
456 }
457 default:
458 return -EINVAL;
459 }
460 }
461
462 void
add_handlers()463 FromDAGDump::add_handlers()
464 {
465 add_read_handler("sampling_prob", read_handler, H_SAMPLING_PROB);
466 add_data_handlers("active", Handler::f_read | Handler::f_checkbox, &_active);
467 add_write_handler("active", write_handler, H_ACTIVE);
468 add_read_handler("encap", read_handler, H_ENCAP);
469 add_write_handler("stop", write_handler, H_STOP);
470 add_write_handler("extend_interval", write_handler, H_EXTEND_INTERVAL);
471 _ff.add_handlers(this);
472 if (output_is_push(0))
473 add_task_handlers(&_task);
474 }
475
476 CLICK_ENDDECLS
477 ELEMENT_REQUIRES(userlevel int64 FakePcap)
478 EXPORT_ELEMENT(FromDAGDump)
479