// Copyright (C) FIT VUT
// Petr Lampa <lampa@fit.vutbr.cz>
// $Id$
// vi:set ts=8 sts=8 sw=8:
//
// TCP protocol
//

#ifndef __NDWATCH_TCP_H__
#define __NDWATCH_TCP_H__

//
// TCP Options
//
class TCP_Option 
{
	Octet _kind;
public:
	enum OptionKind { OptionEnd=0,OptionNOOP=1,OptionMSS=2, 
		// RFC 1323
		OptionWinScale=3, OptionTimestamps=8,
		// RFC2014
		OptionSackPerm=4, 
		};
	TCP_Option(Octet kind): _kind(kind) { }
	Octet kind() const { return _kind; }
	virtual string name() const = 0;
	virtual string to_string(int level=0) const = 0;
	virtual bool do_build(Buffer &pkt, int phase, Word &pos, Word &checksum) = 0;
	virtual bool decode(Buffer &pkt, int &lopts) = 0;
	virtual ~TCP_Option() { }
	static TCP_Option *decode_option(Buffer &pkt, Octet kind, int &lopts);
};

// End of Option List
// +--------+
// |00000000|
// +--------+
// RFC793 3.1
class TCP_Option_End: public TCP_Option
{
public:
	TCP_Option_End(): TCP_Option(OptionEnd) { }
	string name() const { return "End"; }
	string to_string(int level=0) const { return "End"; }
	bool do_build(Buffer &pkt, int phase, Word &pos, Word &checksum) {
		pos++;
		if (phase == 0) return true;
		if (!pkt.add_octet(OptionEnd)) return false;
		return true;
	}
	bool decode(Buffer &pkt, int &lopts) {
		return true;
	}
	~TCP_Option_End() { }
};

//
// No-Operation
// +--------+
// |00000001|
// +--------+
// RFC793 3.1
class TCP_Option_NOOP: public TCP_Option
{
public:
	TCP_Option_NOOP(): TCP_Option(OptionNOOP) { }
	string name() const { return "NOOP"; }
	string to_string(int level=0) const { return "NOOP"; }
	bool do_build(Buffer &pkt, int phase, Word &pos, Word &checksum) {
		if (phase == 0) {
			if (pos & 1) checksum += OptionNOOP;
			else checksum += OptionNOOP<<8;
			pos++;
			return true;
		}
		if (!pkt.add_octet(OptionNOOP)) return false;
		pos++;
		return true;
	}
	bool decode(Buffer &pkt, int &lopts) {
		return true;
	}
	~TCP_Option_NOOP() { }
};

//
// Maximum Segment Size
// +--------+--------+---------+--------+
// |    2   |    4   |   max seg size   |
// +--------+--------+---------+--------+
// RFC793 3.1
class TCP_Option_MSS: public TCP_Option
{
	Short _size;
public:
	TCP_Option_MSS(): TCP_Option(OptionMSS),_size(0) { }
	explicit TCP_Option_MSS(unsigned size): TCP_Option(OptionMSS),_size(size) { }
	Short size() const { return _size; }
	string name() const { return "MSS"; }
	string to_string(int level=0) const { 
		string str = "MSS "; 
		str += cvt_int(_size);
		return str;
	}
	bool do_build(Buffer &pkt, int phase, Word &pos, Word &checksum) {
		if (phase == 0) {
			if (pos & 1) checksum += OptionMSS + (4<<8);
			else checksum += (OptionMSS<<8) + 4;
			pos += 2;
			if (pos & 1) checksum += (_size>>8) + ((_size&0xff) << 8);
			else checksum += _size;
			pos += 2;
			return true;
		}
		if (!pkt.add_octet(OptionMSS)) return false;
		pos++;
		if (!pkt.add_octet(4)) return false;
		pos++;
		if (!pkt.add_hshort(_size)) return false;
		pos += 2;
		return true;
	}
	bool decode(Buffer &pkt, int &lopts) {
		Octet l;
		if (lopts <= 0 || !pkt.get_octet(l)) return false;
		--lopts;
		if (l != 4) return false;
		if (lopts < 2 || !pkt.get_hshort(_size)) return false;
		lopts -= 2;
		return true;
	}
	~TCP_Option_MSS() { }
};

