// -*- mode: c++; c-basic-offset: 4 -*-
/*
 * ipsumdumpinfo.{cc,hh} -- information used by IP summary dump elements
 * Eddie Kohler
 *
 * Copyright (c) 2002 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 "ipsumdumpinfo.hh"
#include <click/packet.hh>
#include <click/packet_anno.hh>
#include <clicknet/ip.h>
CLICK_DECLS

int
IPSummaryDumpInfo::parse_content(const String &word)
{
    if (word == "timestamp" || word == "ts")
	return W_TIMESTAMP;
    else if (word == "sec" || word == "ts_sec")
	return W_TIMESTAMP_SEC;
    else if (word == "usec" || word == "ts_usec")
	return W_TIMESTAMP_USEC;
    else if (word == "usec1" || word == "ts_usec1")
	return W_TIMESTAMP_USEC1;
    else if (word == "src" || word == "ip_src")
	return W_IP_SRC;
    else if (word == "dst" || word == "ip_dst")
	return W_IP_DST;
    else if (word == "sport")
	return W_SPORT;
    else if (word == "dport")
	return W_DPORT;
    else if (word == "frag" || word == "ip_frag")
	return W_IP_FRAG;
    else if (word == "fragoff" || word == "ip_fragoff")
	return W_IP_FRAGOFF;
    else if (word == "len" || word == "length" || word == "ip_len")
	return W_IP_LEN;
    else if (word == "id" || word == "ip_id")
	return W_IP_ID;
    else if (word == "proto" || word == "ip_proto" || word == "ip_p")
	return W_IP_PROTO;
    else if (word == "tcp_seq" || word == "tcp_seqno")
	return W_TCP_SEQ;
    else if (word == "tcp_ack" || word == "tcp_ackno")
	return W_TCP_ACK;
    else if (word == "tcp_flags")
	return W_TCP_FLAGS;
    else if (word == "tcp_sack")
	return W_TCP_SACK;
    else if (word == "tcp_opt")
	return W_TCP_OPT;
    else if (word == "tcp_ntopt")
	return W_TCP_NTOPT;
    else if (word == "payload_len" || word == "payload_length")
	return W_PAYLOAD_LEN;
    else if (word == "count" || word == "pkt_count" || word == "packet_count")
	return W_COUNT;
    else if (word == "payload")
	return W_PAYLOAD;
    else if (word == "link" || word == "direction")
	return W_LINK;
    else if (word == "aggregate" || word == "agg")
	return W_AGGREGATE;
    else if (word == "first_timestamp" || word == "first_ts")
	return W_FIRST_TIMESTAMP;
    else if (word == "ntimestamp")
	return W_NTIMESTAMP;
    else if (word == "first_ntimestamp")
	return W_FIRST_NTIMESTAMP;
    else if (word == "tcp_window" || word == "tcp_win")
	return W_TCP_WINDOW;
    else if (word == "ip_opt")
	return W_IP_OPT;
    else if (word == "ip_tos")
	return W_IP_TOS;
    else if (word == "ip_ttl")
	return W_IP_TTL;
    else if (word == "ip_capture_len")
	return W_IP_CAPTURE_LEN;
    else if (word == "none")
	return W_NONE;
    else if (word == "tcp_urp")
	return W_TCP_URP;
    else if (find(word, ' ') != word.end()) {
	const char *space = find(word, ' ');
	return parse_content(word.substring(word.begin(), space) + "_" + word.substring(space + 1, word.end()));
    } else
	return -1;
}

static int content_binary_sizes[] = {
    0, 8, 4, 4, 4,	// W_NONE, W_TIMESTAMP, W_TS_SEC, W_TS_USEC, W_IP_SRC
    4, 4, 1, 2, 2,	// W_IP_DST, W_IP_LEN, W_IP_PROTO, W_IP_ID, W_SPORT
    2, 4, 4, 1, 4,	// W_DPORT, W_TCP_SEQ, W_TCP_ACK, W_TCP_FLAGS,
			// W_PAYLOAD_LEN
    4, 1, 2, -10000, 1,	// W_COUNT, W_IP_FRAG, W_IP_FRAGOFF, W_PAYLOAD, W_LINK
    4, 4, 4, 4, 8,      // W_AGGREGATE, W_TCP_SACK, W_TCP_OPT, W_TCP_NTOPT,
			// W_FIRST_TIMESTAMP
    2, 4, 1, 1, 8,	// W_TCP_WINDOW, W_IP_OPT, W_IP_TOS, W_IP_TTL,
			// W_TIMESTAMP_USEC1
    4, 2, 8, 8		// W_IP_CAPTURE_LEN, W_TCP_URP, W_NTIMESTAMP,
			// W_FIRST_NTIMESTAMP
};

int
IPSummaryDumpInfo::content_binary_size(int content)
{
    if (content < 0 || content >= (int)(sizeof(content_binary_sizes) / sizeof(content_binary_sizes[0])))
	return -10000;
    else
	return content_binary_sizes[content];
}



/////////////////////
// PARSING

namespace IPSummaryDump {

static bool none_extract(PacketDesc&, int)
{
    return false;
}

static Field* fields;
const Field null_field = {
    "none", B_0, 0, none_extract, num_outa, outb, inb, 0, 0
};

int Field::binary_size() const
{
    switch (thunk & B_TYPEMASK) {
      case B_0:		return 0;
      case B_1:		return 1;
      case B_2:		return 2;
      case B_4:		return 4;
      case B_8:		return 8;
      case B_4NET:	return 4;
      case B_SPECIAL:	return 4;
      default:		return -1;
    }
}
    
const Field* find_field(const String& name, bool likely_synonyms)
{
    // search for "name"
    for (Field* f = fields; f; f = f->next)
	if (name == f->name)
	    return (f->synonym ? f->synonym : f);
    if (name == "none")
	return &null_field;
    if (!likely_synonyms)
	return 0;
    
    // if not found, change spaces to underscores and try again
    const char *s = find(name, ' ');
    if (s != name.end())
	return find_field(name.substring(name.begin(), s) + "_" + name.substring(s + 1, name.end()));
    
    // if not found, change "X" to "ip_X" and try again
    if (find(name, '_') == name.end())
	return find_field("ip_" + name);
    
    // not found
    return 0;
}

int register_unparser(const char* name, int thunk,
		      void (*prepare)(PacketDesc&),
		      bool (*extract)(PacketDesc&, int),
		      void (*outa)(const PacketDesc&, int),
		      void (*outb)(const PacketDesc&, bool, int),
		      const uint8_t *(*inb)(PacketDesc&, const uint8_t*, const uint8_t*, int))
{
    Field* f = const_cast<Field*>(find_field(name, false));
    if (f) {
	if (f == &null_field
	    || f->synonym
	    || f->thunk != thunk
	    || (f->prepare && f->prepare != prepare)
	    || (f->extract && f->extract != extract)
	    || (f->outa && f->outa != outa)
	    || (f->outb && f->outb != outb)
	    || (f->inb && f->inb != inb))
	    return -1;
	if (!f->prepare && prepare)
	    f->prepare = prepare;
	if (!f->extract && extract)
	    f->extract = extract;
	if (!f->outa && outa)
	    f->outa = outa;
	if (!f->outb && outb)
	    f->outb = outb;
	if (!f->inb && inb)
	    f->inb = inb;
    } else if (!f) {
	if (!(f = new Field))
	    return -1;
	f->name = name;
	f->thunk = thunk;
	f->prepare = prepare;
	f->extract = extract;
	f->outa = outa;
	f->outb = outb;
	f->inb = inb;
	f->synonym = 0;
	f->next = fields;
	fields = f;
    }
    return 0;
}

void static_cleanup()
{
    while (Field* f = fields) {
	fields = f->next;
	delete f;
    }
}

int register_synonym(const char* name, const char* synonym)
{
    Field* synf = const_cast<Field*>(find_field(synonym));
    if (!synf)
	return -1;
    
    Field* f = const_cast<Field*>(find_field(name, false));
    if (f)
	return f->synonym == synf;
    else {
	if (!(f = new Field))
	    return -1;
	f->name = name;
	f->synonym = synf;
	f->next = fields;
	fields = f;
	return 0;
    }
}

bool field_missing(const PacketDesc& d, int what, const char* header_name, int l)
{
    if (d.bad_sa && !*d.bad_sa) {
	*d.bad_sa << "!bad ";
	if (what == MISSING_IP || what == MISSING_IP_TRANSPORT) {
	    if (!d.iph)
		*d.bad_sa << "no IP header";
	    else if (what == MISSING_IP)
		*d.bad_sa << "truncated IP header capture";
	    else if ((int) (d.p->transport_length() + EXTRA_LENGTH_ANNO(d.p)) >= l)
		*d.bad_sa << "truncated " << header_name << " header capture";
	    else if (IP_ISFRAG(d.iph))
		*d.bad_sa << "fragmented " << header_name << " header";
	    else
		*d.bad_sa << "truncated " << header_name << " header";
	} else
	    *d.bad_sa << header_name << " header";
	*d.bad_sa << '\n';
    }
    return false;
}

#ifdef i386
# define PUT4NET(p, d)	*reinterpret_cast<uint32_t *>((p)) = (d)
# define PUT4(p, d)	*reinterpret_cast<uint32_t *>((p)) = htonl((d))
# define PUT2(p, d)	*reinterpret_cast<uint16_t *>((p)) = htons((d))
# define GET4NET(p)	*reinterpret_cast<const uint32_t *>((p))
# define GET4(p)	ntohl(*reinterpret_cast<const uint32_t *>((p)))
# define GET2(p)	ntohs(*reinterpret_cast<const uint16_t *>((p)))
#else
# define PUT4NET(p, d)	do { uint32_t d__ = ntohl((d)); (p)[0] = d__>>24; (p)[1] = d__>>16; (p)[2] = d__>>8; (p)[3] = d__; } while (0)
# define PUT4(p, d)	do { (p)[0] = (d)>>24; (p)[1] = (d)>>16; (p)[2] = (d)>>8; (p)[3] = (d); } while (0)
# define PUT2(p, d)	do { (p)[0] = (d)>>8; (p)[1] = (d); } while (0)
# define GET4NET(p)	htonl((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3])
# define GET4(p)	((p)[0]<<24 | (p)[1]<<16 | (p)[2]<<8 | (p)[3])
# define GET2(p)	((p)[0]<<8 | (p)[1])
#endif
#define PUT1(p, d)	((p)[0] = (d))

void num_outa(const PacketDesc& d, int)
{
    *d.sa << d.v;
}

void outb(const PacketDesc& d, bool, int thunk)
{
    switch (thunk & B_TYPEMASK) {
      case B_0:
	break;
      case B_1: {
	  char* c = d.sa->extend(1);
	  PUT1(c, d.v);
	  break;
      }
      case B_2: {
	  char* c = d.sa->extend(2);
	  PUT2(c, d.v);
	  break;
      }
      case B_4: {
	  char* c = d.sa->extend(4);
	  PUT4(c, d.v);
	  break;
      }
      case B_8: {
	  char* c = d.sa->extend(8);
	  PUT4(c, d.v);
	  PUT4(c + 4, d.v2);
	  break;
      }
      case B_4NET: {
	  char* c = d.sa->extend(4);
	  PUT4NET(c, d.v);
	  break;
      }
    }
}

const uint8_t *inb(PacketDesc& d, const uint8_t *s, const uint8_t *end, int thunk)
{
    d.v = 0;
    switch (thunk & B_TYPEMASK) {
      case B_0:
	return s;
      case B_1:
	if (s >= end)
	    goto bad;
	d.v = s[0];
	return s + 1;
      case B_2:
	if (s + 1 >= end)
	    goto bad;
	d.v = GET2(s);
	return s + 2;
      case B_4:
	if (s + 3 >= end)
	    goto bad;
	d.v = GET4(s);
	return s + 4;
      case B_8:
	if (s + 7 >= end)
	    goto bad;
	d.v = GET4(s);
	d.v2 = GET4(s + 4);
	return s + 8;
      case B_4NET:
	if (s + 3 >= end)
	    goto bad;
	d.v = GET4NET(s);
	return s + 4;
      bad:
      default:
	d.v = d.v2 = 0;
	return end;
    }
}


const char tcp_flags_word[] = "FSRPAUECN";

const uint8_t tcp_flag_mapping[256] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x1-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x2-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3-
    0, 5, 0, 8, 0, 7, 1, 0, 0, 0, 0, 0, 0, 0, 9, 0, // 0x4-
    4, 0, 3, 2, 0, 6, 0, 8, 7, 8, 0, 0, 0, 0, 0, 0, // 0x5-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x6-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x7-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x8-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x9-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xA-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xB-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xC-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xD-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xE-
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  // 0xF-    
};

}

ELEMENT_REQUIRES(userlevel)
ELEMENT_PROVIDES(IPSummaryDumpInfo)
ELEMENT_PROVIDES(IPSummaryDump)
CLICK_ENDDECLS


syntax highlighted by Code2HTML, v. 0.9.1