// -*- mode: c++; c-basic-offset: 4 -*-
/*
* toipsummarydump.{cc,hh} -- element writes packet summary in ASCII
* Eddie Kohler
*
* Copyright (c) 2001-3 International Computer Science Institute
* Copyright (c) 2004 Regents of the University of California
*
* 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 "toipsumdump.hh"
#include <click/standard/scheduleinfo.hh>
#include <click/confparse.hh>
#include <click/error.hh>
#include <click/packet_anno.hh>
#include <clicknet/ip.h>
#include <clicknet/udp.h>
#include <clicknet/tcp.h>
#include <unistd.h>
#include <time.h>
CLICK_DECLS
ToIPSummaryDump::ToIPSummaryDump()
: _f(0), _task(this)
{
}
ToIPSummaryDump::~ToIPSummaryDump()
{
}
void
ToIPSummaryDump::static_initialize()
{
IPSummaryDump::anno_register_unparsers();
IPSummaryDump::ip_register_unparsers();
IPSummaryDump::tcp_register_unparsers();
IPSummaryDump::udp_register_unparsers();
}
void
ToIPSummaryDump::static_cleanup()
{
IPSummaryDump::static_cleanup();
}
int
ToIPSummaryDump::configure(Vector<String> &conf, ErrorHandler *errh)
{
int before = errh->nerrors();
String save = "timestamp ip_src";
bool verbose = false;
bool bad_packets = false;
bool careful_trunc = true;
bool multipacket = false;
bool binary = false;
if (cp_va_parse(conf, this, errh,
cpFilename, "dump filename", &_filename,
cpKeywords,
"CONTENTS", cpArgument, "log contents", &save,
"DATA", cpArgument, "log contents", &save,
"VERBOSE", cpBool, "be verbose?", &verbose,
"BANNER", cpString, "banner", &_banner,
"MULTIPACKET", cpBool, "output multiple packets based on packet count anno?", &multipacket,
"BAD_PACKETS", cpBool, "output '!bad' messages for non-IP or bad IP packets?", &bad_packets,
"CAREFUL_TRUNC", cpBool, "output '!bad' messages for truncated IP packets?", &careful_trunc,
"BINARY", cpBool, "output binary data?", &binary,
cpEnd) < 0)
return -1;
Vector<String> v;
cp_spacevec(save, v);
_binary_size = 4;
for (int i = 0; i < v.size(); i++) {
String word = cp_unquote(v[i]);
const IPSummaryDump::Field* f = IPSummaryDump::find_field(word);
if (!f) {
errh->error("unknown content type '%s'", word.c_str());
continue;
}
// remember field
_fields.push_back(f);
// _prepare_fields
for (int j = 0; j < _prepare_fields.size(); j++)
if (_prepare_fields[j]->prepare == f->prepare)
goto found_prepare;
if (f->prepare)
_prepare_fields.push_back(f);
// binary size
found_prepare:
int s = f->binary_size();
if ((s < 0 || !f->outb) && binary)
errh->error("cannot use CONTENTS %s with BINARY", word.c_str());
_binary_size += s;
// remove _multipacket if packet count specified
if (strcmp(f->name, "count") == 0)
_multipacket = false;
}
if (_fields.size() == 0)
errh->error("no contents specified");
_verbose = verbose;
_bad_packets = bad_packets;
_careful_trunc = careful_trunc;
_multipacket = multipacket;
_binary = binary;
return (before == errh->nerrors() ? 0 : -1);
}
int
ToIPSummaryDump::initialize(ErrorHandler *errh)
{
assert(!_f);
if (_filename != "-") {
_f = fopen(_filename.c_str(), "wb");
if (!_f)
return errh->error("%s: %s", _filename.c_str(), strerror(errno));
} else {
_f = stdout;
_filename = "<stdout>";
}
if (input_is_pull(0)) {
ScheduleInfo::join_scheduler(this, &_task, errh);
_signal = Notifier::upstream_empty_signal(this, 0, &_task);
}
_active = true;
_output_count = 0;
// magic number
StringAccum sa;
sa << "!IPSummaryDump " << IPSummaryDump::MAJOR_VERSION << '.' << IPSummaryDump::MINOR_VERSION << '\n';
if (_banner)
sa << "!creator " << cp_quote(_banner) << '\n';
// host and start time
if (_verbose) {
char buf[BUFSIZ];
buf[BUFSIZ - 1] = '\0'; // ensure NUL-termination
if (gethostname(buf, BUFSIZ - 1) >= 0)
sa << "!host " << buf << '\n';
time_t when = time(0);
const char *cwhen = ctime(&when);
struct timeval tv;
if (gettimeofday(&tv, 0) >= 0)
sa << "!runtime " << tv << " (" << String(cwhen, strlen(cwhen) - 1) << ")\n";
}
// data description
sa << "!data ";
for (int i = 0; i < _fields.size(); i++)
sa << (i ? " " : "")
<< (strcmp(_fields[i]->name, "ntimestamp") == 0 && !_binary ? "timestamp" : _fields[i]->name);
sa << '\n';
// binary marker
if (_binary)
sa << "!binary\n";
// print output
fwrite(sa.data(), 1, sa.length(), _f);
return 0;
}
void
ToIPSummaryDump::cleanup(CleanupStage)
{
if (_f && _f != stdout)
fclose(_f);
_f = 0;
}
bool
ToIPSummaryDump::summary(Packet* p, StringAccum& sa, StringAccum* bad_sa, bool force_extra_length) const
{
IPSummaryDump::PacketDesc d(p, &sa, bad_sa, _careful_trunc, force_extra_length);
for (int i = 0; i < _prepare_fields.size(); i++)
_prepare_fields[i]->prepare(d);
if (_binary) {
sa.extend(4);
for (int i = 0; i < _fields.size(); i++) {
d.clear_values();
bool ok = _fields[i]->extract(d, _fields[i]->thunk);
_fields[i]->outb(d, ok, _fields[i]->thunk);
}
*(reinterpret_cast<uint32_t*>(sa.data())) = htonl(sa.length());
} else {
for (int i = 0; i < _fields.size(); i++) {
if (i)
sa << ' ';
if (_fields[i]->extract(d, _fields[i]->thunk) && _fields[i]->outa)
_fields[i]->outa(d, _fields[i]->thunk);
else
sa << '-';
}
sa << '\n';
}
return true;
}
void
ToIPSummaryDump::write_packet(Packet* p, int multipacket)
{
if (multipacket > 0 && EXTRA_PACKETS_ANNO(p) > 0) {
uint32_t count = 1 + EXTRA_PACKETS_ANNO(p);
uint32_t total_len = p->length() + EXTRA_LENGTH_ANNO(p);
uint32_t len = p->length();
if (total_len < count * len)
total_len = count * len;
// do timestamp stepping
Timestamp end_timestamp = p->timestamp_anno();
Timestamp timestamp_delta;
if (FIRST_TIMESTAMP_ANNO(p)) {
timestamp_delta = (end_timestamp - FIRST_TIMESTAMP_ANNO(p)) / (count - 1);
p->set_timestamp_anno(FIRST_TIMESTAMP_ANNO(p));
} else
timestamp_delta = Timestamp();
SET_EXTRA_PACKETS_ANNO(p, 0);
for (uint32_t i = count; i > 0; i--) {
uint32_t l = total_len / i;
SET_EXTRA_LENGTH_ANNO(p, l - len);
total_len -= l;
write_packet(p, -1);
if (i == 1)
p->timestamp_anno() = end_timestamp;
else
p->timestamp_anno() += timestamp_delta;
}
} else {
_sa.clear();
_bad_sa.clear();
summary(p, _sa, (_bad_packets ? &_bad_sa : 0), (multipacket < 0 || EXTRA_PACKETS_ANNO(p) > 0));
if (_bad_packets && _bad_sa)
write_line(_bad_sa.take_string());
fwrite(_sa.data(), 1, _sa.length(), _f);
_output_count++;
}
}
void
ToIPSummaryDump::push(int, Packet *p)
{
if (_active)
write_packet(p, _multipacket);
p->kill();
}
bool
ToIPSummaryDump::run_task()
{
if (!_active)
return false;
if (Packet *p = input(0).pull()) {
write_packet(p, _multipacket);
p->kill();
_task.fast_reschedule();
return true;
} else if (_signal) {
_task.fast_reschedule();
return false;
} else
return false;
}
void
ToIPSummaryDump::write_line(const String& s)
{
if (s.length()) {
assert(s.back() == '\n');
if (_binary) {
uint32_t marker = htonl(s.length() | 0x80000000U);
fwrite(&marker, 4, 1, _f);
}
fwrite(s.data(), 1, s.length(), _f);
}
}
void
ToIPSummaryDump::add_note(const String &s)
{
if (s.length()) {
int extra = 1 + (s.back() == '\n' ? 0 : 1);
if (_binary) {
uint32_t marker = htonl((s.length() + extra) | 0x80000000U);
fwrite(&marker, 4, 1, _f);
}
fputc('#', _f);
fwrite(s.data(), 1, s.length(), _f);
if (extra > 1)
fputc('\n', _f);
}
}
int
ToIPSummaryDump::flush_handler(const String &, Element *e, void *, ErrorHandler *)
{
ToIPSummaryDump *tod = (ToIPSummaryDump *) e;
if (tod->_f)
fflush(tod->_f);
return 0;
}
void
ToIPSummaryDump::add_handlers()
{
if (input_is_pull(0))
add_task_handlers(&_task);
add_write_handler("flush", flush_handler, 0);
}
ELEMENT_REQUIRES(userlevel IPSummaryDump IPSummaryDump_Anno IPSummaryDump_IP IPSummaryDump_TCP)
EXPORT_ELEMENT(ToIPSummaryDump)
CLICK_ENDDECLS
syntax highlighted by Code2HTML, v. 0.9.1