//
// TCP Window Scale Option
// +---------+---------+---------+
// | Kind=3  |Length=3 |shift.cnt|
// +---------+---------+---------+
// RFC1323 2.2
// RFC793 3.1
class TCP_Option_WinScale: public TCP_Option
{
	Octet _shift;
public:
	TCP_Option_WinScale(): TCP_Option(OptionWinScale),_shift(0) { }
	explicit TCP_Option_WinScale(unsigned shift): TCP_Option(OptionWinScale),_shift(shift) { }
	Octet shift() const { return _shift; }
	string name() const { return "WinScale"; }
	string to_string(int level=0) const { 
		string str = "WinScale "; 
		str += cvt_int(_shift);
		return str;
	}
	bool do_build(Buffer &pkt, int phase, Word &pos, Word &checksum) {
		if (phase == 0) {
			if (pos & 1) checksum += OptionWinScale + (3<<8);
			else checksum += (OptionWinScale<<8) + 3;
			pos += 2;
			if (pos & 1) checksum += (_shift << 8);
			else checksum += _shift;
			pos++;
			return true;
		}
		if (!pkt.add_octet(OptionWinScale)) return false;
		pos++;
		if (!pkt.add_octet(3)) return false;
		pos++;
		if (!pkt.add_octet(_shift)) return false;
		pos++;
		return true;
	}
	bool decode(Buffer &pkt, int &lopts) {
		Octet l;
		if (lopts <= 0 || !pkt.get_octet(l)) return false;
		--lopts;
		if (l != 3) return false;
		if (lopts <= 0 || !pkt.get_octet(_shift)) return false;
		--lopts;
		return true;
	}
	~TCP_Option_WinScale() { }
};

//
// TCP Sack-Permitted Option
// +---------+---------+
// | Kind=4  | Length=2|
// +---------+---------+
// RFC2018 2.
class TCP_Option_SackPerm: public TCP_Option
{
public:
	TCP_Option_SackPerm(): TCP_Option(OptionSackPerm) { }
	string name() const { return "SackPerm"; }
	string to_string(int level=0) const { 
		return "SackPerm"; 
	}
	bool do_build(Buffer &pkt, int phase, Word &pos, Word &checksum) {
		if (phase == 0) {
			if (pos & 1) checksum += OptionSackPerm + (2<<8);
			else checksum += (OptionSackPerm<<8) + 2;
			pos += 2;
			return true;
		}
		if (!pkt.add_octet(OptionSackPerm)) return false;
		pos++;
		if (!pkt.add_octet(2)) return false;
		pos++;
		return true;
	}
	bool decode(Buffer &pkt, int &lopts) {
		Octet l;
		if (lopts <= 0 || !pkt.get_octet(l)) return false;
		--lopts;
		if (l != 2) return false;
		return true;
	}
	~TCP_Option_SackPerm() { }
};

//
// TCP SACK Option
// +--------+--------+
// | Kind=5 | Length |
// +--------+--------+--------+--------+
// |      Left Edge of 1st Block       |
// +--------+--------+--------+--------+
// |      Right Edge of 1st Block      |
// +--------+--------+--------+--------+
// /            . . .                  /
// RFC2018 3.
//
// TCP Echo
// +--------+--------+--------+--------+--------+--------+
// | Kind=6 | Length |   4 bytes of info to be echoed    |
// +--------+--------+--------+--------+--------+--------+
// RFC1072 4.1
//
// TCP Echo Reply
// +--------+--------+--------+--------+--------+--------+
// | Kind=7 | Length |    4 bytes of echoed info         |
// +--------+--------+--------+--------+--------+--------+
// RFC1072 4.1

