// -*- mode: c++; c-basic-offset: 4 -*-
/*
 * fromipsumdump.{cc,hh} -- element reads packets from IP summary dump file
 * Eddie Kohler
 *
 * Copyright (c) 2001 International Computer Science Institute
 *
 * 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 "fromipsumdump.hh"
#include <click/confparse.hh>
#include <click/router.hh>
#include <click/standard/scheduleinfo.hh>
#include <click/error.hh>
#include <click/glue.hh>
#include <click/straccum.hh>
#include <clicknet/ip.h>
#include <clicknet/udp.h>
#include <clicknet/tcp.h>
#include <click/packet_anno.hh>
#include <click/userutils.hh>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
CLICK_DECLS

#ifdef i386
# define GET4(p)	ntohl(*reinterpret_cast<const uint32_t *>((p)))
# define GET2(p)	ntohs(*reinterpret_cast<const uint16_t *>((p)))
#else
# define GET4(p)	(((p)[0]<<24) | ((p)[1]<<16) | ((p)[2]<<8) | (p)[3])
# define GET2(p)	(((p)[0]<<8) | (p)[1])
#endif
#define GET1(p)		((p)[0])

FromIPSummaryDump::FromIPSummaryDump()
    : _work_packet(0), _task(this)
{
    _ff.set_landmark_pattern("%f:%l");
}

FromIPSummaryDump::~FromIPSummaryDump()
{
}

void *
FromIPSummaryDump::cast(const char *n)
{
    if (strcmp(n, Notifier::EMPTY_NOTIFIER) == 0 && !output_is_push(0)) {
	_notifier.initialize(router());
	return static_cast<Notifier *>(&_notifier);
    } else
	return Element::cast(n);
}

int
FromIPSummaryDump::configure(Vector<String> &conf, ErrorHandler *errh)
{
    bool stop = false, active = true, zero = true, checksum = false, multipacket = false;
    uint8_t default_proto = IP_PROTO_TCP;
    _sampling_prob = (1 << SAMPLING_SHIFT);
    String default_contents, default_flowid;
    
    if (cp_va_parse(conf, this, errh,
		    cpFilename, "dump file name", &_ff.filename(),
		    cpKeywords,
		    "STOP", cpBool, "stop driver when done?", &stop,
		    "ACTIVE", cpBool, "start active?", &active,
		    "ZERO", cpBool, "zero packet data?", &zero,
		    "CHECKSUM", cpBool, "set packet checksums?", &checksum,
		    "SAMPLE", cpUnsignedReal2, "sampling probability", SAMPLING_SHIFT, &_sampling_prob,
		    "PROTO", cpByte, "default IP protocol", &default_proto,
		    "MULTIPACKET", cpBool, "generate multiple packets per record?", &multipacket,
		    "DEFAULT_CONTENTS", cpArgument, "default contents of log", &default_contents,
		    "DEFAULT_FLOWID", cpArgument, "default flow ID", &default_flowid,
		    "CONTENTS", cpArgument, "default contents of log", &default_contents,
		    "FLOWID", cpArgument, "default flow ID", &default_flowid,
		    cpEnd) < 0)
	return -1;
    if (_sampling_prob > (1 << SAMPLING_SHIFT)) {
	errh->warning("SAMPLE probability reduced to 1");
	_sampling_prob = (1 << SAMPLING_SHIFT);
    } else if (_sampling_prob == 0)
	errh->warning("SAMPLE probability is 0; emitting no packets");

    _default_proto = default_proto;
    _stop = stop;
    _active = active;
    _zero = zero;
    _checksum = checksum;
    _multipacket = multipacket;
    _have_flowid = _have_aggregate = _use_flowid = _use_aggregate = _binary = false;
    if (default_contents)
	bang_data(default_contents, errh);
    if (default_flowid)
	bang_flowid(default_flowid, 0, errh);
    return 0;
}

int
FromIPSummaryDump::read_binary(String &result, ErrorHandler *errh)
{
    assert(_binary);

    uint8_t record_storage[4];
    const uint8_t *record = _ff.get_unaligned(4, record_storage, errh);
    if (!record)
	return 0;
    int record_length = GET4(record) & 0x7FFFFFFFU;
    if (record_length < 4)
	return _ff.error(errh, "binary record too short");
    bool textual = (record[0] & 0x80 ? true : false);
    result = _ff.get_string(record_length - 4, errh);
    if (!result)
	return 0;
    if (textual) {
	const char *s = result.begin(), *e = result.end();
	while (e > s && e[-1] == 0)
	    e--;
	if (e != result.end())
	    result = result.substring(s, e);
    }
    _ff.set_lineno(_ff.lineno() + 1);
    return (textual ? 2 : 1);
}

int
FromIPSummaryDump::initialize(ErrorHandler *errh)
{
    // make sure notifier is initialized
    if (!output_is_push(0))
	_notifier.initialize(router());
    
    if (_ff.initialize(errh) < 0)
	return -1;
    
    _minor_version = IPSummaryDump::MINOR_VERSION; // expected minor version
    String line;
    if (_ff.peek_line(line, errh) < 0)
	return -1;
    else if (line.substring(0, 14) == "!IPSummaryDump") {
	int major_version;
	if (sscanf(line.c_str() + 14, " %d.%d", &major_version, &_minor_version) == 2) {
	    if (major_version != IPSummaryDump::MAJOR_VERSION || _minor_version > IPSummaryDump::MINOR_VERSION) {
		_ff.warning(errh, "unexpected IPSummaryDump version %d.%d", major_version, _minor_version);
		_minor_version = IPSummaryDump::MINOR_VERSION;
	    }
	}
	(void) _ff.read_line(line, errh); // throw away line
    } else {
	// parse line again, warn if this doesn't look like a dump
	if (line.substring(0, 8) != "!creator" && line.substring(0, 5) != "!data" && line.substring(0, 9) != "!contents") {
	    if (!_contents.size() /* don't warn on DEFAULT_CONTENTS */)
		_ff.warning(errh, "missing banner line; is this an IP summary dump?");
	}
    }
    
    _format_complaint = false;
    if (output_is_push(0))
	ScheduleInfo::initialize_task(this, &_task, _active, errh);
    return 0;
}

void
FromIPSummaryDump::cleanup(CleanupStage)
{
    _ff.cleanup();
    if (_work_packet)
	_work_packet->kill();
    _work_packet = 0;
}

