// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// vim:set sts=4 ts=8:

// Copyright (c) 2001-2011 XORP, Inc and Others-2009 XORP, Inc.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2, June
// 1991 as published by the Free Software Foundation. Redistribution
// and/or modification of this program under the terms of any other
// version of the GNU General Public License is not permitted.
// 
// 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. For more details,
// see the GNU General Public License, Version 2, a copy of which can be
// found in the XORP LICENSE.gpl file.
// 
// XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
// http://xorp.net

// $XORP: xorp/ospf/packet.hh,v 1.44 2008/10/02 21:57:48 bms Exp $

#ifndef __OSPF_PACKET_HH__
#define __OSPF_PACKET_HH__

/**
 * All packet decode routines must inherit from this interface.
 * Also provides some utility routines to perform common packet processing.
 */
class Packet {
 public:
    static const size_t STANDARD_HEADER_V2 = 24;
    static const size_t STANDARD_HEADER_V3 = 16;

    static const size_t VERSION_OFFSET = 0;
    static const size_t TYPE_OFFSET = 1;
    static const size_t LEN_OFFSET = 2;
    static const size_t ROUTER_ID_OFFSET = 4;
    static const size_t AREA_ID_OFFSET = 8;
    static const size_t CHECKSUM_OFFSET = 12;

    // OSPFv2 only.
    static const size_t AUTH_TYPE_OFFSET = 14;
    static const size_t AUTH_PAYLOAD_OFFSET = 16;
    static const size_t AUTH_PAYLOAD_SIZE = 8;

    // OSPFv3 only.
    static const size_t INSTANCE_ID_OFFSET = 14;

    Packet(OspfTypes::Version version)
	    : _version(version), _valid(false), _router_id(0), _area_id(0),
	      _auth_type(0), _instance_id(0)
    {
	memset(&_auth[0], 0, sizeof(_auth));
    }

    virtual ~Packet()
    {}

    /**
     * Decode standard header.
     *
     * Used by the derived classes to extract the standard header and
     * options field.
     *
     * @param len if the frame is larger than what is specified by the
     * header length field drop the length down to the header length.
     *
     * @return the offset where the specific header starts.
     */
    size_t decode_standard_header(uint8_t *ptr, size_t& len) throw(InvalidPacket);

    /**
     * Decode the packet.
     * The returned packet must be free'd.
     */
    virtual Packet *decode(uint8_t *ptr, size_t len)
	const throw(InvalidPacket) = 0;

    /**
     * Encode standard header.
     *
     * Used by the derived classes to put the standard header in the
     * packet. Which includes the checksum so all other fields should
     * already be in the packet.
     *
     * @param ptr location to start writing header.
     * @param len size of the buffer.
     * @return The offset that the header has been written to. Returns
     * 0 on failure, such as the buffer is too small.
     */
    size_t encode_standard_header(uint8_t *ptr, size_t len);

    /**
     * Encode the packet.
     *
     * @param pkt vector into which the packet should be placed.
     * @return true if the encoding suceeded.
     */
    virtual bool encode(vector<uint8_t>& pkt) = 0;

    /**
     * Store the original packet, required for authentication.
     */
    void store(uint8_t *ptr, size_t len) {
	_pkt.resize(len);
	memcpy(&_pkt[0], ptr, len);
    }

    /**
     * Get a reference to the original packet data, required for
     * authentication.
     */
    vector<uint8_t>& get() {
	return _pkt;
    }

    /**
     * @return The version this packet represents.
     */
    OspfTypes::Version get_version() const { return _version; }

    /**
     * It is the responsibilty of the derived type to return this
     * information.
     * @return The type this packet represents.
     */
    virtual OspfTypes::Type get_type() const = 0;

    /**
     * Get the Router ID.
     */
    OspfTypes::RouterID get_router_id() const { return _router_id; }

    /**
     * Set the Router ID.
     */
    void set_router_id(OspfTypes::RouterID id) { _router_id = id; }