// TCP Timestamps Option
// +-------+-------+---------------------+---------------------+
// |Kind=8 |  10   |   TS Value (TSval)  |TS Echo Reply (TSecr)|
// +-------+-------+---------------------+---------------------+
// RFC1323 3.2
class TCP_Option_Timestamps: public TCP_Option
{
	Word _value;
	Word _reply;
public:
	TCP_Option_Timestamps(): TCP_Option(OptionTimestamps),_value(0),_reply(0) { }
	TCP_Option_Timestamps(Word value, Word reply): TCP_Option(OptionTimestamps),_value(value),_reply(reply) { }
	Word ts_value() const { return _value; }
	Word ts_reply() const { return _reply; }
	string name() const { return "Timestamps"; }
	string to_string(int level=0) const { 
		string str = "Timestamps "; 
		str += cvt_int(_value);
		str += ",";
		str += cvt_int(_reply);
		return str;
	}
	bool do_build(Buffer &pkt, int phase, Word &pos, Word &checksum) {
		if (phase == 0) {
			if (pos & 1) checksum += OptionTimestamps + (10<<8);
			else checksum += (OptionTimestamps<<8) + 10;
			pos += 2;
			if (pos & 1) checksum += (_value>>24) + ((_value>>8)&0xffff) + ((_value&0xff) << 8);
			else checksum += (_value>>16) + (_value & 0xffff);
			pos += 4;
			if (pos & 1) checksum += (_reply>>24) + ((_reply>>8)&0xffff) + ((_reply&0xff) << 8);
			else checksum += (_reply>>16) + (_reply & 0xffff);
			pos += 4;
			return true;
		}
		if (!pkt.add_octet(OptionTimestamps)) return false;
		pos++;
		if (!pkt.add_octet(10)) return false;
		pos++;
		if (!pkt.add_hlong(_value)) return false;
		pos += 4;
		if (!pkt.add_hlong(_reply)) return false;
		pos += 4;
		return true;
	}
	bool decode(Buffer &pkt, int &lopts) {
		Octet l;
		if (lopts <= 0 || !pkt.get_octet(l)) return false;
		--lopts;
		if (l != 10) return false;
		if (lopts < 4 || !pkt.get_hlong(_value)) return false;
		lopts -= 4;
		if (lopts < 4 || !pkt.get_hlong(_reply)) return false;
		lopts -= 4;
		return true;
	}
	~TCP_Option_Timestamps() { }
};
//
// TCP Partial Order Connection permitted
// +-----------+-------------+
// |  Kind=9   |  Length=2   |
// +-----------+-------------+
// RFC1693
//
// TCP Partial Order Connection service profile
// +----------+----------+-+-+--------+
// |  Kind=10 | Length=3 |S|E| Filler |
// +----------+----------+-+-+--------+
// RFC1693
//
// T/TCP CC Option
// +----------+----------+--------+--------+--------+--------+
// |  Kind=11 | Length=6 |    Connection Count:  SEG.CC      |
// +----------+----------+--------+--------+--------+--------+
// RFC1644
//
// T/TCP CC.NEW Option
// +----------+----------+--------+--------+--------+--------+
// |  Kind=12 | Length=6 |    Connection Count:  SEG.CC      |
// +----------+----------+--------+--------+--------+--------+
// RFC1644
//
// T/TCP CC.ECHO Option
// +----------+----------+--------+--------+--------+--------+
// |  Kind=13 | Length=6 |    Connection Count:  SEG.CC      |
// +----------+----------+--------+--------+--------+--------+
// RFC1644
//
// TCP Alternate Checksum Request
// +----------+----------+----------+
// |  Kind=14 | Length=3 |  chksum  |
// +----------+----------+----------+
// RFC1146 
//
// TCP Alternate Checksum Data
// +----------+----------+---------+     +---------+
// |  Kind=15 | Length=N |  data   | ... |  data   |
// +----------+----------+---------+     +---------+
// RFC1146
//
// MD5 Signature Option
// +---------+---------+-------------------+
// | Kind=19 |Length=18|   MD5 digest...   |
// +---------+---------+-------------------+
// |                                       |
// +---------------------------------------+
// |                                       |
// +---------------------------------------+
// |                                       |
// +-------------------+-------------------+
// |                   |
// +-------------------+
// RFC2385
//
// Quick-Start Response Option
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |     Kind=27   |  Length=8     | Resv. | Rate  |   TTL Diff    |
// |               |               |       |Request|               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                   QS Nonce                                | R |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// RFC4782 4.2
//
// TCP User Timeout Option
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |     Kind=28   |  Length=8     | Resv. | Rate  |   TTL Diff    |
// |               |               |       |Request|               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                   QS Nonce                                | R |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// RFC5482
//
// TCP Authenticatin Option
// +------------+------------+------------+------------+
// |  Kind=29   |   Length   |   KeyID    | RNextKeyID |
// +------------+------------+------------+------------+
// |                     MAC           ...
// +-----------------------------------...
// RFC5925 2.2

