//#include "ndwatch/packet.h"
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the BUT OPEN SOURCE LICENCE
// Version 1 as published by the Brno University of Technology, Antonínská 548/1,
// 601 90, Czech Republic.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// BUT OPEN SOURCE LICENCE (BUTOSL) for more details.
// 
// You should have received a copy of the BUT OPEN SOURCE LICENCE (BUTOSL)
// along with this program; if not, write to the Brno University of Technology,
// Antonínská 548/1, 601 90, Czech Republic.



#ifndef DHCPV6_H
#define DHCPV6_H

#include <stdio.h>
#include <string>
#include <sstream>

#include "ndwatch/ethernet.h"
#include "ndwatch/ipv6.h"


using namespace std;

class DUID
{
private:
	Octet *_duid;
	int _len;
public:
	enum{DUID_LLT=1,DUID_EN=2,DUID_LL=3};

	DUID (): _duid(NULL), _len(0){};
	DUID (const DUID& duid);
	DUID& operator= (const DUID& duid);
	virtual string name() {return "DUID";}
	static DUID *decode_duid(Buffer &pkt, int len);
	virtual bool decode(Buffer &pkt, int len);
	string to_string();
	
	virtual int code() {return 0;}
	virtual ~DUID(){ if(_duid) delete[] _duid;}
};

/*
class DUID_HW_Identifier
{
public:
	enum{MAC=1,EUI64=27};

	virtual string name() {return "DUID_HW_Identifier";}
	static DUID_HW_Identifier *decode_DUID_HW_Identifier(Buffer &pkt, int hw_type);
	virtual bool decode(Buffer &pkt) = 0;
	virtual int code() {return 0;}
	virtual string type() const {return "None";}
	virtual DUID_HW_Identifier *copy() const = 0;
	virtual string to_string() const = 0;
	virtual ~DUID_HW_Identifier(){}
};

class DUID_EUI64: public DUID_HW_Identifier
{
private:
	Octet _addr[8];
public:
	virtual string name() {return "DUID_EUI64";}
	virtual string type() const {return "EUI-64";}
	string to_string() const {
		string s = "00:00:00:00:00:00:00:00";
		for (int i = 0; i < 8; i++) {
			s[i*3] = xdigits[_addr[i]>>4 & 0xf];
			s[i*3+1] = xdigits[_addr[i] & 0xf];
		}
		return s;
	}
	virtual bool decode(Buffer &pkt){
		for (int i = 0; i < 8; i++){
			if (!pkt.get_octet(_addr[i])) return false;
		}
		return true;
	}
	virtual int code() {return EUI64;}
	virtual DUID_HW_Identifier *copy() const {return new DUID_EUI64(*this);}
};

class DUID_MAC: public DUID_HW_Identifier
{
private:
	MacAddress _address;
public:
	DUID_MAC(): _address("00:00:00:00:00:00"){}
	virtual string name() {return "DUID_MAC";}
	virtual string type() const {return "MAC";}
	virtual int code() {return MAC;}
	string to_string() const {
		return _address.to_string();
	}
	virtual bool decode(Buffer &pkt){
		return _address.decode(pkt);
	}
	virtual DUID_HW_Identifier *copy() const {return new DUID_MAC(*this);}
};


//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |               1               |    hardware type (16 bits)    |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                        time (32 bits)                         |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// .                                                               .
// .             link-layer address (variable length)              .
// .                                                               .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

class DUID_LLT_Format: public DUID
{
private:
	Short _hw_type;
	Word _time;
	DUID_HW_Identifier *_address;

	DUID_LLT_Format(const DUID_LLT_Format&);
	DUID_LLT_Format& operator=(const DUID_LLT_Format&);
public:

	DUID_LLT_Format(): _hw_type(0), _time(0), _address(NULL){}
	virtual string name() {return "DUID_LLT_Format";}
	virtual bool decode(Buffer &pkt);
	Short hw_type(){return _hw_type;}
	Word time(){return _time;}
	virtual int code() {return DUID_LLT;}
	DUID_HW_Identifier *address(){return _address;}
	virtual ~DUID_LLT_Format()
	{
		if(_address != NULL){
			delete _address;
		}
	}
};


class DUID_LL_Format: public DUID
{
private:
	Short _hw_type;
	DUID_HW_Identifier *_address;

	DUID_LL_Format(const DUID_LL_Format&);
	DUID_LL_Format& operator=(const DUID_LL_Format&);
public:

	DUID_LL_Format(): _hw_type(0), _address(NULL){}
	virtual string name() {return "DUID_LL_Format";}
	virtual bool decode(Buffer &pkt);
	Short hw_type(){return _hw_type;}
	virtual int code() {return DUID_LL;}
	DUID_HW_Identifier *address(){return _address;}
	virtual ~DUID_LL_Format()
	{
		if(_address != NULL){
			delete _address;
		}
	}
};*/

