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