void
FromIPSummaryDump::bang_data(const String &line, ErrorHandler *errh)
{
    Vector<String> words;
    cp_spacevec(line, words);

    _contents.clear();
    uint32_t all_contents = 0;
    for (int i = 0; i < words.size(); i++) {
	String word = cp_unquote(words[i]);
	int what = parse_content(word);
	if (what >= W_NONE && what < W_LAST) {
	    _contents.push_back(what);
	    all_contents |= (1 << (what - W_NONE));
	} else if (i > 0 || (word != "!data" && word != "!contents")) {
	    _ff.warning(errh, "unknown content type '%s'", word.c_str());
	    _contents.push_back(W_NONE);
	}
    }

    if (_contents.size() == 0)
	_ff.error(errh, "no contents specified");

    // If we have W_IP_FRAGOFF, ignore W_IP_FRAG.
    if (all_contents & (1 << (W_IP_FRAGOFF - W_NONE)))
	for (int i = 0; i < _contents.size(); i++)
	    if (_contents[i] == W_IP_FRAG)
		_contents[i] = W_NONE;

    // recheck whether to use '!flowid' and '!aggregate'
    check_defaults();
}

void
FromIPSummaryDump::check_defaults()
{
    _use_flowid = false;
    _flowid = (_have_flowid ? _given_flowid : IPFlowID());
    _use_aggregate = _have_aggregate;
    for (int i = 0; i < _contents.size(); i++)
	if (_contents[i] == W_IP_SRC)
	    _flowid.set_saddr(IPAddress());
	else if (_contents[i] == W_IP_DST)
	    _flowid.set_daddr(IPAddress());
	else if (_contents[i] == W_SPORT)
	    _flowid.set_sport(0);
	else if (_contents[i] == W_DPORT)
	    _flowid.set_dport(0);
	else if (_contents[i] == W_AGGREGATE)
	    _use_aggregate = false;
    if (_flowid || _flowid.sport() || _flowid.dport())
	_use_flowid = true;
}

void
FromIPSummaryDump::bang_flowid(const String &line, click_ip *iph,
			       ErrorHandler *errh)
{
    Vector<String> words;
    cp_spacevec(line, words);

    IPAddress src, dst;
    uint32_t sport = 0, dport = 0, proto = 0;
    if (words.size() < 5
	|| (!cp_ip_address(words[1], &src) && words[1] != "-")
	|| (!cp_unsigned(words[2], &sport) && words[2] != "-")
	|| (!cp_ip_address(words[3], &dst) && words[3] != "-")
	|| (!cp_unsigned(words[4], &dport) && words[4] != "-")
	|| sport > 65535 || dport > 65535) {
	_ff.error(errh, "bad !flowid specification");
	_have_flowid = _use_flowid = false;
    } else {
	if (words.size() >= 6) {
	    if (cp_unsigned(words[5], &proto) && proto < 256)
		_default_proto = proto;
	    else if (words[5] == "T")
		_default_proto = IP_PROTO_TCP;
	    else if (words[5] == "U")
		_default_proto = IP_PROTO_UDP;
	    else if (words[5] == "I")
		_default_proto = IP_PROTO_ICMP;
	    else
		_ff.error(errh, "bad protocol in !flowid");
	}
	_given_flowid = IPFlowID(src, htons(sport), dst, htons(dport));
	_have_flowid = true;
	check_defaults();
	if (iph)
	    iph->ip_p = _default_proto;
    }
}

void
FromIPSummaryDump::bang_aggregate(const String &line, ErrorHandler *errh)
{
    Vector<String> words;
    cp_spacevec(line, words);

    if (words.size() != 2
	|| !cp_unsigned(words[1], &_aggregate)) {
	_ff.error(errh, "bad !aggregate specification");
	_have_aggregate = _use_aggregate = false;
    } else {
	_have_aggregate = true;
	check_defaults();
    }
}

void
FromIPSummaryDump::bang_binary(const String &line, ErrorHandler *errh)
{
    Vector<String> words;
    cp_spacevec(line, words);
    if (words.size() != 1)
	_ff.error(errh, "bad !binary specification");
    for (int i = 0; i < _contents.size(); i++)
	if (content_binary_size(_contents[i]) < 0) {
	    _ff.error(errh, "contents incompatible with !binary");
	    // XXX _pos = 0xFFFFFFFFU;	// prevent reading more data
	}
    _binary = true;
    _ff.set_landmark_pattern("%f:record %l");
    _ff.set_lineno(1);
}

static void
append_net_uint32_t(StringAccum &sa, uint32_t u)
{
    sa << (char)(u >> 24) << (char)(u >> 16) << (char)(u >> 8) << (char)u;
}

