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

// RFC826 over Ethernet
//
class ARP: public Packet
{
private:
	Short _htype;	// hardware type
	Short _ptype;	// protocol type
	Octet _hlen;	// hardware address length
	Octet _plen;	// protocol address length
	Short _op;	// operation
	MacAddress _sha;	// sender hardware address
	IPv4Address _spa;	// sender protocol address
	MacAddress _tha;	// target hardware address
	IPv4Address _tpa;	// target protocol address
	ARP(const ARP&);
	ARP& operator=(const ARP&);
public:
	enum { REQUEST = 1, REPLY = 2};
	ARP(): _htype(1), _ptype(0x800), _hlen(6), _plen(4), _op(REQUEST), _sha(), _spa(), _tha(), _tpa() { }
	void operation(int op) { _op = op; }
	int operation() const { return _op; }
	void set_sender(const MacAddress &sha, const IPv4Address &spa) { _sha = sha; _spa = spa; }
	void get_sender(MacAddress &sha, IPv4Address &spa) const { sha = _sha; spa = _spa; }
	void set_target(const MacAddress &tha, const IPv4Address &tpa) { _tha = tha; _tpa = tpa; }
	void get_target(MacAddress &tha, IPv4Address &tpa) const { tha = _tha; tpa = _tpa; }
	string to_string(int level = 0) const;
	string name() const { return "ARP"; }
	void fixup() { }
	bool do_build(Buffer &pkt, int phase, Word &pos);
	bool decode(Buffer &pkt);
	~ARP() { }

private:
	static Packet *ARP_factory();
};

#ifdef _LIB
static Packet *ARP_factory()
{
	Packet *p = new ARP();
	// if (debug > 1) cerr << "new IPv4 instance " << p << endl;
	return p;
}

class autoinit
{
public:
	autoinit() { Ethernet::add_proto(0x0806, ARP_factory);  }
};
static autoinit init;

string ARP::to_string(int level) const
{
	string ret("<ARP");
	if (_htype != 1) {
		ret += " htype=";
		ret += cvt_int(_htype);
	}
	if (_ptype != 0x800) {
		ret += " ptype=0x";
		ret += cvt_hex(_ptype);
	}
	if (_hlen != 6) {
		ret += " hlen=6";
		ret += cvt_int(_hlen);
	}
	if (_plen != 4) {
		ret += " plen=4";
		ret += cvt_int(_plen);
	}
	if (_plen != 4) {
		ret += " plen=4";
		ret += cvt_int(_plen);
	}
	if (_op == REQUEST) ret += " request";
	else
	if (_op == REPLY) ret += " reply";
	else {
		ret += " op=";
		ret += cvt_int(_op);
	}
	ret += " sha=";
	if (_sha.is_unspecified()) ret += "unknown";
	else ret += _sha.to_string(level);
	ret += " spa=";
	ret += _spa.to_string(level);
	ret += " tha=";
	if (_tha.is_unspecified()) ret += "unknown";
	else ret += _tha.to_string(level);
	ret += " tpa=";
	ret += _tpa.to_string(level);
	ret += ">";
	return ret;
}

bool ARP::do_build(Buffer &pkt, int phase, Word &pos)
{
	if (phase == 0) {
		pos += 28;
		_plen = pos;
		return true;
	}
	if (!pkt.add_hshort(_htype)) return false;
	pos += 2;
	if (!pkt.add_hshort(_ptype)) return false;
	pos += 2;
	if (!pkt.add_octet(_hlen)) return false;
	pos += 1;
	if (!pkt.add_octet(_plen)) return false;
	pos += 1;
	if (!pkt.add_hshort(_op)) return false;
	pos += 2;
	if (!_sha.build(pkt, phase)) return false;
	pos += 6;
	if (!_spa.build(pkt, phase)) return false;
	pos += 4;
	if (!_tha.build(pkt, phase)) return false;
	pos += 6;
	if (!_tpa.build(pkt, phase)) return false;
	pos += 4;
	return true;
}

bool ARP::decode(Buffer &pkt)
{
	pkt.set();
	if (!pkt.get_hshort(_htype)) {
		if (debug > 0) cerr << "ARP::decode(): missing hardware type " << endl;
		return false;
	}
	if (!pkt.get_hshort(_ptype)) {
		if (debug > 0) cerr << "ARP::decode(): missing protocol type " << endl;
		return false;
	}
	if (!pkt.get_octet(_hlen)) {
		if (debug > 0) cerr << "ARP::decode(): missing hardware address length " << endl;
		return false;
	}
	if (!pkt.get_octet(_plen)) {
		if (debug > 0) cerr << "ARP::decode(): missing protocol address length " << endl;
		return false;
	}
	if (!pkt.get_hshort(_op)) {
		if (debug > 0) cerr << "ARP::decode(): missing operation type " << endl;
		return false;
	}
	if (!_sha.decode(pkt)) {
		if (debug > 0) cerr << "ARP::decode(): missing sender hardware address" << endl;
		return false;
	}
	if (!_spa.decode(pkt)) {
		if (debug > 0) cerr << "ARP::decode(): missing sender protocol address" << endl;
		return false;
	}
	if (!_tha.decode(pkt)) {
		if (debug > 0) cerr << "ARP::decode(): missing target hardware address" << endl;
		return false;
	}
	if (!_tpa.decode(pkt)) {
		if (debug > 0) cerr << "ARP::decode(): missing target protocol address" << endl;
		return false;
	}
	return true;
}
#endif
#endif
