// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2001-2011 XORP, Inc and Others
//
// 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/rip/packets.hh,v 1.30 2008/10/02 21:58:16 bms Exp $

#ifndef __RIP_PACKET_ENTRIES_HH__
#define __RIP_PACKET_ENTRIES_HH__

#include "libxorp/ipv4.hh"
#include "libxorp/ipv4net.hh"
#include "libxorp/ipv6.hh"
#include "libxorp/ipv6net.hh"
#include "libxorp/utility.h"

#include "libproto/packet.hh"

#include "constants.hh"

/**
 * @short Header appearing at the start of each RIP packet.
 *
 * The RIP packet header has the following content:
 *
 * command (1 byte):	// The command: 1 - request, 2 - response
 * version (1 byte):	// 1 - IPv4 RIPv1/IPv6 RIPng, 2 - IPv4 RIP v2
 * unused0 (1 byte):	// Unused
 * unused1 (1 byte):	// Unused
 */
class RipPacketHeader {
public:
    RipPacketHeader(const uint8_t* data)
	: _data(data),
	  _command(_data + _command_offset),
	  _version(_data + _version_offset),
	  _unused0(_data + _unused0_offset),
	  _unused1(_data + _unused1_offset)
    {
	x_static_assert(RipPacketHeader::SIZE == _command_sizeof
		      + _version_sizeof + _unused0_sizeof + _unused1_sizeof);
	x_static_assert(RipPacketHeader::SIZE == _unused1_offset
		      + _unused1_sizeof);
    }

    static const uint8_t REQUEST  = 1;
    static const uint8_t RESPONSE = 2;
    static const uint8_t IPv4_VERSION = RIP_AF_CONSTANTS<IPv4>::PACKET_VERSION;
    static const uint8_t IPv6_VERSION = RIP_AF_CONSTANTS<IPv6>::PACKET_VERSION;
    static const size_t SIZE = 4;	// The header size

    /**
     * Get the RIP packet header size.
     *
     * @return the RIP packet header size.
     */
    static size_t size()	{ return RipPacketHeader::SIZE; }

    uint8_t command() const	{ return extract_8(_command); }
    uint8_t version() const	{ return extract_8(_version); }
    uint8_t unused0() const	{ return extract_8(_unused0); }
    uint8_t unused1() const	{ return extract_8(_unused1); }

    bool valid_command() const;
    bool valid_version(uint8_t v) const { return version() == v; }
    bool valid_padding() const;

protected:
    // Sizes of the fields
    static const size_t _command_sizeof = 1;
    static const size_t _version_sizeof = 1;
    static const size_t _unused0_sizeof = 1;
    static const size_t _unused1_sizeof = 1;

    // Offsets for the fields
    static const size_t _command_offset = 0;
    static const size_t _version_offset = _command_offset + _command_sizeof;
    static const size_t _unused0_offset = _version_offset + _version_sizeof;
    static const size_t _unused1_offset = _unused0_offset + _unused0_sizeof;

private:
    const uint8_t* _data;	// The buffer data

    // Pointers to the fields
    const uint8_t* _command;	// 1 - request, 2 - response
    const uint8_t* _version;	// 1 - IPv4 RIPv1/IPv6 RIPng, 2 - IPv4 RIP v2
    const uint8_t* _unused0;
    const uint8_t* _unused1;
};

inline bool
RipPacketHeader::valid_command() const
{
    return command() == REQUEST || command() == RESPONSE;
}

inline bool
RipPacketHeader::valid_padding() const
{
    return (unused0() | unused1()) == 0;
}

/*
 * @short Class for writing data to RIP packet header.
 */
class RipPacketHeaderWriter : public RipPacketHeader {
public:
    RipPacketHeaderWriter(uint8_t* data)
	: RipPacketHeader(data),
	  _data(data),
	  _command(_data + _command_offset),
	  _version(_data + _version_offset),
	  _unused0(_data + _unused0_offset),
	  _unused1(_data + _unused1_offset)
    {}

    void initialize(uint8_t cmd, uint8_t vers);

private:
    uint8_t* _data;		// The buffer data

    // Pointers to the fields
    uint8_t* _command;		// 1 - request, 2 - response
    uint8_t* _version;		// 1 - IPv4 RIPv1/IPv6 RIPng, 2 - IPv4 RIP v2
    uint8_t* _unused0;
    uint8_t* _unused1;
};