#ifdef _LIB
TCP_Option *TCP_Option::decode_option(Buffer &pkt, Octet kind, int &lopts)
{
	TCP_Option *opt;
	switch (kind) {
	case OptionEnd: opt = new TCP_Option_End(); break;
	case OptionNOOP: opt = new TCP_Option_NOOP(); break;
	case OptionMSS: opt = new TCP_Option_MSS(); break;
	case OptionWinScale: opt = new TCP_Option_WinScale(); break;
	case OptionTimestamps: opt = new TCP_Option_Timestamps(); break;
	case OptionSackPerm: opt = new TCP_Option_SackPerm(); break;
	default: 
		Octet l,b;
		if (lopts <= 0 || !pkt.get_octet(l)) return NULL;
		lopts--;
		// skip unknown option octets
		while (l > 2) {
			if (lopts <= 0) return NULL;
			if (!pkt.get_octet(b)) return NULL;
			lopts--;
			l--;
		}
		return NULL;
	}
	if (opt->decode(pkt, lopts)) return opt;
	delete opt;	// XXX return broken options?
	return NULL;
}
#endif

// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |          Source Port          |       Destination Port        |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                        Sequence Number                        |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                    Acknowledgment Number                      |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |  Data |       |C|E|U|A|P|R|S|F|                               |
// | Offset| Res   |W|C|R|C|S|S|Y|I|            Window             |
// |       |       |R|E|G|K|H|T|N|N|                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |           Checksum            |         Urgent Pointer        |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                    Options                    |    Padding    |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                             data                              |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// RFC 793 3.1
// RFC3168 23.1
class TCP: public Packet
{
public:
	enum FlagType { FIN=1,SYN=2,RST=4,PSH=8,ACK=16,URG=32,ECE=64,CWR=128 };
	enum { MAX_OPTS=16 };
private:
	Short _sport;	// source port
	Short _dport;	// destination port
	Word _seq;	// sequence number (SYN - initial sequence number)
	Word _ack;	// acknowledgment number
	Octet _dataofs;	// data begin, in 32 bit word units
	Short _flags;	// control bits
	Short _window;	// accept windows size
	Short _checksum;// checksum
	Short _urgptr;	// pointer to urgent data (URG)
	Octet *_data;	// data segment
	Word _length;	// data length
	bool _own;
	unsigned _nopts;
	TCP_Option *_opts[MAX_OPTS];

	TCP(const TCP&);
	TCP& operator=(const TCP&);
public:
	TCP(): _sport(0), _dport(0), _seq(0), _ack(0), _dataofs(5), _flags(0), _window(8192), _checksum(0), _urgptr(0), _data(0), _length(0), _own(false), _nopts(0) { }
	TCP(unsigned sport, unsigned dport): _sport(sport), _dport(dport), _seq(0), _ack(0), _dataofs(5), _flags(0), _window(8192), _checksum(0), _urgptr(0), _data(0), _length(0), _own(false), _nopts(0) { }