    /**
     * Get the Area ID.
     */
    OspfTypes::AreaID get_area_id() const { return _area_id; }

    /**
     * Set the Area ID.
     */
    void set_area_id(OspfTypes::AreaID id) { _area_id = id; }    

    /**
     * Get the Auth Type.
     */
    uint16_t get_auth_type() const { return _auth_type; }

    /**
     * Set the Auth Type.
     */
    void set_auth_type(uint16_t auth_type) { _auth_type = auth_type; }

    /**
     * Get the Instance ID.
     */
    uint8_t get_instance_id() const { 
	XLOG_ASSERT(OspfTypes::V3 == get_version());
	return _instance_id;
    }

    /**
     * Set the Instance ID.
     */
    void set_instance_id(uint8_t instance_id) { 
	XLOG_ASSERT(OspfTypes::V3 == get_version());
	_instance_id = instance_id;
    }

    /**
     * @return the standard header length for this version of OSPF.
     */
    size_t get_standard_header_length() {
	switch(_version) {
	case OspfTypes::V2:
	    return STANDARD_HEADER_V2;
	    break;
	case OspfTypes::V3:
	    return STANDARD_HEADER_V3;
	    break;
	}
	XLOG_UNREACHABLE();
	return 0;
    }

    /**
     * Decode Point.
     * 
     * If a packet is being decoded the standard header has already been
     * processed. This method returns the offset at which the specific
     * data starts.
     *
     * @return The offset at which a derived class should start decoding.
     */
    size_t decode_point() const;

    /**
     * Generate a printable representation of the standard header.
     * Used by the derived classes implementing str().
     */
    string standard() const;

    /**
     * Generate a printable representation of the packet.
     */
    virtual string str() const = 0;

 private:
    const OspfTypes::Version 	_version;
    vector<uint8_t> _pkt;	// Raw packet

    /**
     * Set to true when the standard header fields are set.
     */
    bool _valid;

    /**
     * Standard header.
     * Version and Type information are constant so therefore not present.
     */
    OspfTypes::RouterID	_router_id;
    OspfTypes::AreaID	_area_id;
    OspfTypes::AuType	_auth_type;			// OSPFv2 Only
    uint8_t 		_auth[AUTH_PAYLOAD_SIZE];	// OSPFv2 Only
    uint8_t		_instance_id;			// OSPFv3 Only
};

/**
 * Packet byte streams are decoded through this class.
 */
class PacketDecoder {
 public:    
     ~PacketDecoder();

     /**
      * Register the packet/decode routines
      *
      * @param packet decoder
      */
    void register_decoder(Packet *packet);

#if	0
    /**
     * Register the packet/decode routines
     *
     * @param packet decoder
     * @param version OSPF version of the decoder
     * @param type of decoder
     */
    void register_decoder(Packet *packet,
			  OspfTypes::Version version,
			  OspfTypes::Type type);
#endif

    /**
     * Decode byte stream.
     *
     * @param ptr to data packet
     * @param length of data packet
     *
     * @return a packet structure, which must be free'd
     */
    Packet *decode(uint8_t *ptr, size_t len) throw(InvalidPacket);
 private:
    map<OspfTypes::Type , Packet *> _ospfv2;	// OSPFv2 Packet decoders
    map<OspfTypes::Type , Packet *> _ospfv3;	// OSPFv3 Packet decoders
};

/**
 * Hello packet
 */
class HelloPacket : public Packet {
 public:
    static const size_t MINIMUM_LENGTH = 20;	// The minumum length
						// of a hello packet.
						// The same for OSPFv2
						// and OSPFv3. How did
						// that happen?


    static const size_t DESIGNATED_ROUTER_OFFSET = 12;
    static const size_t BACKUP_DESIGNATED_ROUTER_OFFSET = 16;

