// -*- mode: c++; c-basic-offset: 4 -*-
/*
* todump.{cc,hh} -- element writes packets to tcpdump-like file
* John Jannotti, Eddie Kohler
*
* Copyright (c) 1999-2000 Massachusetts Institute of Technology
* Copyright (c) 2000 Mazu Networks, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, subject to the conditions
* listed in the Click LICENSE file. These conditions include: you must
* preserve this copyright notice, and you cannot mention the copyright
* holders in advertising related to the Software without their permission.
* The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
* notice is a summary of the Click LICENSE file; the license in that file is
* legally binding.
*/
#include <click/config.h>
#include <click/glue.hh>
#include "todump.hh"
#include <click/confparse.hh>
#include <click/router.hh>
#if CLICK_NS
# include <click/master.hh>
#endif
#include <click/error.hh>
#include <click/standard/scheduleinfo.hh>
#include <click/packet_anno.hh>
#include "fakepcap.hh"
#include <click/userutils.hh>
CLICK_DECLS
ToDump::ToDump()
: _fp(0), _task(this), _use_encap_from(0)
{
}
ToDump::~ToDump()
{
}
int
ToDump::configure(Vector<String> &conf, ErrorHandler *errh)
{
String encap_type;
String use_encap_from;
_snaplen = 2000;
_extra_length = true;
#if CLICK_NS
bool per_node = false;
#endif
if (cp_va_parse(conf, this, errh,
cpFilename, "dump filename", &_filename,
cpOptional,
cpUnsigned, "max packet length", &_snaplen,
cpWord, "encapsulation type", &encap_type,
cpKeywords,
"SNAPLEN", cpUnsigned, "max packet length", &_snaplen,
"ENCAP", cpWord, "encapsulation type", &encap_type,
"USE_ENCAP_FROM", cpArgument, "use encapsulation from elements", &use_encap_from,
"EXTRA_LENGTH", cpBool, "record extra length?", &_extra_length,
#if CLICK_NS
"PER_NODE", cpBool, "prepend unique node name?", &per_node,
#endif
cpEnd) < 0)
return -1;
if (_snaplen == 0)
_snaplen = 0xFFFFFFFFU;
if (use_encap_from && encap_type)
return errh->error("specify at most one of 'ENCAP' and 'USE_ENCAP_FROM'");
else if (use_encap_from) {
Vector<String> words;
cp_spacevec(use_encap_from, words);
_use_encap_from = new Element *[words.size() + 1];
for (int i = 0; i < words.size(); i++)
if (!(_use_encap_from[i] = router()->find(words[i], this, errh)))
return -1;
_use_encap_from[words.size()] = 0;
if (words.size() == 0)
return errh->error("element names missing after 'USE_ENCAP_FROM'");
} else if (!encap_type)
_linktype = FAKE_DLT_EN10MB;
else if ((_linktype = fake_pcap_parse_dlt(encap_type)) < 0)
return errh->error("bad encapsulation type");
#ifdef CLICK_NS
if (per_node) {
simclick_sim mysiminst = router()->master()->siminst();
char tmp[255];
simclick_sim_get_node_name(mysiminst,tmp,255);
_filename = String(tmp) + String("_") + _filename;
}
#endif
return 0;
}
ToDump *
ToDump::hotswap_element() const
{
if (Element *e = Element::hotswap_element())
if (ToDump *td = (ToDump *)e->cast("ToDump"))
if (td->_filename == _filename
&& td->_linktype == _linktype)
return td;
return 0;
}
int
ToDump::initialize(ErrorHandler *errh)
{
// check _use_encap_from
if (_use_encap_from) {
_linktype = -1;
// collect encap types
Vector<String> encap_types;
for (int i = 0; _use_encap_from[i]; i++) {
const Handler *h = Router::handler(_use_encap_from[i], "encap");
if (!h || !h->readable())
return errh->error("'%{element}' has no 'encap' read handler", _use_encap_from[i]);
encap_types.push_back(cp_uncomment(h->call_read(_use_encap_from[i])));
}
// parse encap types
for (int i = 0; i < encap_types.size(); i++) {
int et = fake_pcap_parse_dlt(encap_types[i]);
if (et < 0)
return errh->error("'%{element}.encap' did not return a valid encapsulation type", _use_encap_from[i]);
else if (_linktype >= 0 && et != _linktype) {
errh->error("source encapsulation types disagree:");
for (int j = 0; j < encap_types.size(); j++)
errh->error(" %s has %s\n", _use_encap_from[j]->declaration().c_str(), encap_types[j].c_str());
return -EINVAL;
} else
_linktype = et;
}
}
// skip initialization if we're hotswapping later
if (!hotswap_element()) {
// prepare files
assert(!_fp);
if (_filename != "-") {
if (compressed_filename(_filename) > 0)
_fp = open_compress_pipe(_filename, errh);
else
_fp = fopen(_filename.c_str(), "wb");
if (!_fp)
return errh->error("%s: %s", _filename.c_str(), strerror(errno));
} else {
_fp = stdout;
_filename = "<stdout>";
}
struct fake_pcap_file_header h;
h.magic = FAKE_PCAP_MAGIC;
h.version_major = FAKE_PCAP_VERSION_MAJOR;
h.version_minor = FAKE_PCAP_VERSION_MINOR;
h.thiszone = 0; // timestamps are in GMT
h.sigfigs = 0; // XXX accuracy of timestamps?
h.snaplen = _snaplen;
h.linktype = _linktype;
size_t wrote_header = fwrite(&h, sizeof(h), 1, _fp);
if (wrote_header != 1)
return errh->error("%s: unable to write file header", _filename.c_str());
}
if (input_is_pull(0) && noutputs() == 0) {
ScheduleInfo::join_scheduler(this, &_task, errh);
_signal = Notifier::upstream_empty_signal(this, 0, &_task);
}
_active = true;
return 0;
}
void
ToDump::take_state(Element *e, ErrorHandler *)
{
ToDump *td = static_cast<ToDump *>(e); // result of hotswap_element()
_fp = td->_fp;
td->_fp = 0;
}
void
ToDump::cleanup(CleanupStage)
{
if (_fp && _fp != stdout)
fclose(_fp);
_fp = 0;
}
void
ToDump::write_packet(Packet *p)
{
struct fake_pcap_pkthdr ph;
const Timestamp& ts = p->timestamp_anno();
if (!ts) {
Timestamp now = Timestamp::now();
ph.ts.tv_sec = now.sec();
ph.ts.tv_usec = now.usec();
} else {
ph.ts.tv_sec = ts.sec();
ph.ts.tv_usec = ts.usec();
}
unsigned to_write = p->length();
ph.len = to_write + (_extra_length ? EXTRA_LENGTH_ANNO(p) : 0);
if (_snaplen && to_write > _snaplen)
to_write = _snaplen;
ph.caplen = to_write;
// XXX writing to pipe?
if (fwrite(&ph, sizeof(ph), 1, _fp) == 0
|| fwrite(p->data(), 1, to_write, _fp) == 0) {
if (errno != EAGAIN) {
_active = false;
click_chatter("ToDump(%s): %s", _filename.c_str(), strerror(errno));
}
}
}
void
ToDump::push(int, Packet *p)
{
if (_active)
write_packet(p);
checked_output_push(0, p);
}
Packet *
ToDump::pull(int)
{
Packet *p = input(0).pull();
if (_active && p)
write_packet(p);
return p;
}
bool
ToDump::run_task()
{
if (!_active)
return false;
Packet *p = input(0).pull();
if (p) {
write_packet(p);
p->kill();
} else if (!_signal)
return false;
_task.fast_reschedule();
return p != 0;
}
enum { H_FILENAME = 0 };
String
ToDump::read_handler(Element *e, void *thunk)
{
ToDump *td = static_cast<ToDump *>(e);
switch ((uintptr_t) thunk) {
case H_FILENAME:
return td->_filename;
default:
return "<error>";
}
}
void
ToDump::add_handlers()
{
add_read_handler("filename", read_handler, (void *)H_FILENAME);
if (input_is_pull(0) && noutputs() == 0)
add_task_handlers(&_task);
}
CLICK_ENDDECLS
ELEMENT_REQUIRES(userlevel|ns FakePcap)
EXPORT_ELEMENT(ToDump)
syntax highlighted by Code2HTML, v. 0.9.1