inline void
RipPacketHeaderWriter::initialize(uint8_t cmd, uint8_t vers)
{
    embed_8(_command, cmd);
    embed_8(_version, vers);
    embed_8(_unused0, 0);
    embed_8(_unused1, 0);
}

/**
 * @short Route Entry as it appears in a RIP Packet.
 *
 * This structure is useful only it's specialized forms
 * @ref PacketRouteEntry<IPv4> and @ref PacketRouteEntry<IPv6>.
 */
template <typename A>
class PacketRouteEntry {
};

/*
 * @short Class for writing data to RIP route entry.
 *
 * This structure is useful only it's specialized forms
 * @ref PacketRouteEntryWriter<IPv4> and @ref PacketRouteEntryWriter<IPv6>.
 */
template <typename A>
class PacketRouteEntryWriter {
};

/**
 * Smallest RIPv2 packet size.
 */
static const size_t RIPv2_MIN_PACKET_BYTES = 4;
static const size_t RIP_MIN_PACKET_BYTES = 4;

/**
 * Smallest authenticated RIPv2 packet size.
 */
static const size_t RIPv2_MIN_AUTH_PACKET_BYTES = 4 + 20;

/**
 * Largest RIPv2 packet size.
 */
static const size_t RIPv2_MAX_PACKET_BYTES = 4 + 20 * RIPv2_ROUTES_PER_PACKET;


/**
 * @short Route Entry appearing in RIP packets on IPv4.
 *
 * This payload is carried in RIP packets on IPv4.  The entry contains
 * all the fields for RIPv2.  RIPv1 and RIPv2 use the same size
 * structure, except RIPv1 treats the route tag, subnet mask and
 * nexthop fields as Must-Be-Zero (MBZ) items.  The interpretation of
 * the fields is described in RFC2453.
 *
 * All items in the route entry are stored in network order.  The
 * accessor methods provide values in host order, and the modifiers
 * take arguments in host order.
 *
 * The route entry has the following content:
 *
 * af (2 bytes):	// The address family
 * tag (2 bytes):	// Tag
 * addr (4 bytes):	// Address
 * mask (4 bytes):	// Mask
 * nh (4 bytes):	// Next hop
 * metric (4 bytes):	// Metric
 */
template <>
class PacketRouteEntry<IPv4> {
public:
    PacketRouteEntry(const uint8_t* data)
	: _data(data),
	  _af(_data + _af_offset),
	  _tag(_data + _tag_offset),
	  _addr(_data + _addr_offset),
	  _mask(_data + _mask_offset),
	  _nh(_data + _nh_offset),
	  _metric(_data + _metric_offset)
    {
	x_static_assert(PacketRouteEntry<IPv4>::SIZE == _af_sizeof + _tag_sizeof
		      + _addr_sizeof + _mask_sizeof + _nh_sizeof
		      + _metric_sizeof);
	x_static_assert(PacketRouteEntry<IPv4>::SIZE == _metric_offset
		      + _metric_sizeof);
    }

    static const size_t SIZE = 20;	// The entry size

    /**
     * Get the RIP IPv4 route entry size.
     *
     * @return the RIP IPv4 route entry size.
     */
    static size_t size()	{ return PacketRouteEntry<IPv4>::SIZE; }

    uint16_t	addr_family() const 	{ return extract_16(_af); }
    uint16_t	tag() const		{ return extract_16(_tag); }
    IPv4	addr() const		{ return IPv4(_addr); }
    uint32_t	prefix_len() const;
    IPv4Net	net() const;
    IPv4	nexthop() const		{ return IPv4(_nh); }
    uint32_t	metric() const		{ return extract_32(_metric); }

    /**
     * @return true if route entry has properties of a table request.
     */
    bool	is_table_request() const;

    /**
     * @return true if route entry has properties of an authentication entry.
     */
    bool	is_auth_entry() const;

    static const uint16_t ADDR_FAMILY = 2;
    static const uint16_t ADDR_FAMILY_DUMP = 0;
    static const uint16_t ADDR_FAMILY_AUTH = 0xffff;

protected:
    // Sizes of the fields
    static const size_t _af_sizeof	= 2;
    static const size_t _tag_sizeof	= 2;
    static const size_t _addr_sizeof	= 4;
    static const size_t _mask_sizeof	= 4;
    static const size_t _nh_sizeof	= 4;
    static const size_t _metric_sizeof	= 4;