const unsigned char *
FromIPSummaryDump::parse_ip_opt_ascii(const unsigned char *begin, const unsigned char *end, String *result, int contents)
{
    StringAccum sa;
    const unsigned char *s = begin;
    
    while (1) {
	const unsigned char *t;
	uint32_t u1;

	if (s + 3 < end && memcmp(s, "rr{", 3) == 0
	    && (contents & DO_IPOPT_ROUTE)) {
	    // record route
	    sa << (char)IPOPT_RR;
	    s += 3;
	  parse_route:
	    int sa_pos = sa.length() - 1;
	    int pointer = -1;
	    sa << '\0' << '\0';
	    // loop over entries
	    while (1) {
		if (s < end && *s == '^' && pointer < 0)
		    pointer = sa.length() - sa_pos + 1, s++;
		if (s >= end || !isdigit(*s))
		    break;
		for (int i = 0; i < 4; i++) {
		    u1 = 256;
		    s = cp_unsigned(s, end, 10, &u1) + (i < 3);
		    if (u1 > 255 || (i < 3 && (s > end || s[-1] != '.')))
			goto bad_opt;
		    sa << (char)u1;
		}
		if (s < end && *s == ',')
		    s++;
	    }
	    if (s >= end || *s != '}') // must end with a brace
		goto bad_opt;
	    sa[sa_pos + 2] = (pointer >= 0 ? pointer : sa.length() - sa_pos + 1);
	    if (s + 2 < end && s[1] == '+' && isdigit(s[2])) {
		s = cp_unsigned(s + 2, end, 10, &u1);
		if (u1 < 64)
		    sa.append_fill('\0', u1 * 4);
	    } else
		s++;
	    if (sa.length() - sa_pos > 255)
		goto bad_opt;
	    sa[sa_pos + 1] = sa.length() - sa_pos;
	    
	} else if (s + 5 < end && memcmp(s, "ssrr{", 5) == 0
		   && (contents & DO_IPOPT_ROUTE)) {
	    // strict source route option
	    sa << (char)IPOPT_SSRR;
	    s += 5;
	    goto parse_route;
	    
	} else if (s + 5 < end && memcmp(s, "lsrr{", 5) == 0
		   && (contents & DO_IPOPT_ROUTE)) {
	    // loose source route option
	    sa << (char)IPOPT_LSRR;
	    s += 5;
	    goto parse_route;
	    
	} else if (s + 3 < end
		   && (memcmp(s, "ts{", 3) == 0 || memcmp(s, "ts.", 3) == 0)
		   && (contents & DO_IPOPT_TS)) {
	    // timestamp option
	    int sa_pos = sa.length();
	    sa << (char)IPOPT_TS << (char)0 << (char)0 << (char)0;
	    uint32_t top_bit;
	    int flag = -1;
	    if (s[2] == '.') {
		if (s + 6 < end && memcmp(s + 3, "ip{", 3) == 0)
		    flag = 1, s += 6;
		else if (s + 9 < end && memcmp(s + 3, "preip{", 6) == 0)
		    flag = 3, s += 9;
		else if (isdigit(s[3])
			 && (t = cp_unsigned(s + 3, end, 0, (uint32_t *)&flag))
			 && flag <= 15 && t < end && *t == '{')
		    s = t + 1;
		else
		    goto bad_opt;
	    } else
		s += 3;
	    int pointer = -1;
	    
	    // loop over timestamp entries
	    while (1) {
		if (s < end && *s == '^' && pointer < 0)
		    pointer = sa.length() - sa_pos + 1, s++;
		if (s >= end || (!isdigit(*s) && *s != '!'))
		    break;
		const unsigned char *entry = s;
		
	      retry_entry:
		if (flag == 1 || flag == 3 || flag == -2) {
		    // parse IP address
		    for (int i = 0; i < 4; i++) {
			u1 = 256;
			s = cp_unsigned(s, end, 10, &u1) + (i < 3);
			if (u1 > 255 || (i < 3 && (s > end || s[-1] != '.')))
			    goto bad_opt;
			sa << (char)u1;
		    }
		    // prespecified IPs if we get here
		    if (pointer >= 0 && flag == -2)
			flag = 3;
		    // check for valid value: either "=[DIGIT]", "=!", "=?"
		    // (for pointer >= 0)
		    if (s + 1 < end && *s == '=') {
			if (isdigit(s[1]) || s[1] == '!')
			    s++;
			else if (s[1] == '?' && pointer >= 0) {
			    sa << (char)0 << (char)0 << (char)0 << (char)0;
			    s += 2;
			    goto done_entry;
			} else
			    goto bad_opt;
		    } else if (pointer >= 0) {
			sa << (char)0 << (char)0 << (char)0 << (char)0;
			goto done_entry;
		    } else
			goto bad_opt;
		}
		
		// parse timestamp value
		assert(s < end);
		top_bit = 0;
		if (*s == '!')
		    top_bit = 0x80000000U, s++;
		if (s >= end || !isdigit(*s))
		    goto bad_opt;
		s = cp_unsigned(s, end, 0, &u1);
		if (s < end && *s == '.' && flag == -1) {
		    flag = -2;
		    s = entry;
		    goto retry_entry;
		} else if (flag == -1)
		    flag = 0;
		u1 |= top_bit;
		append_net_uint32_t(sa, u1);
	      done_entry:
		// check separator
		if (s < end && *s == ',')
		    s++;
	    }
	    
	    // done with entries
	    if (s < end && *s++ != '}')
		goto bad_opt;
	    if (flag == -2)
		flag = 1;
	    sa[sa_pos + 2] = (pointer >= 0 ? pointer : sa.length() - sa_pos + 1);
	    if (s + 1 < end && *s == '+' && isdigit(s[1])
		&& (s = cp_unsigned(s + 1, end, 0, &u1))
		&& u1 < 64)
		sa.append_fill('\0', u1 * (flag == 1 || flag == 3 ? 8 : 4));
	    int overflow = 0;
	    if (s + 2 < end && *s == '+' && s[1] == '+' && isdigit(s[2])
		&& (s = cp_unsigned(s + 2, end, 0, &u1))
		&& u1 < 16)
		overflow = u1;
	    sa[sa_pos + 3] = (overflow << 4) | flag;
	    if (sa.length() - sa_pos > 255)
		goto bad_opt;
	    sa[sa_pos + 1] = sa.length() - sa_pos;
	    
	} else if (s < end && isdigit(*s) && (contents & DO_IPOPT_UNKNOWN)) {
	    // unknown option
	    s = cp_unsigned(s, end, 0, &u1);
	    if (u1 >= 256)
		goto bad_opt;
	    sa << (char)u1;
	    if (s + 1 < end && *s == '=' && isdigit(s[1])) {
		int pos0 = sa.length();
		sa << (char)0;
		do {
		    s = cp_unsigned(s + 1, end, 0, &u1);
		    if (u1 >= 256)
			goto bad_opt;
		    sa << (char)u1;
		} while (s + 1 < end && *s == ':' && isdigit(s[1]));
		if (sa.length() > pos0 + 254)
		    goto bad_opt;
		sa[pos0] = (char)(sa.length() - pos0 + 1);
	    }
	} else if (s + 3 <= end && memcmp(s, "nop", 3) == 0
		   && (contents & DO_IPOPT_PADDING)) {
	    sa << (char)IPOPT_NOP;
	    s += 3;
	} else if (s + 3 <= end && memcmp(s, "eol", 3) == 0
		   && (contents & DO_IPOPT_PADDING)
		   && (s + 3 == end || s[3] != ',')) {
	    sa << (char)IPOPT_EOL;
	    s += 3;
	} else
	    goto bad_opt;

	if (s >= end || isspace(*s)) {
	    // check for improper padding
	    while (sa.length() > 40 && sa[0] == TCPOPT_NOP) {
		memmove(&sa[0], &sa[1], sa.length() - 1);
		sa.pop_back();
	    }
	    // options too long?
	    if (sa.length() > 40)
		goto bad_opt;
	    // otherwise ok
	    *result = sa.take_string();
	    return s;
	} else if (*s != ',' && *s != ';')
	    goto bad_opt;

	s++;
    }

  bad_opt:
    *result = String();
    return begin;
}