    // OSPFv2
    static const size_t NETWORK_MASK_OFFSET = 0;
    static const size_t HELLO_INTERVAL_V2_OFFSET = 4;
    static const size_t OPTIONS_V2_OFFSET = 6;
    static const size_t ROUTER_PRIORITY_V2_OFFSET = 7;
    static const size_t ROUTER_DEAD_INTERVAL_V2_OFFSET = 8;

    // OSPFv3
    static const size_t INTERFACE_ID_OFFSET = 0;
    static const size_t ROUTER_PRIORITY_V3_OFFSET = 4;
    static const size_t OPTIONS_V3_OFFSET = 4;
    static const size_t HELLO_INTERVAL_V3_OFFSET = 8;
    static const size_t ROUTER_DEAD_INTERVAL_V3_OFFSET = 10;

    HelloPacket(OspfTypes::Version version)
	: Packet(version), _network_mask(0), _interface_id(0),
	  _hello_interval(0), _options(0), _router_priority(0),
	  _router_dead_interval(0)
    {}

    OspfTypes::Type get_type() const { return 1; }

    Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket);

    /**
     * Encode the packet.
     *
     * @param pkt vector into which the packet should be placed.
     * @return true if the encoding succeeded.
     */
    bool encode(vector<uint8_t>& pkt);

    // Network Mask.
    void set_network_mask(uint32_t network_mask) {
	XLOG_ASSERT(OspfTypes::V2 == get_version());
	_network_mask = network_mask;
    }

    uint32_t get_network_mask() const {
	XLOG_ASSERT(OspfTypes::V2 == get_version());
	return _network_mask;
    }

    // Interface ID.
    void set_interface_id(uint32_t interface_id) {
	XLOG_ASSERT(OspfTypes::V3 == get_version());
	_interface_id = interface_id;
    }

    uint32_t get_interface_id() const {
	XLOG_ASSERT(OspfTypes::V3 == get_version());
	return _interface_id;
    }

    // Hello Interval.
    void set_hello_interval(uint16_t hello_interval) {
	_hello_interval = hello_interval;
    }

    uint16_t get_hello_interval() const {
	return _hello_interval;
    }

    // Options.
    void set_options(uint32_t options) {
	switch(get_version()) {
	case OspfTypes::V2:
	    if (options > 0xff)
		XLOG_WARNING("Attempt to set %#x in an 8 bit field",
			     options);
	    _options = options & 0xff;
	    break;
	case OspfTypes::V3:
	    if (options  > 0xffffff)
		XLOG_WARNING("Attempt to set %#x in a 24 bit field",
			     options);
	    _options = options & 0xffffff;
	    break;
	}
    }

    uint32_t get_options() const {
	return _options;
    }

    // Router Priority.
    void set_router_priority(uint8_t router_priority) {
	_router_priority = router_priority;
    }

    uint8_t get_router_priority() const {
	return _router_priority;
    }

    // Router Dead Interval.
    void set_router_dead_interval(uint32_t router_dead_interval) {
	switch(get_version()) {
	case OspfTypes::V2:
	    _router_dead_interval = router_dead_interval;
	    break;
	case OspfTypes::V3:
	    if ( router_dead_interval > 0xffff)
		XLOG_WARNING("Attempt to set %#x in a 16 bit field",
			     router_dead_interval);
	    _router_dead_interval = router_dead_interval & 0xffff;
	    break;
	}
    }

    uint32_t get_router_dead_interval() const {
	return _router_dead_interval;
    }

    // Designated Router
    void set_designated_router(OspfTypes::RouterID dr) {
	_dr = dr;
    }

    OspfTypes::RouterID get_designated_router() const {
	return _dr;
    }
    
    // Backup Designated Router
    void set_backup_designated_router(OspfTypes::RouterID bdr) {
	_bdr = bdr;
    }

    OspfTypes::RouterID get_backup_designated_router() const {
	return _bdr;
    }
    
    list<OspfTypes::RouterID>& get_neighbours() {
	return _neighbours;
    }

    /**
     * Generate a printable representation of the packet.
     */
    string str() const;

 private:
    uint32_t _network_mask;	// OSPF V2 Only
    uint32_t _interface_id;	// OSPF V3 Only
    uint16_t _hello_interval;
    uint32_t _options;		// Large enough to accomodate V2 and V3 options
    uint8_t  _router_priority;	// Router Priority.
    uint32_t _router_dead_interval;	// In seconds.
    OspfTypes::RouterID _dr;	// Designated router.
    OspfTypes::RouterID _bdr;	// Backup Designated router.

    list<OspfTypes::RouterID> _neighbours; // Router IDs of neighbours.
};