    // Offsets for the fields
    static const size_t _af_offset	= 0;
    static const size_t _tag_offset	= _af_offset + _af_sizeof;
    static const size_t _addr_offset	= _tag_offset + _tag_sizeof;
    static const size_t _mask_offset	= _addr_offset + _addr_sizeof;
    static const size_t _nh_offset	= _mask_offset + _mask_sizeof;
    static const size_t _metric_offset	= _nh_offset + _nh_sizeof;

private:
    const uint8_t* _data;	// The buffer data

    // Pointers to the fields
    const uint8_t* _af;
    const uint8_t* _tag;
    const uint8_t* _addr;
    const uint8_t* _mask;
    const uint8_t* _nh;
    const uint8_t* _metric;
};

inline uint32_t
PacketRouteEntry<IPv4>::prefix_len() const
{
    return IPv4(_mask).mask_len();
}

inline IPv4Net
PacketRouteEntry<IPv4>::net() const
{
    return IPv4Net(IPv4(_addr), IPv4(_mask).mask_len());
}

inline bool
PacketRouteEntry<IPv4>::is_table_request() const
{
    return addr_family() == ADDR_FAMILY_DUMP && metric() == RIP_INFINITY;
}

inline bool
PacketRouteEntry<IPv4>::is_auth_entry() const
{
    return addr_family() == ADDR_FAMILY_AUTH;
}

/*
 * @short Class for writing data to RIP IPv4 route entry.
 */
template <>
class PacketRouteEntryWriter<IPv4> : public PacketRouteEntry<IPv4> {
public:
    PacketRouteEntryWriter(uint8_t* data)
	: PacketRouteEntry<IPv4>(data),
	  _data(data),
	  _af(_data + _af_offset),
	  _tag(_data + _tag_offset),
	  _addr(_data + _addr_offset),
	  _mask(_data + _mask_offset),
	  _nh(_data + _nh_offset),
	  _metric(_data + _metric_offset)
    {}

    /**
     * Initialize fields as a regular routing entry.
     */
    void initialize(uint16_t		tag,
		    const IPv4Net&	net,
		    IPv4		nh,
		    uint32_t		cost);

    /**
     * Initialize fields as a route table request.
     */
    void initialize_table_request();

private:
    uint8_t* _data;		// The buffer data

    // Pointers to the fields
    uint8_t* _af;
    uint8_t* _tag;
    uint8_t* _addr;
    uint8_t* _mask;
    uint8_t* _nh;
    uint8_t* _metric;
};

inline void
PacketRouteEntryWriter<IPv4>::initialize(uint16_t	tag,
					 const IPv4Net&	net,
					 IPv4		nh,
					 uint32_t	cost)
{
    embed_16(_af, ADDR_FAMILY);
    embed_16(_tag, tag);
    net.masked_addr().copy_out(_addr);
    net.netmask().copy_out(_mask);
    nh.copy_out(_nh);
    embed_32(_metric, cost);
}

inline void
PacketRouteEntryWriter<IPv4>::initialize_table_request()
{
    embed_16(_af, ADDR_FAMILY_DUMP);
    embed_16(_tag, 0);
    IPv4::ZERO().copy_out(_addr);
    IPv4::ZERO().copy_out(_mask);
    IPv4::ZERO().copy_out(_nh);
    embed_32(_metric, RIP_INFINITY);
}


/**
 * @short Route Entry for Plaintext password.
 *
 * The PlaintextPacketRouteEntry4 may appear as the first route entry
 * in a RIPv2 packet.  It has the same size as an @ref
 * PacketRouteEntry<IPv4>. The address family has the special value
 * 0xffff which implies authentication.  The authentication type is
 * overlaid in the route tag field and takes the special value 2.
 *
 * All items in the route entry are stored in network order.  The
 * accessor methods provide values in host order, and the modifiers
 * take arguments in host order.
 *
 * The plaintext authentication entry has the following content:
 *
 * af (2 bytes):	// The address family
 * auth (2 bytes):	// Authentication type
 * pw (16 bytes):	// Password
 */
class PlaintextPacketRouteEntry4 {
public:
    PlaintextPacketRouteEntry4(const uint8_t* data)
	: _data(data),
	  _af(_data + _af_offset),
	  _auth(_data + _auth_offset),
	  _pw(_data + _pw_offset)
    {
	x_static_assert(PlaintextPacketRouteEntry4::SIZE == _af_sizeof
		      + _auth_sizeof + _pw_sizeof);
	x_static_assert(PlaintextPacketRouteEntry4::SIZE == _pw_offset 
		      + _pw_sizeof);
    }