//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |          option-code          |           option-len          |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                          option-data                          |
// |                      (option-len octets)                      |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 
class DHCPv6_Option
{
private:
	Short _len;

public:
	enum{OPTION_CLIENTID=1,OPTION_SERVERID=2,OPTION_IA_NA=3,
		OPTION_IA_TA=4,OPTION_IAADDR=5,OPTION_ORO=6,OPTION_PREFERENCE=7,
		OPTION_ELAPSED_TIME=8,OPTION_RELAY_MSG=9,OPTION_AUTH=11,
		OPTION_UNICAST=12,OPTION_STATUS_CODE=13,OPTION_RAPID_COMMIT=14,
		OPTION_USER_CLASS=15,OPTION_VENDOR_CLASS=16,OPTION_VENDOR_OPTS=17,
		OPTION_INTERFACE_ID=18,OPTION_RECONF_MSG=19,OPTION_RECONF_ACCEPT=20,
		OPTION_IA_PD=25,OPTION_IAPREFIX=26};
	// there is no option 10 in rfc!

	DHCPv6_Option():_len(0){}
	virtual string name() {return "DHCPv6_Option";}
	//Short code(){return _code;}
	virtual Short len(){return _len;}
	static DHCPv6_Option *decode_option(Buffer &pkt);
	virtual bool decode(Buffer &pkt);
	virtual int code() {return 0;}
	virtual DHCPv6_Option *option(int i) {return 0;}
	virtual ~DHCPv6_Option(){}
};



//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |        OPTION_CLIENTID=1      |          option-len           |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// .                                                               .
// .                              DUID                             .
// .                        (variable length)                      .
// .                                                               .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


class DHCPv6_CLIENTID_Option: public DHCPv6_Option
{
private:
	Short _len;
	DUID *_duid;

	DHCPv6_CLIENTID_Option(const DHCPv6_CLIENTID_Option&);
	DHCPv6_CLIENTID_Option& operator=(const DHCPv6_CLIENTID_Option&);
public:
	DHCPv6_CLIENTID_Option(): _len(0), _duid(NULL) {}
	virtual string name() {return "DHCPv6_CLIENTID_Option";}
	Short len(){return _len;}
	DUID *duid(){return _duid;}

	virtual int code() {return OPTION_CLIENTID;}
	virtual bool decode(Buffer &pkt);
	virtual ~DHCPv6_CLIENTID_Option()
	{
		if(_duid != NULL){
			delete _duid;
		}
	}
};


// Identity Association for Non-temporary Addresses Option
//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |      OPTION_IA_NA = 3         |          option-len           |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                        IAID (4 octets)                        |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                              T1                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                              T2                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                                                               |
// .                         IA_NA-options                         .
// .                                                               .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// IAID                 The unique identifier for this IA_NA; the
//                      IAID must be unique among the identifiers for
//                      all of this client's IA_NAs.  The number
//                      space for IA_NA IAIDs is separate from the
//                      number space for IA_TA IAIDs.
//
// T1                   The time at which the client contacts the
//                      server from which the addresses in the IA_NA
//                      were obtained to extend the lifetimes of the
//                      addresses assigned to the IA_NA; T1 is a
//                      time duration relative to the current time
//                      expressed in units of seconds.
//
// T2                   The time at which the client contacts any
//                      available server to extend the lifetimes of
//                      the addresses assigned to the IA_NA; T2 is a
//                      time duration relative to the current time
//                      expressed in units of seconds.
//
// IA_NA-options        Options associated with this IA_NA.