/**
 * Database Description Packet
 */
class DataDescriptionPacket : public Packet {
 public:
    DataDescriptionPacket(OspfTypes::Version version)
	: Packet(version), _interface_mtu(0), _options(0),
	  _i_bit(false), _m_bit(false), _ms_bit(false), _DD_seqno(0)
    {}

    OspfTypes::Type get_type() const { return 2; }

    size_t minimum_length() const {
	switch(get_version()) {
	case OspfTypes::V2:
	    return 8;
	    break;
	case OspfTypes::V3:
	    return 12;
	    break;
	}
	XLOG_UNREACHABLE();
	return 0;
    }

    Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket);

    /**
     * Encode the packet.
     *
     * @param pkt vector into which the packet should be placed.
     * @return true if the encoding suceeded.
     */
    bool encode(vector<uint8_t>& pkt);

    // Interface MTU
    void set_interface_mtu(uint16_t mtu) {
	_interface_mtu = mtu;
    }

    uint16_t get_interface_mtu() const {
	return _interface_mtu;
    }

    // Options.
    void set_options(uint32_t options) {
	switch(get_version()) {
	case OspfTypes::V2:
	    if (options > 0xff)
		XLOG_WARNING("Attempt to set %#x in an 8 bit field",
			     options);
	    _options = options & 0xff;
	    break;
	case OspfTypes::V3:
	    if (options  > 0xffffff)
		XLOG_WARNING("Attempt to set %#x in a 24 bit field",
			     options);
	    _options = options & 0xffffff;
	    break;
	}
    }

    uint32_t get_options() const {
	return _options;
    }

    // Init bit.
    void set_i_bit(bool bit) {
	_i_bit = bit;
    }

    bool get_i_bit() const {
	return _i_bit;
    }

    // More bit.
    void set_m_bit(bool bit) {
	_m_bit = bit;
    }

    bool get_m_bit() const {
	return _m_bit;
    }

    // Master/Slave bit
    void set_ms_bit(bool bit) {
	_ms_bit = bit;
    }

    bool get_ms_bit() const {
	return _ms_bit;
    }
    
    // Database description sequence number.
    void set_dd_seqno(uint32_t seqno) {
	_DD_seqno = seqno;
    }
    
    uint32_t get_dd_seqno() const {
	return _DD_seqno;
    }

    list<Lsa_header>& get_lsa_headers() {
	return _lsa_headers;
    }
    
    /**
     * Generate a printable representation of the packet.
     */
    string str() const;

 private:
    uint16_t	_interface_mtu;
    uint32_t	_options;	// Large enough to accomodate V2 and V3 options
    bool	_i_bit;		// The init bit.
    bool	_m_bit;		// The more bit.
    bool	_ms_bit;	// The Master/Slave bit.
    uint32_t	_DD_seqno;	// Database description sequence number.
    
    list<Lsa_header> _lsa_headers;
};

/**
 * Link State Request Packet
 */
class LinkStateRequestPacket : public Packet {
 public:
    LinkStateRequestPacket(OspfTypes::Version version)
	: Packet(version)
    {}

    OspfTypes::Type get_type() const { return 3; }

    Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket);

    /**
     * Encode the packet.
     *
     * @param pkt vector into which the packet should be placed.
     * @return true if the encoding succeeded.
     */
    bool encode(vector<uint8_t>& pkt);
    
    list<Ls_request>& get_ls_request() {
	return _ls_request;
    }
    
    /**
     * Generate a printable representation of the packet.
     */
    string str() const;
 private:
    list<Ls_request> _ls_request;
};