    static const size_t SIZE = 20;	// The entry size

    /**
     * Get the RIP IPv4 plaintext password route entry size.
     *
     * @return the RIP IPv4 plaintext password route entry size.
     */
    static size_t size()	{ return PlaintextPacketRouteEntry4::SIZE; }

    uint16_t addr_family() const	{ return extract_16(_af); }
    uint16_t auth_type() const		{ return extract_16(_auth); }
    string   password() const;

    static const uint16_t ADDR_FAMILY = 0xffff;
    static const uint16_t AUTH_TYPE = 2;

protected:
    // Sizes of the fields
    static const size_t _af_sizeof	= 2;
    static const size_t _auth_sizeof	= 2;
    static const size_t _pw_sizeof	= 16;

    // Offsets for the fields
    static const size_t _af_offset	= 0;
    static const size_t _auth_offset	= _af_offset + _af_sizeof;
    static const size_t _pw_offset	= _auth_offset + _auth_sizeof;

private:
    const uint8_t* _data;	// The buffer data

    // Pointers to the fields
    const uint8_t* _af;
    const uint8_t* _auth;
    const uint8_t* _pw;
};

inline string
PlaintextPacketRouteEntry4::password() const
{
    return string(reinterpret_cast<const char *>(_pw), 0, _pw_sizeof);
}

/**
 * @short Class for writing data to Plaintext password authentication entry.
 */
class PlaintextPacketRouteEntry4Writer : public PlaintextPacketRouteEntry4 {
public:
    PlaintextPacketRouteEntry4Writer(uint8_t* data)
	: PlaintextPacketRouteEntry4(data),
	  _data(data),
	  _af(_data + _af_offset),
	  _auth(_data + _auth_offset),
	  _pw(_data + _pw_offset)
    {}

    /**
     * Initialize the entry.
     */
    void initialize(const string& s);

private:
    uint8_t* _data;		// The buffer data

    // Pointers to the fields
    uint8_t* _af;
    uint8_t* _auth;
    uint8_t* _pw;
};

inline void
PlaintextPacketRouteEntry4Writer::initialize(const string& s)
{
    embed_16(_af, ADDR_FAMILY);
    embed_16(_auth, AUTH_TYPE);
    memset(_pw, 0, _pw_sizeof);
    s.copy(reinterpret_cast<char *>(_pw), _pw_sizeof);
}


/**
 * @short Route Entry for MD5 data.
 *
 * The MD5PacketRouteEntry4 may appear as the first route entry
 * in a RIPv2 packet.  It has the same size as an @ref
 * PacketRouteEntry<IPv4>. The address family has the special value
 * 0xffff which implies authentication.  The authentication type is
 * overlaid in the route tag field and takes the special value 3.  With
 * MD5 the authentication data appears after the remaining route entries.
 * Details are disclosed in RFC2082.
 *
 * All items in the route entry are stored in network order.  The
 * accessor methods provide values in host order, and the modifiers
 * take arguments in host order.
 *
 * NB We describe the field labelled as "RIP-2 Packet Length" on page 5 of
 * RFC 2082 as the "auth_off".  This matches the textual description in
 * the RFC.
 *
 * The MD5 authentication entry has the following content:
 *
 * af (2 bytes):		// 0xffff - Authentication header
 * auth (2 bytes):		// Authentication type
 * auth_off (2 bytes):		// Offset of authentication data
 * key_id (1 byte):		// Key number used
 * auth_bytes (1 byte):		// Auth data length at end of packet
 * seqno (4 bytes):		// Monotonically increasing seqno
 * mbz (8 bytes):		// Must-be-zero
 */
class MD5PacketRouteEntry4 {
public:
    MD5PacketRouteEntry4(const uint8_t* data)
	: _data(data),
	  _af(_data + _af_offset),
	  _auth(_data + _auth_offset),
	  _auth_off(_data + _auth_off_offset),
	  _key_id(_data + _key_id_offset),
	  _auth_bytes(_data + _auth_bytes_offset),
	  _seqno(_data + _seqno_offset),
	  _mbz(_data + _mbz_offset)
    {
	x_static_assert(MD5PacketRouteEntry4::SIZE == _af_sizeof + _auth_sizeof
		      + _auth_off_sizeof + _key_id_sizeof + _auth_bytes_sizeof
		      + _seqno_sizeof + _mbz_sizeof);
	x_static_assert(MD5PacketRouteEntry4::SIZE == _mbz_offset + _mbz_sizeof);
    }