const unsigned char *
FromIPSummaryDump::parse_tcp_opt_ascii(const unsigned char *begin, const unsigned char *end, String *result, int contents)
{
    StringAccum sa;
    const unsigned char *s = begin;
    
    while (1) {
	uint32_t u1, u2;

	if (s + 3 < end && memcmp(s, "mss", 3) == 0
	    && (contents & DO_TCPOPT_MSS)) {
	    u1 = 0x10000U;	// bad value
	    s = cp_unsigned(s + 3, end, 0, &u1);
	    if (u1 <= 0xFFFFU)
		sa << (char)TCPOPT_MAXSEG << (char)TCPOLEN_MAXSEG << (char)(u1 >> 8) << (char)u1;
	    else
		goto bad_opt;
	} else if (s + 6 < end && memcmp(s, "wscale", 6) == 0
		   && (contents & DO_TCPOPT_WSCALE)) {
	    u1 = 256;		// bad value
	    s = cp_unsigned(s + 6, end, 0, &u1);
	    if (u1 <= 255)
		sa << (char)TCPOPT_WSCALE << (char)TCPOLEN_WSCALE << (char)u1;
	    else
		goto bad_opt;
	} else if (s + 6 <= end && memcmp(s, "sackok", 6) == 0
		   && (contents & DO_TCPOPT_SACK)) {
	    sa << (char)TCPOPT_SACK_PERMITTED << (char)TCPOLEN_SACK_PERMITTED;
	    s += 6;
	} else if (s + 4 < end && memcmp(s, "sack", 4) == 0
		   && (contents & DO_TCPOPT_SACK)) {
	    // combine adjacent SACK options into a block
	    int sa_pos = sa.length();
	    sa << (char)TCPOPT_SACK << (char)0;
	    s += 4;
	    while (1) {
		const unsigned char *t = cp_unsigned(s, end, 0, &u1);
		if (t >= end || (*t != ':' && *t != '-'))
		    goto bad_opt;
		t = cp_unsigned(t + 1, end, 0, &u2);
		append_net_uint32_t(sa, u1);
		append_net_uint32_t(sa, u2);
		if (t < s + 3) // at least 1 digit in each block
		    goto bad_opt;
		s = t;
		if (s + 5 >= end || memcmp(s, ",sack", 5) != 0)
		    break;
		s += 5;
	    }
	    sa[sa_pos + 1] = (char)(sa.length() - sa_pos);
	} else if (s + 2 < end && memcmp(s, "ts", 2) == 0
		   && (contents & DO_TCPOPT_TIMESTAMP)) {
	    const unsigned char *t = cp_unsigned(s + 2, end, 0, &u1);
	    if (t >= end || *t != ':')
		goto bad_opt;
	    t = cp_unsigned(t + 1, end, 0, &u2);
	    if (sa.length() == 0)
		sa << (char)TCPOPT_NOP << (char)TCPOPT_NOP;
	    sa << (char)TCPOPT_TIMESTAMP << (char)TCPOLEN_TIMESTAMP;
	    append_net_uint32_t(sa, u1);
	    append_net_uint32_t(sa, u2);
	    if (t < s + 5)	// at least 1 digit in each block
		goto bad_opt;
	    s = t;
	} else if (s < end && isdigit(*s)
		   && (contents & DO_TCPOPT_UNKNOWN)) {
	    s = cp_unsigned(s, end, 0, &u1);
	    if (u1 >= 256)
		goto bad_opt;
	    sa << (char)u1;
	    if (s + 1 < end && *s == '=' && isdigit(s[1])) {
		int pos0 = sa.length();
		sa << (char)0;
		do {
		    s = cp_unsigned(s + 1, end, 0, &u1);
		    if (u1 >= 256)
			goto bad_opt;
		    sa << (char)u1;
		} while (s + 1 < end && *s == ':' && isdigit(s[1]));
		if (sa.length() > pos0 + 254)
		    goto bad_opt;
		sa[pos0] = (char)(sa.length() - pos0 + 1);
	    }
	} else if (s + 3 <= end && memcmp(s, "nop", 3) == 0
		   && (contents & DO_TCPOPT_PADDING)) {
	    sa << (char)TCPOPT_NOP;
	    s += 3;
	} else if (s + 3 <= end && strncmp((const char *) s, "eol", 3) == 0
		   && (contents & DO_TCPOPT_PADDING)
		   && (s + 3 == end || s[3] != ',')) {
	    sa << (char)TCPOPT_EOL;
	    s += 3;
	} else
	    goto bad_opt;

	if (s >= end || isspace(*s)) {
	    // check for improper padding
	    while (sa.length() > 40 && sa[0] == TCPOPT_NOP) {
		memmove(&sa[0], &sa[1], sa.length() - 1);
		sa.pop_back();
	    }
	    // options too long?
	    if (sa.length() > 40)
		goto bad_opt;
	    // otherwise ok
	    *result = sa.take_string();
	    return s;
	} else if (*s != ',' && *s != ';')
	    goto bad_opt;

	s++;
    }

  bad_opt:
    *result = String();
    return begin;
}

static WritablePacket *
handle_ip_opt(WritablePacket *q, const String &optstr)
{
    int olen = (optstr.length() + 3) & ~3;
    if (!(q = q->put(olen)))
	return 0;
    memmove(q->transport_header() + olen, q->transport_header(), sizeof(click_tcp));
    q->ip_header()->ip_hl = (sizeof(click_ip) + olen) >> 2;
    memcpy(q->ip_header() + 1, optstr.data(), optstr.length());
    if (optstr.length() & 3)
	*(reinterpret_cast<uint8_t *>(q->ip_header() + 1) + optstr.length()) = IPOPT_EOL;
    q->set_ip_header(q->ip_header(), sizeof(click_ip) + olen);
    return q;
}

static WritablePacket *
handle_tcp_opt(WritablePacket *q, const String &optstr)
{
    int olen = (optstr.length() + 3) & ~3;
    if (!(q = q->put(olen)))
	return 0;
    q->tcp_header()->th_off = (sizeof(click_tcp) + olen) >> 2;
    memcpy(q->tcp_header() + 1, optstr.data(), optstr.length());
    if (optstr.length() & 3)
	*(reinterpret_cast<uint8_t *>(q->tcp_header() + 1) + optstr.length()) = TCPOPT_EOL;
    return q;
}

static void
set_checksums(WritablePacket *q, click_ip *iph)
{
    assert(iph == q->ip_header());
    
    iph->ip_sum = 0;
    iph->ip_sum = click_in_cksum((uint8_t *)iph, iph->ip_hl << 2);

    if (IP_ISFRAG(iph))
	/* nada */;
    else if (iph->ip_p == IP_PROTO_TCP) {
	click_tcp *tcph = q->tcp_header();
	tcph->th_sum = 0;
	unsigned csum = click_in_cksum((uint8_t *)tcph, q->transport_length());
	tcph->th_sum = click_in_cksum_pseudohdr(csum, iph, q->transport_length());
    } else if (iph->ip_p == IP_PROTO_UDP) {
	click_udp *udph = q->udp_header();
	udph->uh_sum = 0;
	unsigned csum = click_in_cksum((uint8_t *)udph, q->transport_length());
	udph->uh_sum = click_in_cksum_pseudohdr(csum, iph, q->transport_length());
    }
}