/**
 * Link State Update Packet
 */
class LinkStateUpdatePacket : public Packet {
 public:
    LinkStateUpdatePacket(OspfTypes::Version version, LsaDecoder& lsa_decoder)
	: Packet(version), _lsa_decoder(lsa_decoder)
    {}

    OspfTypes::Type get_type() const { return 4; }

    Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket);

    /**
     * Encode the packet.
     *
     * @param pkt vector into which the packet should be placed.
     * @return true if the encoding succeeded.
     */
    bool encode(vector<uint8_t>& pkt);

    /**
     * Encode the packet.
     *
     * @param pkt vector into which the packet should be placed.
     * @param inftransdelay add this delay to the age field of each LSA.
     * @return true if the encoding succeeded.
     */
    bool encode(vector<uint8_t>& pkt, uint16_t inftransdelay);

    list<Lsa::LsaRef>& get_lsas() {
	return _lsas;
    }
    
    /**
     * Generate a printable representation of the packet.
     */
    string str() const;
    
 private:
    LsaDecoder& _lsa_decoder;	// LSA decoders.

    // The packet contains a field with the number of LSAs that it
    // contains there is no point in storing it.

    list<Lsa::LsaRef> _lsas;	// The list of LSAs in the packet.
};

/**
 * Link State Acknowledgement Packet
 */
class LinkStateAcknowledgementPacket : public Packet {
 public:
    LinkStateAcknowledgementPacket(OspfTypes::Version version)
	: Packet(version)
    {}

    OspfTypes::Type get_type() const { return 5; }

    Packet *decode(uint8_t *ptr, size_t len) const throw(InvalidPacket);

    /**
     * Encode the packet.
     *
     * @param pkt vector into which the packet should be placed.
     * @return true if the encoding succeeded.
     */
    bool encode(vector<uint8_t>& pkt);

    list<Lsa_header>& get_lsa_headers() {
	return _lsa_headers;
    }
    
    /**
     * Generate a printable representation of the packet.
     */
    string str() const;
    
 private:
    list<Lsa_header> _lsa_headers;
};

/**
 * The definitive list of packets. All decoder lists should be primed
 * using this function.
 */
inline
void
initialise_packet_decoder(OspfTypes::Version version,
			  PacketDecoder& packet_decoder, 
			  LsaDecoder& lsa_decoder)
{
    packet_decoder.register_decoder(new HelloPacket(version));
    packet_decoder.register_decoder(new DataDescriptionPacket(version));
    packet_decoder.register_decoder(new LinkStateUpdatePacket(version,
							       lsa_decoder));
    packet_decoder.register_decoder(new LinkStateRequestPacket(version));
    packet_decoder.
	register_decoder(new LinkStateAcknowledgementPacket(version));
}

/**
 * Helper class to manipulate the options field in packets and LSAs.
 */
class Options {
 public:
     static const uint32_t V6_bit = 0x1;
     static const uint32_t E_bit = 0x2;
     static const uint32_t MC_bit = 0x4;
     static const uint32_t N_bit = 0x8;
     static const uint32_t P_bit = N_bit;
     static const uint32_t R_bit = 0x10;
     static const uint32_t EA_bit = 0x10;
     static const uint32_t DC_bit = 0x20;

     Options(OspfTypes::Version version, uint32_t options)
	 : _version(version), _options(options)
     {
     }

     void set_bit(bool set, uint32_t bit) {
	 if (set)
	     _options |= bit;
	 else
	     _options &= ~bit;
	
     }
     bool get_bit(uint32_t bit) const { return _options & bit ? true : false; }

     void set_v6_bit(bool set) {
	 XLOG_ASSERT(OspfTypes::V3 == _version);
	 set_bit(set, V6_bit);
     }
     bool get_v6_bit() const {
	 XLOG_ASSERT(OspfTypes::V3 == _version);
	 return get_bit(V6_bit);
     }