    static const size_t SIZE = 20;	// The entry size

    /**
     * Get the RIP IPv4 MD5 authentication route entry size.
     *
     * @return the RIP IPv4 MD5 authentication route entry size.
     */
    static size_t size()		{ return MD5PacketRouteEntry4::SIZE; }

    uint16_t addr_family() const	{ return extract_16(_af); }
    uint16_t auth_type() const		{ return extract_16(_auth); }
    uint16_t auth_off() const		{ return extract_16(_auth_off); }
    uint8_t  key_id() const		{ return extract_8(_key_id); }
    uint8_t  auth_bytes() const		{ return extract_8(_auth_bytes); }
    uint32_t seqno() const		{ return extract_32(_seqno); }

    static const uint16_t ADDR_FAMILY = 0xffff;
    static const uint16_t AUTH_TYPE = 3;

protected:
    // Sizes of the fields
    static const size_t _af_sizeof		= 2;
    static const size_t _auth_sizeof		= 2;
    static const size_t _auth_off_sizeof	= 2;
    static const size_t _key_id_sizeof		= 1;
    static const size_t _auth_bytes_sizeof	= 1;
    static const size_t _seqno_sizeof		= 4;
    static const size_t _mbz_sizeof		= 8;

    // Offsets for the fields
    static const size_t _af_offset	= 0;
    static const size_t _auth_offset	= _af_offset + _af_sizeof;
    static const size_t _auth_off_offset = _auth_offset + _auth_sizeof;
    static const size_t _key_id_offset	= _auth_off_offset + _auth_off_sizeof;
    static const size_t _auth_bytes_offset = _key_id_offset + _key_id_sizeof;
    static const size_t _seqno_offset = _auth_bytes_offset + _auth_bytes_sizeof;
    static const size_t _mbz_offset	= _seqno_offset + _seqno_sizeof;

private:
    const uint8_t* _data;	// The buffer data

    // Pointers to the fields
    const uint8_t* _af;		// 0xffff - Authentication header
    const uint8_t* _auth;	// Authentication type
    const uint8_t* _auth_off;	// Offset of authentication data
    const uint8_t* _key_id;	// Key number used
    const uint8_t* _auth_bytes;	// Auth data length at end of packet
    const uint8_t* _seqno;	// Monotonically increasing seqno
    const uint8_t* _mbz;	// Must-be-zero
};

/**
 * @short Class for writing data to MD5 authentication entry.
 */
class MD5PacketRouteEntry4Writer : public MD5PacketRouteEntry4 {
public:
    MD5PacketRouteEntry4Writer(uint8_t* data)
	: MD5PacketRouteEntry4(data),
	  _data(data),
	  _af(_data + _af_offset),
	  _auth(_data + _auth_offset),
	  _auth_off(_data + _auth_off_offset),
	  _key_id(_data + _key_id_offset),
	  _auth_bytes(_data + _auth_bytes_offset),
	  _seqno(_data + _seqno_offset),
	  _mbz(_data + _mbz_offset)
    {}

    void set_auth_off(uint16_t b) 	{ embed_16(_auth_off, b); }
    void set_key_id(uint8_t id)		{ embed_8(_key_id, id); }
    void set_auth_bytes(uint8_t b)	{ embed_8(_auth_bytes, b); }
    void set_seqno(uint32_t sno)	{ embed_32(_seqno, sno); }

    /**
     * Initialize the entry.
     */
    void initialize(uint16_t pkt_bytes,
		    uint8_t  key_id,
		    uint8_t  auth_bytes,
		    uint32_t seqno);

private:
    uint8_t* _data;		// The buffer data

    // Pointers to the fields
    uint8_t* _af;		// 0xffff - Authentication header
    uint8_t* _auth;		// Authentication type
    uint8_t* _auth_off;		// Offset of authentication data
    uint8_t* _key_id;		// Key number used
    uint8_t* _auth_bytes;	// Auth data length at end of packet
    uint8_t* _seqno;		// Monotonically increasing seqno
    uint8_t* _mbz;		// Must-be-zero
};