Packet *
FromIPSummaryDump::read_packet(ErrorHandler *errh)
{
    WritablePacket *q = Packet::make(0, (const unsigned char *)0, sizeof(click_ip) + sizeof(click_tcp), 8);
    if (!q) {
	_ff.error(errh, strerror(ENOMEM));
	return 0;
    }
    if (_zero)
	memset(q->data(), 0, q->length());
    q->set_ip_header((click_ip *)q->data(), sizeof(click_ip));
    click_ip *iph = q->ip_header();
    iph->ip_v = 4;
    iph->ip_hl = sizeof(click_ip) >> 2;
    iph->ip_p = _default_proto;
    iph->ip_off = 0;
    
    String line;
    StringAccum payload;
    String ip_opt;
    String tcp_opt;

    while (1) {

	bool binary = _binary;

	if (binary) {
	    int result = read_binary(line, errh);
	    if (result <= 0) {
		q->kill();
		return 0;
	    } else
		binary = (result == 1);
	} else if (_ff.read_line(line, errh) <= 0) {
	    q->kill();
	    return 0;
	}

	const unsigned char *data = (const unsigned char *) line.begin();
	const unsigned char *end = (const unsigned char *) line.end();

	if (data == end)
	    continue;
	else if (!binary && data[0] == '!') {
	    if (data + 6 <= end && memcmp(data, "!data", 5) == 0 && isspace(data[5]))
		bang_data(line, errh);
	    else if (data + 8 <= end && memcmp(data, "!flowid", 7) == 0 && isspace(data[7]))
		bang_flowid(line, iph, errh);
	    else if (data + 11 <= end && memcmp(data, "!aggregate", 10) == 0 && isspace(data[10]))
		bang_aggregate(line, errh);
	    else if (data + 8 <= end && memcmp(data, "!binary", 7) == 0 && isspace(data[7]))
		bang_binary(line, errh);
	    else if (data + 10 <= end && memcmp(data, "!contents", 9) == 0 && isspace(data[9]))
		bang_data(line, errh);
	    continue;
	} else if (!binary && data[0] == '#')
	    continue;

	int ok = (binary ? 1 : 0);
	int ip_ok = 0;
	uint32_t byte_count = 0;
	uint32_t payload_len = 0;
	bool have_payload_len = false;
	bool have_payload = false;
	bool have_ip_opt = false;
	bool have_tcp_opt = false;
	
	for (int i = 0; data < end && i < _contents.size(); i++) {
	    const unsigned char *original_data = data;
	    const unsigned char *next;
	    uint32_t u1 = 0, u2 = 0;

	    // check binary case
	    if (binary) {
		switch (_contents[i]) {
		  case W_NONE:
		    break;
		  case W_TIMESTAMP:
		  case W_FIRST_TIMESTAMP:
		    u1 = GET4(data);
		    u2 = GET4(data + 4) * 1000;
		    data += 8;
		    break;
		  case W_NTIMESTAMP:
		  case W_FIRST_NTIMESTAMP:
		    u1 = GET4(data);
		    u2 = GET4(data + 4);
		    data += 8;
		    break;
		  case W_TIMESTAMP_USEC1:
		    u1 = GET4(data);
		    u2 = GET4(data + 4);
		    data += 8;
		    break;
		  case W_TIMESTAMP_SEC:
		  case W_TIMESTAMP_USEC:
		  case W_IP_LEN:
		  case W_PAYLOAD_LEN:
		  case W_IP_CAPTURE_LEN:
		  case W_TCP_SEQ:
		  case W_TCP_ACK:
		  case W_COUNT:
		  case W_AGGREGATE:
		  case W_IP_SRC:
		  case W_IP_DST:
		    u1 = GET4(data);
		    data += 4;
		    break;
		  case W_IP_ID:
		  case W_SPORT:
		  case W_DPORT:
		  case W_IP_FRAGOFF:
		  case W_TCP_WINDOW:
		  case W_TCP_URP:
		    u1 = GET2(data);
		    data += 2;
		    break;
		  case W_IP_PROTO:
		  case W_TCP_FLAGS:
		  case W_LINK:
		  case W_IP_TOS:
		  case W_IP_TTL:
		    u1 = GET1(data);
		    data++;
		    break;
		  case W_IP_FRAG:
		    // XXX less checking here
		    if (*data == 'F')
			u1 = htons(IP_MF);
		    else if (*data == 'f')
			u1 = htons(100); // random number
		    data++;	// u1 already 0
		    break;
		  case W_IP_OPT: {
		      const unsigned char *endopt = data + 1 + *data;
		      if (endopt <= end) {
			  ip_opt = line.substring((const char *) data + 1, (const char *) endopt);
			  have_ip_opt = true;
			  data = endopt;
		      }
		      break;
		  }
		  case W_TCP_OPT:
		  case W_TCP_NTOPT:
		  case W_TCP_SACK: {
		      const unsigned char *endopt = data + 1 + *data;
		      if (endopt <= end) {
			  tcp_opt = line.substring((const char *) data + 1, (const char *) endopt);
			  have_tcp_opt = true;
			  data = endopt;
		      }
		      break;
		  }
		}
		goto store_contents;
	    }

	    // otherwise, ascii
	    // first, parse contents
	    switch (_contents[i]) {

	      case W_NONE:
		while (data < end && !isspace(*data))
		    data++;
		break;

	      case W_TIMESTAMP:
	      case W_NTIMESTAMP:
	      case W_FIRST_TIMESTAMP:
	      case W_FIRST_NTIMESTAMP:
		next = cp_unsigned(data, end, 10, &u1);
		if (next > data) {
		    data = next;
		    if (data + 1 < end && *data == '.') {
			int digit = 0;
			for (data++; digit < 9 && data < end && isdigit(*data); digit++, data++)
			    u2 = (u2 * 10) + *data - '0';
			for (; digit < 9; digit++)
			    u2 = (u2 * 10);
			for (; data < end && isdigit(*data); data++)
			    /* nada */;
		    }
		}
		break;
		
	      case W_TIMESTAMP_SEC:
	      case W_TIMESTAMP_USEC:
	      case W_IP_LEN:
	      case W_PAYLOAD_LEN:
	      case W_IP_CAPTURE_LEN:
	      case W_IP_ID:
	      case W_SPORT:
	      case W_DPORT:
	      case W_TCP_SEQ:
	      case W_TCP_ACK:
	      case W_COUNT:
	      case W_AGGREGATE:
	      case W_TCP_WINDOW:
	      case W_TCP_URP:
	      case W_IP_TOS:
	      case W_IP_TTL:
		data = cp_unsigned(data, end, 0, &u1);
		break;

	      case W_TIMESTAMP_USEC1: {
#if HAVE_INT64_TYPES
		  uint64_t uu;
		  data = cp_unsigned(data, end, 0, &uu);
		  u1 = (uint32_t)(uu >> 32);
		  u2 = (uint32_t) uu;
#else
		  // silently truncate large numbers
		  data = cp_unsigned(data, end, 0, &u2);
#endif
		  break;
	      }
		
	      case W_IP_SRC:
	      case W_IP_DST:
		for (int j = 0; j < 4; j++) {
		    const unsigned char *first = data;
		    int x = 0;
		    while (data < end && isdigit(*data) && x < 256)
			(x = (x * 10) + *data - '0'), data++;
		    if (x >= 256 || data == first || (j < 3 && (data >= end || *data != '.'))) {
			data = original_data;
			break;
		    }
		    u1 = (u1 << 8) + x;
		    if (j < 3)
			data++;
		}
		break;

	      case W_IP_PROTO:
		if (*data == 'T') {
		    u1 = IP_PROTO_TCP;
		    data++;
		} else if (*data == 'U') {
		    u1 = IP_PROTO_UDP;
		    data++;
		} else if (*data == 'I') {
		    u1 = IP_PROTO_ICMP;
		    data++;
		} else
		    data = cp_unsigned(data, end, 0, &u1);
		break;

	      case W_IP_FRAG:
		if (*data == 'F') {
		    u1 = htons(IP_MF);
		    data++;
		} else if (*data == 'f') {
		    u1 = htons(100);	// random number
		    data++;
		} else if (*data == '.')
		    data++;	// u1 already 0
		break;

	      case W_IP_FRAGOFF:
		next = cp_unsigned(data, end, 0, &u1);
		if (_minor_version == 0) // old-style file
		    u1 <<= 3;
		if (next > data && (u1 & 7) == 0 && u1 < 65536) {
		    u1 >>= 3;
		    data = next;
		    if (data < end && *data == '+') {
			u1 |= IP_MF;
			data++;
		    }
		}
		break;

	      case W_TCP_FLAGS:
		if (isdigit(*data))
		    data = cp_unsigned(data, end, 0, &u1);
		else if (*data == '.')
		    data++;
		else
		    while (data < end && IPSummaryDump::tcp_flag_mapping[*data]) {
			u1 |= 1 << (IPSummaryDump::tcp_flag_mapping[*data] - 1);
			data++;
		    }
		break;

	      case W_IP_OPT:
		if (*data == '.')
		    data++;
		else if (*data != '-') {
		    have_ip_opt = true;
		    data = parse_ip_opt_ascii(data, end, &ip_opt, DO_IPOPT_ALL);
		}
		break;

	      case W_TCP_SACK:
		if (*data == '.')
		    data++;
		else if (*data != '-') {
		    have_tcp_opt = true;
		    data = parse_tcp_opt_ascii(data, end, &tcp_opt, DO_TCPOPT_SACK);
		}
		break;
		
	      case W_TCP_NTOPT:
		if (*data == '.')
		    data++;
		else if (*data != '-') {
		    have_tcp_opt = true;
		    data = parse_tcp_opt_ascii(data, end, &tcp_opt, DO_TCPOPT_NTALL);
		}
		break;
		
	      case W_TCP_OPT:
		if (*data == '.')
		    data++;
		else if (*data != '-') {
		    have_tcp_opt = true;
		    data = parse_tcp_opt_ascii(data, end, &tcp_opt, DO_TCPOPT_ALL);
		}
		break;
		
	      case W_LINK:
		if (*data == '>' || *data == 'L') {
		    u1 = 0;
		    data++;
		} else if (*data == '<' || *data == 'X' || *data == 'R') {
		    u1 = 1;
		    data++;
		} else
		    data = cp_unsigned(data, end, 0, &u1);
		break;

	      case W_PAYLOAD:
		if (*data == '\"') {
		    payload.clear();
		    const unsigned char *fdata = data + 1;
		    for (data++; data < end && *data != '\"'; data++)
			if (*data == '\\' && data < end - 1) {
			    payload.append((const char *) fdata, (const char *) data);
			    fdata = (const unsigned char *) cp_process_backslash((const char *) data, (const char *) end, payload);
			    data = fdata - 1; // account for loop increment
			}
		    payload.append((const char *) fdata, (const char *) data);
		    // bag payload if it didn't parse correctly
		    if (data >= end || *data != '\"')
			data = original_data;
		    else {
			have_payload = have_payload_len = true;
			payload_len = payload.length();
		    }
		}
		break;

	    }

	    // check whether we correctly parsed something
	    {
		bool this_ok = (data > original_data && (data >= end || isspace(*data)));
		while (data < end && !isspace(*data))
		    data++;
		while (data < end && isspace(*data))
		    data++;
		if (!this_ok)
		    continue;
	    }

	    // store contents
	  store_contents:
	    switch (_contents[i]) {

	      case W_TIMESTAMP:
	      case W_NTIMESTAMP:
		if (u2 < 1000000000)
		    q->timestamp_anno().set_nsec(u1, u2), ok++;
		break;

	      case W_TIMESTAMP_SEC:
		q->timestamp_anno().set_sec(u1), ok++;
		break;

	      case W_TIMESTAMP_USEC:
		if (u1 < 1000000)
		    q->timestamp_anno().set_subsec(Timestamp::usec_to_subsec(u1)), ok++;
		break;

	      case W_TIMESTAMP_USEC1:
		if (u1 == 0 && u2 < 1000000)
		    q->timestamp_anno().set_usec(0, u2), ok++;
		else if (u1 == 0)
		    q->timestamp_anno().set_usec(u2/1000000, u2%1000000), ok++;
#if HAVE_INT64_TYPES
		else {
		    uint64_t uu = ((uint64_t)u1 << 32) | u2;
		    q->timestamp_anno().set_usec(uu/1000000, uu%1000000), ok++;
		}
#endif
		break;
		
	      case W_IP_SRC:
		iph->ip_src.s_addr = htonl(u1), ip_ok++;
		break;

	      case W_IP_DST:
		iph->ip_dst.s_addr = htonl(u1), ip_ok++;
		break;
		
	      case W_IP_LEN:
		if (u1 <= 0xFFFF)
		    byte_count = u1, ok++;
		break;
		
	      case W_PAYLOAD_LEN:
		if (u1 <= 0xFFFF)
		    payload_len = u1, have_payload_len = true, ok++;
		break;

	      case W_IP_CAPTURE_LEN:
		/* XXX do nothing with this for now */
		ok++;
		break;
		
	      case W_IP_PROTO:
		if (u1 <= 255)
		    iph->ip_p = u1, ip_ok++;
		break;

	      case W_IP_TOS:
		if (u1 <= 255)
		    iph->ip_tos = u1, ip_ok++;
		break;

	      case W_IP_TTL:
		if (u1 <= 255)
		    iph->ip_ttl = u1, ip_ok++;
		break;

	      case W_IP_ID:
		if (u1 <= 0xFFFF)
		    iph->ip_id = htons(u1), ip_ok++;
		break;

	      case W_IP_FRAG:
		iph->ip_off = u1, ip_ok++;
		break;

	      case W_IP_FRAGOFF:
		if ((u1 & ~IP_MF) <= IP_OFFMASK)
		    iph->ip_off = htons(u1), ip_ok++;
		break;

	      case W_SPORT:
		if (u1 <= 0xFFFF)
		    q->udp_header()->uh_sport = htons(u1), ip_ok++;
		break;

	      case W_DPORT:
		if (u1 <= 0xFFFF)
		    q->udp_header()->uh_dport = htons(u1), ip_ok++;
		break;

	      case W_TCP_SEQ:
		q->tcp_header()->th_seq = htonl(u1), ip_ok++;
		break;

	      case W_TCP_ACK:
		q->tcp_header()->th_ack = htonl(u1), ip_ok++;
		break;

	      case W_TCP_FLAGS:
		if (u1 <= 0xFF)
		    q->tcp_header()->th_flags = u1, ip_ok++;
		else if (u1 <= 0xFFF)
		    // th_off will be set later
		    *reinterpret_cast<uint16_t *>(q->transport_header() + 12) = htons(u1), ip_ok++;
		break;

	      case W_TCP_WINDOW:
		if (u1 <= 0xFFFF)
		    q->tcp_header()->th_win = htons(u1), ip_ok++;
		break;
		
	      case W_TCP_URP:
		if (u1 <= 0xFFFF)
		    q->tcp_header()->th_urp = htons(u1), ip_ok++;
		break;
		
	      case W_COUNT:
		if (u1)
		    SET_EXTRA_PACKETS_ANNO(q, u1 - 1), ok++;
		break;

	      case W_LINK:
		SET_PAINT_ANNO(q, u1), ok++;
		break;

	      case W_AGGREGATE:
		SET_AGGREGATE_ANNO(q, u1), ok++;
		break;

	      case W_FIRST_TIMESTAMP:
	      case W_FIRST_NTIMESTAMP:
		if (u2 < 1000000000) {
		    SET_FIRST_TIMESTAMP_ANNO(q, Timestamp::make_nsec(u1, u2));
		    ok++;
		}
		break;

	    }
	}

	if (!ok && !ip_ok)
	    break;

	// append IP options if any
	if (have_ip_opt && ip_opt) {
	    if (!(q = handle_ip_opt(q, ip_opt)))
		return 0;
	    else		// iph may have changed!! (don't use tcph etc.)
		iph = q->ip_header();
	}
	
	// set TCP offset to a reasonable value; possibly reduce packet length
	if (iph->ip_p == IP_PROTO_TCP && IP_FIRSTFRAG(iph)) {
	    if (!have_tcp_opt || !tcp_opt)
		q->tcp_header()->th_off = sizeof(click_tcp) >> 2;
	    else if (!(q = handle_tcp_opt(q, tcp_opt)))
		return 0;
	    else		// iph may have changed!!
		iph = q->ip_header();
	} else if (iph->ip_p == IP_PROTO_UDP && IP_FIRSTFRAG(iph))
	    q->take(sizeof(click_tcp) - sizeof(click_udp));
	else
	    q->take(sizeof(click_tcp));

	// set IP length
	if (have_payload) {	// XXX what if byte_count indicates IP options?
	    int old_length = q->length();
	    iph->ip_len = ntohs(old_length + payload.length());
	    if ((q = q->put(payload.length()))) {
		memcpy(q->data() + old_length, payload.data(), payload.length());
		// iph may have changed!!
		iph = q->ip_header();
	    }
	} else if (byte_count) {
	    iph->ip_len = ntohs(byte_count);
	    SET_EXTRA_LENGTH_ANNO(q, byte_count - q->length());
	} else if (have_payload_len) {
	    iph->ip_len = ntohs(q->length() + payload_len);
	    SET_EXTRA_LENGTH_ANNO(q, payload_len);
	} else
	    iph->ip_len = ntohs(q->length());

	// set UDP length (after IP length is available)
	if (iph->ip_p == IP_PROTO_UDP && IP_FIRSTFRAG(iph))
	    q->udp_header()->uh_ulen = htons(ntohs(iph->ip_len) - (iph->ip_hl << 2));
	
	// set data from flow ID
	if (_use_flowid) {
	    IPFlowID flowid = (PAINT_ANNO(q) & 1 ? _flowid.rev() : _flowid);
	    if (flowid.saddr())
		iph->ip_src = flowid.saddr();
	    if (flowid.daddr())
		iph->ip_dst = flowid.daddr();
	    if (flowid.sport() && IP_FIRSTFRAG(iph))
		q->tcp_header()->th_sport = flowid.sport();
	    if (flowid.dport() && IP_FIRSTFRAG(iph))
		q->tcp_header()->th_dport = flowid.dport();
	    if (_use_aggregate)
		SET_AGGREGATE_ANNO(q, _aggregate);
	} else if (!ip_ok)
	    q->set_network_header(0, 0);

	// set destination IP address annotation
	q->set_dst_ip_anno(iph->ip_dst);

	// set checksum
	if (_checksum && ip_ok)
	    set_checksums(q, iph);
	
	return q;
    }

    // bad format if we get here
    if (!_format_complaint) {
	// don't complain if the line was all blank
	if ((int) strspn(line.data(), " \t\n\r") != line.length()) {
	    if (_contents.size() == 0)
		_ff.error(errh, "no '!data' provided");
	    else
		_ff.error(errh, "packet parse error");
	    _format_complaint = true;
	}
    }
    if (q)
	q->kill();
    return 0;
}