class DHCPv6_IA_NA_Option: public DHCPv6_Option
{
public:
	enum { MAX_OPTS = 20};
private:
	Short _len;
	Word _IAID;
	Word _T1;
	Word _T2;
	DHCPv6_Option *_opts[MAX_OPTS];

	DHCPv6_IA_NA_Option(const DHCPv6_IA_NA_Option&);
	DHCPv6_IA_NA_Option& operator=(const DHCPv6_IA_NA_Option&);
public:
	DHCPv6_IA_NA_Option(): _len(0), _IAID(0), _T1(0), _T2(0){memset(_opts,0,MAX_OPTS * sizeof(DHCPv6_Option*));}
	static DHCPv6_Option *decode_option(Buffer &pkt){return DHCPv6_Option::decode_option(pkt);}
	virtual string name() {return "DHCPv6_IA_NA_Option";}
	virtual void len(Word len){_len = len;}
	Short len(){return _len;}
	void IAID(Word iaid){_IAID = iaid;}
	virtual int code() {return OPTION_IA_NA;}
	Word IAID(){return _IAID;}
	void T1(Word t1){_T1 = t1;}
	Word T1(){return _T1;}
	void T2(Word t2){_T1 = t2;}
	Word T2(){return _T2;}
	DHCPv6_Option *option(int i);

	virtual bool decode(Buffer &pkt);
	virtual ~DHCPv6_IA_NA_Option()
	{
		for(int i = 0; i<MAX_OPTS;i++){
			if(_opts[i] != NULL){
				delete _opts[i];
			}
		}
	}
};



//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |          OPTION_IAADDR = 5    |          option-len           |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                                                               |
// |                         IPv6 address                          |
// |                                                               |
// |                                                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                      preferred-lifetime                       |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                        valid-lifetime                         |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// .                                                               .
// .                        IAaddr-options                         .
// .                                                               .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

class DHCPv6_IAADDR_Option: public DHCPv6_Option
{
public:
	enum { MAX_OPTS = 20};
private:
	Short _len;
	IPv6Address _address;
	Word _preferred_lifetime;
	Word _valid_lifetime;
	DHCPv6_Option *_opts[MAX_OPTS];

	DHCPv6_IAADDR_Option(const DHCPv6_IAADDR_Option&);
	DHCPv6_IAADDR_Option& operator=(const DHCPv6_IAADDR_Option&);
public:
	DHCPv6_IAADDR_Option(): _len(0), _address("::"),  _preferred_lifetime(0), _valid_lifetime(0){memset(_opts,0,MAX_OPTS * sizeof(DHCPv6_Option*));}
	static DHCPv6_Option *decode_option(Buffer &pkt){return DHCPv6_Option::decode_option(pkt);}
	virtual string name() {return "DHCPv6_IAADDR_Option";}
	virtual void len(Word len){_len = len;}
	Short len(){return _len;}
	DHCPv6_Option *option(int i);
	Word preferred_lifetime(){return _preferred_lifetime;}
	virtual int code() {return OPTION_IAADDR;}
	Word valid_lifetime(){return _valid_lifetime;}
	IPv6Address *address(){return &_address;}

	virtual bool decode(Buffer &pkt);
	virtual ~DHCPv6_IAADDR_Option()
	{
		for(int i = 0; i<MAX_OPTS;i++){
			if(_opts[i] != NULL){
				delete _opts[i];
			}
		}
	}
};


//     0                   1                   2                   3
//     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |        OPTION_RELAY_MSG       |           option-len          |
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//    |                                                               |
//    .                       DHCP-relay-message                      .
//    .                                                               .
//    .                                                               .
//    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//    option-code   OPTION_RELAY_MSG (9)
//
//    option-len    Length of DHCP-relay-message
//
//    DHCP-relay-message In a Relay-forward message, the received
//                  message, relayed verbatim to the next relay agent
//                  or server; in a Relay-reply message, the message to
//                  be copied and relayed to the relay agent or client
//                  whose address is in the peer-address field of the
//                  Relay-reply message
//
//

class DHCPv6_Relay_msg_Option: public DHCPv6_Option
{
private:
	Short _len;
	class DHCPv6 *_relayed_DHCPv6;