inline void
MD5PacketRouteEntry4Writer::initialize(uint16_t auth_off,
				       uint8_t  key_id,
				       uint8_t  auth_bytes,
				       uint32_t seqno)
{
    embed_16(_af, ADDR_FAMILY);
    embed_16(_auth, AUTH_TYPE);
    set_auth_off(auth_off);
    set_key_id(key_id);
    set_auth_bytes(auth_bytes);
    set_seqno(seqno);
    memset(_mbz, 0, _mbz_sizeof);
}


/**
 * @short Container for MD5 trailer.
 *
 * THE MD5 authentication trailer has the following content:
 *
 * af (2 bytes):		// 0xffff - Authentication header
 * one (2 bytes):		// 0x01	  - RFC2082 defined
 * auth_data (16 bytes);	// 16 bytes of data
 */
class MD5PacketTrailer {
public:
    MD5PacketTrailer(const uint8_t* data)
	: _data(data),
	  _af(_data + _af_offset),
	  _one(_data + _one_offset),
	  _auth_data(_data + _auth_data_offset)
    {
	x_static_assert(MD5PacketTrailer::SIZE == _af_sizeof + _one_sizeof
		      + _auth_data_sizeof);
	x_static_assert(MD5PacketTrailer::SIZE == _auth_data_offset
		      + _auth_data_sizeof);
    }

    static const size_t SIZE = 20;	// The trailer size

    /**
     * Get the RIP IPv4 MD5 authentication trailer size.
     *
     * @return the RIP IPv4 MD5 authentication trailer size.
     */
    static size_t size()		{ return MD5PacketTrailer::SIZE; }

    uint16_t addr_family() const	{ return extract_16(_af); }
    const uint8_t* auth_data() const	{ return _auth_data; }
    uint32_t auth_data_bytes() const	{ return _auth_data_sizeof; }
    uint32_t auth_data_offset() const	{ return _auth_data_offset; }
    bool valid() const;

protected:
    // Sizes of the fields
    static const size_t _af_sizeof		= 2;
    static const size_t _one_sizeof		= 2;
    static const size_t _auth_data_sizeof	= 16;

    // Offsets for the fields
    static const size_t _af_offset		= 0;
    static const size_t _one_offset		= _af_offset + _af_sizeof;
    static const size_t _auth_data_offset	= _one_offset + _one_sizeof;

private:
    const uint8_t* _data;	// The buffer data

    // Pointers to the fields
    const uint8_t* _af;		// 0xffff - Authentication header
    const uint8_t* _one;	// 0x01	  - RFC2082 defined
    const uint8_t* _auth_data;	// 16 bytes of data
};

inline bool
MD5PacketTrailer::valid() const
{
    return (addr_family() == 0xffff) && (extract_16(_one) == 1);
}

/**
 * @short Class for writing data to MD5 authentication trailer.
 */
class MD5PacketTrailerWriter : public MD5PacketTrailer {
public:
    MD5PacketTrailerWriter(uint8_t* data)
	: MD5PacketTrailer(data),
	  _data(data),
	  _af(_data + _af_offset),
	  _one(_data + _one_offset),
	  _auth_data(_data + _auth_data_offset)
    {}

    /**
     * Initialize the entry.
     */
    void initialize();

    uint8_t* auth_data()	{ return _auth_data; }

private:
    uint8_t* _data;		// The buffer data

    // Pointers to the fields
    uint8_t* _af;		// 0xffff - Authentication header
    uint8_t* _one;		// 0x01	  - RFC2082 defined
    uint8_t* _auth_data;	// 16 bytes of data
};

inline void
MD5PacketTrailerWriter::initialize()
{
    embed_16(_af, 0xffff);
    embed_16(_one, 1);
}


/**
 * @short Route Entry appearing in RIP packets on IPv6.
 *
 * This payload is carried in RIP packets on IPv6.  The interpretation
 * of the fields is defined in RFC2080.
 *
 * All fields in this structure are stored in network order.
 *
 * The route entry has the following content:
 *
 * prefix (16 bytes):	// Prefix
 * tag (2 bytes):	// Tag
 * prefix_len (1 byte):	// Prefix length
 * metric (1 byte):	// Metric
 */
template <>
class PacketRouteEntry<IPv6> {
public:
    PacketRouteEntry(const uint8_t* data)
	: _data(data),
	  _prefix(_data + _prefix_offset),
	  _tag(_data + _tag_offset),
	  _prefix_len(_data + _prefix_len_offset),
	  _metric(_data + _metric_offset)
    {
	x_static_assert(PacketRouteEntry<IPv6>::SIZE == _prefix_sizeof
		      + _tag_sizeof + _prefix_len_sizeof + _metric_sizeof);
	x_static_assert(PacketRouteEntry<IPv6>::SIZE == _metric_offset
		      + _metric_sizeof);
    }