inline Packet *
set_packet_lengths(Packet *p, uint32_t extra_length)
{
    uint32_t length = p->length() + extra_length;
    if (htons(length) != p->ip_header()->ip_len) {
	if (WritablePacket *q = p->uniqueify()) {
	    click_ip *ip = q->ip_header();
	    ip->ip_len = htons(length);
	    if (ip->ip_p == IP_PROTO_UDP)
		q->udp_header()->uh_ulen = htons(length - (ip->ip_hl << 2));
	    return q;
	} else
	    return 0;
    } else
	return p;
}

Packet *
FromIPSummaryDump::handle_multipacket(Packet *p)
{
    assert(!_work_packet || _work_packet == p);
    
    if (!p || !EXTRA_PACKETS_ANNO(p)) {
	_work_packet = 0;
	return p;
    }

    uint32_t count = 1 + EXTRA_PACKETS_ANNO(p);

    // set up _multipacket variables on new packets (_work_packet == 0)
    if (!_work_packet) {
	assert(count > 1);
	// set length of all but the last packet
	_multipacket_length = (p->length() + EXTRA_LENGTH_ANNO(p)) / count;
	// beware if there isn't enough EXTRA_LENGTH to cover all the packets
	if (_multipacket_length < p->length()) {
	    _multipacket_length = p->length();
	    SET_EXTRA_LENGTH_ANNO(p, _multipacket_length * (count - 1));
	}
	// set timestamps
	_multipacket_end_timestamp = p->timestamp_anno();
	if (FIRST_TIMESTAMP_ANNO(p)) {
	    _multipacket_timestamp_delta = (p->timestamp_anno() - FIRST_TIMESTAMP_ANNO(p)) / (count - 1);
	    p->timestamp_anno() = FIRST_TIMESTAMP_ANNO(p);
	} else
	    _multipacket_timestamp_delta = Timestamp();
	// prepare IP lengths for _multipacket_extra_length
	_work_packet = set_packet_lengths(p, _multipacket_length - p->length());
	if (!_work_packet)
	    return 0;
    }

    // prepare packet to return
    if ((p = p->clone())) {
	SET_EXTRA_PACKETS_ANNO(p, 0);
	SET_EXTRA_LENGTH_ANNO(p, _multipacket_length - p->length());
    }

    // reduce weight of _work_packet 
    SET_EXTRA_PACKETS_ANNO(_work_packet, count - 2);
    SET_EXTRA_LENGTH_ANNO(_work_packet, EXTRA_LENGTH_ANNO(_work_packet) - _multipacket_length);
    if (count == 2) {
	_work_packet->timestamp_anno() = _multipacket_end_timestamp;
	_work_packet = set_packet_lengths(_work_packet, EXTRA_LENGTH_ANNO(_work_packet));
    } else
	_work_packet->timestamp_anno() += _multipacket_timestamp_delta;

    return p;
}