	DHCPv6_Relay_msg_Option(const DHCPv6_Relay_msg_Option&);
	DHCPv6_Relay_msg_Option& operator=(const DHCPv6_Relay_msg_Option&);
public:
	DHCPv6_Relay_msg_Option(): _len(0), _relayed_DHCPv6(NULL){}
	virtual string name() {return "DHCPv6_Relay_msg_Option";}
	Short len(){return _len;}
	DHCPv6 *relayed_DHCPv6(){return _relayed_DHCPv6;}

	virtual int code() {return OPTION_RELAY_MSG;}
	virtual bool decode(Buffer &pkt);
	virtual ~DHCPv6_Relay_msg_Option();
};



//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |       OPTION_STATUS_CODE      |         option-len            |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |          status-code          |                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
// .                                                               .
// .                        status-message                         .
// .                                                               .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//   option-code          OPTION_STATUS_CODE (13).
//
//   option-len           2 + length of status-message.
//
//   status-code          The numeric code for the status encoded in
//                        this option.  The status codes are defined in
//                        section 24.4.
//
//   status-message       A UTF-8 encoded text string suitable for
//                        display to an end user, which MUST NOT be
//                        null-terminated.

class DHCPv6_Status_Code_Option: public DHCPv6_Option
{
public:
	enum { SUCCESS = 0, UNSPECFAIL = 1, NOADDRSAVAIL = 2, NOBINDING = 3,
		NOTONLINK = 4, USEMULTICAST = 5 };
private:
	Short _len;
	Short _status_code;
	string _status_message;

public:
	DHCPv6_Status_Code_Option(): _len(0), _status_code(0), _status_message("") {}
	virtual string name() {return "DHCPv6_Status_Code_Option";}
	Short len(){return _len;}
	Short status_code(){ return _status_code;}
	string status_message(){return _status_message;}

	virtual int code() {return OPTION_STATUS_CODE;}
	virtual bool decode(Buffer &pkt);
	virtual ~DHCPv6_Status_Code_Option(){}
};




//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |         OPTION_IA_PD          |         option-length         |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                         IAID (4 octets)                       |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                              T1                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                              T2                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// .                                                               .
// .                          IA_PD-options                        .
// .                                                               .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// option-code:      OPTION_IA_PD (25)
//
// option-length:    12 + length of IA_PD-options field.

class DHCPv6_IA_PD_Option: public DHCPv6_Option
{
public:
	enum { MAX_OPTS = 20};
private:
	Short _len;
	Word _IAID;
	Word _T1;
	Word _T2;
	DHCPv6_Option *_opts[MAX_OPTS];

	DHCPv6_IA_PD_Option(const DHCPv6_IA_PD_Option&);
	DHCPv6_IA_PD_Option& operator=(const DHCPv6_IA_PD_Option&);
public:
	DHCPv6_IA_PD_Option(): _len(0), _IAID(0), _T1(0), _T2(0){memset(_opts,0,MAX_OPTS * sizeof(DHCPv6_Option*));}
	static DHCPv6_Option *decode_option(Buffer &pkt){return DHCPv6_Option::decode_option(pkt);}
	virtual string name() {return "DHCPv6_IA_PD_Option";}
	virtual void len(Word len){_len = len;}
	Short len(){return _len;}
	void IAID(Word iaid){_IAID = iaid;}
	virtual int code() {return OPTION_IA_PD;}
	Word IAID(){return _IAID;}
	void T1(Word t1){_T1 = t1;}
	Word T1(){return _T1;}
	void T2(Word t2){_T1 = t2;}
	Word T2(){return _T2;}
	DHCPv6_Option *option(int i);

	virtual bool decode(Buffer &pkt);
	virtual ~DHCPv6_IA_PD_Option()
	{
		for(int i = 0; i<MAX_OPTS;i++){
			if(_opts[i] != NULL){
				delete _opts[i];
			}
		}
	}
};