    static const size_t SIZE = 20;	// The entry size

    /**
     * Get the RIP IPv6 route entry size.
     *
     * @return the RIP IPv6 route entry size.
     */
    static size_t size()	{ return PacketRouteEntry<IPv6>::SIZE; }

    bool	    is_nexthop() const;

    /**
     * @return true if route entry has properties of a table request.
     */
    bool	    is_table_request() const;

    IPv6	    nexthop() const;

    uint16_t tag() const;
    uint32_t prefix_len() const { return extract_8(_prefix_len); }
    IPv6Net  net() const;
    uint8_t  metric() const;

    static const uint8_t NEXTHOP_METRIC = 0xff;


protected:
    // Sizes of the fields
    static const size_t _prefix_sizeof		= 16;
    static const size_t _tag_sizeof		= 2;
    static const size_t _prefix_len_sizeof	= 1;
    static const size_t _metric_sizeof		= 1;

    // Offsets for the fields
    static const size_t _prefix_offset	= 0;
    static const size_t _tag_offset	= _prefix_offset + _prefix_sizeof;
    static const size_t _prefix_len_offset = _tag_offset + _tag_sizeof;
    static const size_t _metric_offset	= _prefix_len_offset + _prefix_len_sizeof;

private:
    const uint8_t* _data;	// The buffer data

    // Pointers to the fields
    const uint8_t* _prefix;
    const uint8_t* _tag;
    const uint8_t* _prefix_len;
    const uint8_t* _metric;
};

inline bool
PacketRouteEntry<IPv6>::is_nexthop() const
{
    return metric() == NEXTHOP_METRIC;
}

inline IPv6
PacketRouteEntry<IPv6>::nexthop() const
{
    return IPv6(_prefix);
}

inline uint16_t
PacketRouteEntry<IPv6>::tag() const
{
    return extract_16(_tag);
}

inline IPv6Net
PacketRouteEntry<IPv6>::net() const
{
    return IPv6Net(IPv6(_prefix), prefix_len());
}

inline uint8_t
PacketRouteEntry<IPv6>::metric() const
{
    return extract_8(_metric);
}

inline bool
PacketRouteEntry<IPv6>::is_table_request() const
{
    if (metric() != RIP_INFINITY) {
	return false;
    }
    if (prefix_len() != 0) {
	return false;
    }
    IPv6 addr(_prefix);
    return (addr.is_zero());
}

/*
 * @short Class for writing data to RIP IPv6 route entry.
 */
template <>
class PacketRouteEntryWriter<IPv6> : public PacketRouteEntry<IPv6> {
public:
    PacketRouteEntryWriter(uint8_t* data)
	: PacketRouteEntry<IPv6>(data),
	  _data(data),
	  _prefix(_data + _prefix_offset),
	  _tag(_data + _tag_offset),
	  _prefix_len(_data + _prefix_len_offset),
	  _metric(_data + _metric_offset)
    {}

    /**
     * Initialize fields as a regular routing entry.
     */
    void	    initialize_route(uint16_t		route_tag,
				     const IPv6Net&	net,
				     uint8_t		cost);

    /**
     * Initialize fields as a nexthop entry.
     */
    void	    initialize_nexthop(const IPv6& nexthop);

    /**
     * Initialize fields as a route table request.
     */
    void	    initialize_table_request();

private:
    uint8_t* _data;		// The buffer data

    // Pointers to the fields
    uint8_t* _prefix;
    uint8_t* _tag;
    uint8_t* _prefix_len;
    uint8_t* _metric;
};

inline void
PacketRouteEntryWriter<IPv6>::initialize_route(uint16_t		tag,
					       const IPv6Net&	net,
					       uint8_t		cost)
{
    net.masked_addr().copy_out(_prefix);
    embed_16(_tag, tag);
    embed_8(_prefix_len, net.prefix_len());
    embed_8(_metric, cost);
}

inline void
PacketRouteEntryWriter<IPv6>::initialize_nexthop(const IPv6& nexthop)
{
    nexthop.copy_out(_prefix);
    embed_16(_tag, 0);
    embed_8(_prefix_len, 0);
    embed_8(_metric, NEXTHOP_METRIC);
}