bool
FromIPSummaryDump::run_task()
{
    if (!_active)
	return false;
    Packet *p;

    while (1) {
	p = (_work_packet ? _work_packet : read_packet(0));
	if (!p) {
	    if (_stop)
		router()->please_stop_driver();
	    return false;
	}
	if (_multipacket)
	    p = handle_multipacket(p);
	// check sampling probability
	if (_sampling_prob >= (1 << SAMPLING_SHIFT)
	    || (uint32_t)(random() & ((1 << SAMPLING_SHIFT) - 1)) < _sampling_prob)
	    break;
	if (p)
	    p->kill();
    }

    if (p)
	output(0).push(p);
    _task.fast_reschedule();
    return true;
}

Packet *
FromIPSummaryDump::pull(int)
{
    if (!_active)
	return 0;
    Packet *p;

    while (1) {
	p = (_work_packet ? _work_packet : read_packet(0));
	if (!p) {
	    if (_stop)
		router()->please_stop_driver();
	    _notifier.sleep();
	    return 0;
	}
	if (_multipacket)
	    p = handle_multipacket(p);
	// check sampling probability
	if (_sampling_prob >= (1 << SAMPLING_SHIFT)
	    || (uint32_t)(random() & ((1 << SAMPLING_SHIFT) - 1)) < _sampling_prob)
	    break;
	if (p)
	    p->kill();
    }

    _notifier.wake();
    return p;
}