// 0                   1                   2                   3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//|        OPTION_IAPREFIX        |         option-length         |
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//|                      preferred-lifetime                       |
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//|                        valid-lifetime                         |
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//| prefix-length |                                               |
//+-+-+-+-+-+-+-+-+          IPv6 prefix                          |
//|                                                               |
//|                           (16 octets)                         |
//|                                                               |
//|                                                               |
//|               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//|               |                                               .
//+-+-+-+-+-+-+-+-+                                               .
//.                        IAprefix-options                       .
//.                                                               .
//+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//option-code:      OPTION_IAPREFIX (26)
//
//option-length:    25 + length of IAprefix-options field
//
//preferred-lifetime: The recommended preferred lifetime for the IPv6
//                    prefix in the option, expressed in units of
//                   seconds.  A value of 0xFFFFFFFF represents
//                    infinity.
//
//valid-lifetime:   The valid lifetime for the IPv6 prefix in the
//                  option, expressed in units of seconds.  A value of
//                  0xFFFFFFFF represents infinity.
//prefix-length:    Length for this prefix in bits
//
//IPv6-prefix:      An IPv6 prefix
//
//IAprefix-options: Options associated with this prefix


class DHCPv6_IAPREFIX_Option: public DHCPv6_Option
{
public:
	enum { MAX_OPTS = 20};
private:
	Short _len;
	Word _preferred_lifetime;
	Word _valid_lifetime;
	Octet _prefix_length;
	IPv6Address_prefix _prefix;
	DHCPv6_Option *_opts[MAX_OPTS];

	DHCPv6_IAPREFIX_Option(const DHCPv6_IAPREFIX_Option&);
	DHCPv6_IAPREFIX_Option& operator=(const DHCPv6_IAPREFIX_Option&);
public:
	DHCPv6_IAPREFIX_Option(): _len(0),  _preferred_lifetime(0), _valid_lifetime(0),
	_prefix_length(0) {memset(_opts,0,MAX_OPTS * sizeof(DHCPv6_Option*));}
	static DHCPv6_Option *decode_option(Buffer &pkt){return DHCPv6_Option::decode_option(pkt);}
	virtual string name() {return "DHCPv6_IAPREFIX_Option";}
	virtual void len(Word len){_len = len;}
	Short len(){return _len;}
	DHCPv6_Option *option(int i);
	Word preferred_lifetime(){return _preferred_lifetime;}
	virtual int code() {return OPTION_IAPREFIX;}
	Word valid_lifetime(){return _valid_lifetime;}
	IPv6Address_prefix *prefix(){return &_prefix;}

	virtual bool decode(Buffer &pkt);
	virtual ~DHCPv6_IAPREFIX_Option()
	{
		for(int i = 0; i<MAX_OPTS;i++){
			if(_opts[i] != NULL){
				delete _opts[i];
			}
		}
	}
};


//  0                   1                   2                   3
//  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |    msg-type   |               transaction-id                  |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |                                                               |
// .                            options                            .
// .                           (variable)                          .
// |                                                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

// DHCPv6 relay-reply (13) relay-forw(12)
// 0                   1                   2                   3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |    msg-type   |   hop-count   |                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
// |                                                               |
// |                         link-address                          |
// |                                                               |
// |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
// |                               |                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
// |                                                               |
// |                         peer-address                          |
// |                                                               |
// |                               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
// |                               |                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
// .                                                               .
// .            options (variable number and length)   ....        .
// |                                                               |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

class DHCPv6
{
public:
	enum { MAX_OPTS = 100};
	enum { SOLICIT=1,ADVERTISE=2,REQUEST=3,CONFIRM=4,RENEW=5,
		REBIND=6,REPLY=7,RELEASE=8,DECLINE=9,RECONFIGURE=10,
		INFORMATION_REQUEST=11,RELAY_FORW=12,RELAY_REPL=13};

private:
	Octet _msg_type;
	Octet _transaction_id[3];
	DHCPv6_Option *_opts[MAX_OPTS];
	//relay
	Octet _hop_count;
	IPv6Address _link_address;
	IPv6Address _pear_address;
	DHCPv6(const DHCPv6&);
	DHCPv6& operator=(const DHCPv6&);

public:
	DHCPv6(): _msg_type(0), _hop_count(0), _link_address("::"), _pear_address("::"){
		memset(_opts,0,MAX_OPTS * sizeof(DHCPv6_Option *));
	}
	Octet msg_type() {return _msg_type;}

	DHCPv6_Option *option(int i);
	int transaction_id();

	bool decode(Buffer &pkt, int len);
	~DHCPv6(){
		for(int i = 0; i<MAX_OPTS;i++){
			if(_opts[i] != NULL){
				delete _opts[i];
			}
		}
	}
};


#endif /* DHCPV6_H */