     void set_e_bit(bool set) { set_bit(set, E_bit); }
     bool get_e_bit() const { return get_bit(E_bit); }

     void set_mc_bit(bool set) { set_bit(set, MC_bit); }
     bool get_mc_bit() const { return get_bit(MC_bit); }

     void set_n_bit(bool set) { set_bit(set, N_bit); }
     bool get_n_bit() const { return get_bit(N_bit); }

     void set_p_bit(bool set) { set_n_bit(set); }
     bool get_p_bit() const { return get_n_bit(); }

     void set_r_bit(bool set) {
	 XLOG_ASSERT(OspfTypes::V3 == _version);
	 set_bit(set, R_bit);
     }
     bool get_r_bit() const {
	 XLOG_ASSERT(OspfTypes::V3 == _version);
	 return get_bit(R_bit);
     }

     void set_ea_bit(bool set) {
	 XLOG_ASSERT(OspfTypes::V2 == _version);
	 set_bit(set, EA_bit);
     }
     bool get_ea_bit() const {
	 XLOG_ASSERT(OspfTypes::V2 == _version);
	 return get_bit(EA_bit);
     }

     void set_dc_bit(bool set) { set_bit(set, DC_bit); }
     bool get_dc_bit() const { return get_bit(DC_bit); }
     
     uint32_t get_options() { return _options; }

     string pp_bool(bool val) const { return val ? "1" : "0"; }

     string str() const {
	 string out;

	 switch(_version) {
	 case OspfTypes::V2:
	     out = "DC: " + pp_bool(get_dc_bit());
	     out += " EA: " + pp_bool(get_ea_bit());
	     out += " N/P: " + pp_bool(get_n_bit());
	     out += " MC: " + pp_bool(get_mc_bit());
	     out += " E: " + pp_bool(get_e_bit());
	     break;
	 case OspfTypes::V3:
	     out = "DC: " + pp_bool(get_dc_bit());
	     out += " R: " + pp_bool(get_r_bit());
	     out += " N: " + pp_bool(get_n_bit());
	     out += " MC: " + pp_bool(get_mc_bit());
	     out += " E: " + pp_bool(get_e_bit());
	     out += " V6: " + pp_bool(get_v6_bit());
	     break;
	 }

	 return out;
     }
	
 private:
     OspfTypes::Version _version;
     uint32_t _options;
};

/**
 * Verify the checksum of an IPv6 PDU, throw an exception if the
 * checksum doesn't match.
 *
 * In IPv6 the payload is not checksummed it is up to the protocol to
 * checksum its own payload. The checksum includes a pseduo header
 * that is described in RFC 2460 section 8.1
 * 
 * @param src Source address of packet.
 * @param dst Destination address of packet.
 * @param data pointer to payload.
 * @param len length of payload.
 * @param checksum_offset offset of checksum in the payload.
 * @param protocol protocol number.
 */
template <typename A> 
void
ipv6_checksum_verify(const A& src, const A& dst,
		     const uint8_t *data, size_t len,
		     size_t checksum_offset, uint8_t protocol)
    throw(InvalidPacket);

/**
 * Compute the IPv6 checksum and apply it to the packet provided. If
 * the checksum_offset is outside the packet then an exception is thrown.
 *
 * In IPv6 the payload is not checksummed it is up to the protocol to
 * checksum its own payload. The checksum includes a pseduo header
 * that is described in RFC 2460 section 8.1
 * 
 * @param src Source address of packet.
 * @param dst Destination address of packet.
 * @param data pointer to payload.
 * @param len length of payload.
 * @param checksum_offset offset of checksum in the payload.
 * @param protocol protocol number.
 */
template <typename A> 
void
ipv6_checksum_apply(const A& src, const A& dst,
		    uint8_t *data, size_t len,
		    size_t checksum_offset, uint8_t protocol)
    throw(InvalidPacket);

#endif // __OSPF_PACKET_HH__