	void set_data(Octet *data, Short length, bool own=false) { _data = data; _length = length; _own = own; }
	Short data_length() const { return _length; }
	Octet *data() { return _data; }

	TCP_Option *get_option(unsigned i) const { if (i < _nopts) return _opts[i]; return NULL; }
	void add_option(TCP_Option *opt) { if (_nopts < MAX_OPTS) _opts[_nopts++] = opt; }	

	Short src_port() const { return _sport; }
        Short dst_port() const { return _dport; }
	void seq(Word seq) { _seq = seq; }
	Word seq() const { return _seq; }
	void ack(Word ack) { _seq = ack; }
	Word ack() const { return _seq; }
	void window(Word val) { _window = val; }
	Short window() const { return _seq; }
	void urgptr(Word val) { _urgptr = val; }
	Short urgptr() const { return _seq; }
	void flags(Short flags) { _flags = flags; }
	Short flags() const { return _flags; }

	string name() const { return "TCP"; }
	string to_string(int level=0) const;
	void fixup();
	bool do_build(Buffer &pkt, int phase, Word &pos);
	bool decode(Buffer &pkt);
	void clear_opts() { for (unsigned i = 0; i < _nopts; i++) delete _opts[i]; _nopts = 0; }
	~TCP() { clear_opts(); if (_data && _own) delete _data; }
};

#ifdef _LIB
string TCP::to_string(int level) const
{
	string str("<TCP sport=");
	str += cvt_int(_sport);
	str += " dport=";
	str += cvt_int(_dport);
	str += " seq=";
	str += cvt_int(_seq);
	str += " ack=";
	str += cvt_int(_ack);
	str += " dofs=";
	str += cvt_int(_dataofs);
	str += " flags=";
	if (_flags & CWR) str += "C";
	if (_flags & ECE) str += "E";
	if (_flags & URG) str += "U";
	if (_flags & ACK) str += "A";
	if (_flags & PSH) str += "P";
	if (_flags & RST) str += "R";
	if (_flags & SYN) str += "S";
	if (_flags & FIN) str += "F";
	str += " window=";
	str += cvt_int(_window);
	str += " chk=";
	str += cvt_hex(_checksum);
	for (unsigned i = 0; i < _nopts; i++) {
		str += " ";
		str += _opts[i]->to_string();
	}
	str += ">";
        if (level > 1 && _data && _length > 0) {
                str += '\n';
                for (unsigned i = 0; i < _length; i++) {
                        str += xdigits[_data[i]>>4];
                        str += xdigits[_data[i] & 0xf];
                        if ((i % 16) == 15) str += '\n';
                        else str += ' ';
                }
                str += '\n';
        }
	return str;
}

void TCP::fixup()
{
}