inline void
PacketRouteEntryWriter<IPv6>::initialize_table_request()
{
    IPv6::ZERO().copy_out(_prefix);
    embed_16(_tag, 0);
    embed_8(_prefix_len, 0);
    embed_8(_metric, RIP_INFINITY);
}


/**
 * @short RIP Packet class.
 *
 * A container for RIP packet, provides easy to use accessors and modifiers.
 */
template <typename A>
class RipPacket {
public:
    typedef A Addr;

    /**
     * @return destination address of packet.
     */
    const Addr&	address() const	{ return _addr; }

    /**
     * @return destination port of packet.
     */
    uint16_t	port() const	{ return _port; }

    /**
     * @return the maximum number of route entries packet may have.
     */
    uint32_t	max_entries() const	{ return _max_entries; }

    /**
     * Set the maximum number of route entries a packet may have.
     * This method should be called before using @ref append_data
     * methods as it resizes the internal storage and will cause
     * appended data to be lost.
     */
    void	set_max_entries(uint32_t max_entries);

    RipPacket(const Addr& addr,
	      uint16_t	  port,
	      uint32_t	  max_entries = RIPv2_ROUTES_PER_PACKET)
	: _addr(addr), _port(port), _max_entries(0)
    {
	set_max_entries(max_entries);
    }

    /**
     * @return const pointer to the beginning of the RIP packet header.
     */
    const uint8_t* header_ptr() const;

    /**
     * @return pointer to the beginning of the RIP packet header.
     */
    uint8_t* header_ptr();

    /**
     * Route entry accessor.
     *
     * @param entry_no index of route entry to retrive.
     * @return const pointer to route entry, or 0 if entry_no is greater than
     * the maximum route entries associated with packet.
     */
    const uint8_t* route_entry_ptr(uint32_t entry_no) const;

    /**
     * Route entry accessor.
     *
     * @param entry_no index of route entry to retrive.
     * @return pointer to route entry, or 0 if entry_no is greater than
     * the maximum route entries associated with packet.
     */
    uint8_t* route_entry_ptr(uint32_t entry_no);

    void append_data(const uint8_t* data, uint32_t data_bytes);
    void append_data(const vector<uint8_t>& data);

    vector<uint8_t>& 	  data()		{ return _data; }
    const vector<uint8_t>& data() const		{ return _data; }
    uint32_t 		  data_bytes() const	{ return _data.size(); }
    const uint8_t*	  data_ptr() const	{ return base_ptr(); }
    uint8_t*	  	  data_ptr()		{ return base_ptr(); }

private:
    Addr	    _addr;	// Src addr on inbound, dst address on outbound
    uint16_t	    _port;	// Src port on inbound, dst port on outbound
    vector<uint8_t> _data;	// Data buffer
    uint32_t _max_entries;	// Maximum number of route entries in packet

    const uint8_t* base_ptr() const	{ return &(_data[0]); }
    uint8_t* base_ptr()			{ return &(_data[0]); }
};

template <typename A>
const uint8_t*
RipPacket<A>::header_ptr() const
{
    return (base_ptr());
}

template <typename A>
uint8_t*
RipPacket<A>::header_ptr()
{
    return (base_ptr());
}

template <typename A>
const uint8_t*
RipPacket<A>::route_entry_ptr(uint32_t entry_no) const
{
    if (entry_no >= _max_entries)
	return NULL;

    const uint8_t* p = base_ptr() + RipPacketHeader::size() +
	entry_no * PacketRouteEntry<A>::size();
    return p;
}

template <typename A>
uint8_t*
RipPacket<A>::route_entry_ptr(uint32_t entry_no)
{
    if (entry_no >= _max_entries)
	return NULL;

    uint8_t* p = base_ptr() + RipPacketHeader::size() +
	entry_no * PacketRouteEntry<A>::size();
    return p;
}

template <typename A>
void
RipPacket<A>::append_data(const uint8_t* data, uint32_t data_bytes)
{
    _data.insert(_data.end(), data, data + data_bytes);
}

template <typename A>
void
RipPacket<A>::append_data(const vector<uint8_t>& data)
{
    _data.insert(_data.end(), data.begin(), data.end());
}

template <typename A>
void
RipPacket<A>::set_max_entries(uint32_t max_entries)
{
    if (max_entries != _max_entries) {
	_data.resize(PacketRouteEntry<A>::size() * max_entries
		     + RipPacketHeader::size());
	_max_entries = max_entries;
    }
}

#endif // __RIP_PACKETS_HH__