enum { H_SAMPLING_PROB, H_ACTIVE, H_ENCAP, H_STOP };

String
FromIPSummaryDump::read_handler(Element *e, void *thunk)
{
    FromIPSummaryDump *fd = static_cast<FromIPSummaryDump *>(e);
    switch ((intptr_t)thunk) {
      case H_SAMPLING_PROB:
	return cp_unparse_real2(fd->_sampling_prob, SAMPLING_SHIFT);
      case H_ACTIVE:
	return cp_unparse_bool(fd->_active);
      case H_ENCAP:
	return "IP";
      default:
	return "<error>";
    }
}

int
FromIPSummaryDump::write_handler(const String &s_in, Element *e, void *thunk, ErrorHandler *errh)
{
    FromIPSummaryDump *fd = static_cast<FromIPSummaryDump *>(e);
    String s = cp_uncomment(s_in);
    switch ((intptr_t)thunk) {
      case H_ACTIVE: {
	  bool active;
	  if (cp_bool(s, &active)) {
	      fd->_active = active;
	      if (fd->output_is_push(0) && active && !fd->_task.scheduled())
		  fd->_task.reschedule();
	      else if (!fd->output_is_push(0))
		  fd->_notifier.set_active(active, true);
	      return 0;
	  } else
	      return errh->error("'active' should be Boolean");
      }
      case H_STOP:
	fd->_active = false;
	fd->router()->please_stop_driver();
	return 0;
      default:
	return -EINVAL;
    }
}

void
FromIPSummaryDump::add_handlers()
{
    add_read_handler("sampling_prob", read_handler, (void *)H_SAMPLING_PROB);
    add_read_handler("active", read_handler, (void *)H_ACTIVE);
    add_write_handler("active", write_handler, (void *)H_ACTIVE);
    add_read_handler("encap", read_handler, (void *)H_ENCAP);
    add_write_handler("stop", write_handler, (void *)H_STOP);
    _ff.add_handlers(this);
    if (output_is_push(0))
	add_task_handlers(&_task);
}

ELEMENT_REQUIRES(userlevel FromFile IPSummaryDumpInfo)
EXPORT_ELEMENT(FromIPSummaryDump)
CLICK_ENDDECLS


syntax highlighted by Code2HTML, v. 0.9.1