bool TCP::do_build(Buffer &pkt, int phase, Word &pos)
{
	unsigned i;
	Word checksum;
	Octet *p;
	if (phase == 0) {
		checksum = 0;
		pos += 20;
		if (_nopts) {
			unsigned cpos = pos;
			for (i = 0; i < _nopts; i++) {
				if (!_opts[i]->do_build(pkt, 0, pos, checksum)) return false;
			}
			_dataofs = (pos+3 - cpos)/4+5;
		} else {
			_dataofs = 5;
		}
		checksum += underlayer()->pseudo_checksum(ProtoTCP, _length+_dataofs*4);
		checksum = _sport + _dport;
		checksum += (_seq >> 16) + (_seq & 0xffff);
		checksum += (_ack >> 16) + (_ack & 0xffff);
		checksum += (_dataofs << 12) + (_flags&0xfff);
		checksum += _window;
		checksum += _urgptr;
		if (_length > 0 && _data) {
			p = _data;
			for (i = 0; i < _length-1; i+=2,p+=2) {
				_checksum += (*p << 8) + p[1];
			}
			// last odd octet
			if (_length & 1) _checksum += (*p << 8);
			pos += _length;
		}
		checksum = (checksum & 0xffff) + (checksum >> 16);
		if (checksum > 65535) checksum -= 65535;
		_checksum = ~checksum;
		return true;
	}
	if (!pkt.add_hshort(_sport)) return false;
	pos += 2;
	if (!pkt.add_hshort(_dport)) return false;
	pos += 2;
	if (!pkt.add_hlong(_seq)) return false;
	pos += 4;
	if (!pkt.add_hlong(_ack)) return false;
	pos += 4;
	if (!pkt.add_octet((_dataofs<<4)+((_flags>>8)&0xf))) return false;
	pos++;
	if (!pkt.add_octet(_flags & 0xff)) return false;
	pos++;
	if (!pkt.add_hshort(_window)) return false;
	pos += 2;
	if (!pkt.add_hshort(_checksum)) return false;
	pos += 2;
	if (!pkt.add_hshort(_urgptr)) return false;
	pos += 2;
	if (_nopts) {
		for (i = 0; i < _nopts; i++) {
			if (!_opts[i]->do_build(pkt, 1, pos, checksum)) return false;
		}
		while ((pos % 4) != 0) {
			// end of options
			if (!pkt.add_octet(0)) return false;
			pos++;
		}
	}
	if (_length > 0) {
		p = _data;
		for (i = 0; i < _length; i++) {
			if (_data) {
				if (!pkt.add_octet(*p++)) return false;
			} else {
				if (!pkt.add_octet(0)) return false;
			}
			pos++;
		}
	}
	return true;
}

bool TCP::decode(Buffer &pkt)
{
	clear_opts();
	if (_own && _data) delete _data;
	_own = false;
	_data = 0;
	if (!pkt.get_hshort(_sport)) {
		if (debug > 0) cerr << "TCP::decode(): missing source port octets" << endl;
		return false;
	}
	if (!pkt.get_hshort(_dport)) {
		if (debug > 0) cerr << "TCP::decode(): missing destination port octets" << endl;
		return false;
	}
	if (!pkt.get_hlong(_seq)) {
		if (debug > 0) cerr << "TCP::decode(): missing sequence number octets" << endl;
		return false;
	}
	if (!pkt.get_hlong(_ack)) {
		if (debug > 0) cerr << "TCP::decode(): missing ack number octets" << endl;
		return false;
	}
	if (!pkt.get_hshort(_flags)) {
		if (debug > 0) cerr << "TCP::decode(): missing flags number octets" << endl;
		return false;
	}
	_dataofs = _flags >> 12;
	_flags &= 0xfff;
	if (!pkt.get_hshort(_window)) {
		if (debug > 0) cerr << "TCP::decode(): missing window octets" << endl;
		return false;
	}
	if (!pkt.get_hshort(_checksum)) {
		if (debug > 0) cerr << "TCP::decode(): missing checksum octets" << endl;
		return false;
	}
	if (!pkt.get_hshort(_urgptr)) {
		if (debug > 0) cerr << "TCP::decode(): missing urgent pointer octets" << endl;
		return false;
	}
	int lopts = (_dataofs*4)-20;
	while (lopts > 0) {
		Octet b;
		if (!pkt.get_octet(b)) return false;
		lopts--;
		TCP_Option *opt = TCP_Option::decode_option(pkt, b, lopts);
		if (opt != NULL && _nopts < MAX_OPTS) {
			_opts[_nopts++] = opt;
		}
	}
	_length = pkt.left();
	if (_length > 0) {
		_data = new Octet[_length];
		_own = true;
		for (unsigned i = 0; i < _length; i++) {
			if (!pkt.get_octet(_data[i])) return false;
		}
	}
	return true;
}

static Packet *TCP_factory(Buffer &pkt)
{
	return new TCP();
}


#endif
#endif
