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

// Copyright (c) 2001-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/bgp/aspath.hh,v 1.34 2008/10/02 21:56:14 bms Exp $

#ifndef __BGP_ASPATH_HH__
#define __BGP_ASPATH_HH__

#include <sys/types.h>
#include <inttypes.h>
#include <string>
#include <list>
#include <vector>

#include "libxorp/xorp.h"
#include "libxorp/debug.h"
#include "libxorp/xlog.h"
#include "libxorp/asnum.hh"
#include "libxorp/exceptions.hh"
#include "exceptions.hh"

class AS4Path;

/**
 * This file contains the classes to manipulate AS segments/lists/paths
 *
 * An AS_PATH is made of a list of segments, each segment being
 * an AS_SET or an AS_SEQUENCE.
 * Logically, you can think an AS_SEQUENCE as a list of AsNum,
 * and an AS_SET as an unordered set of AsNum; the path would then be
 * made of alternate AS_SET and AS_SEQUENCEs. However, the max number
 * of elements in an AS_SEQUENCE is 255, so you might need to split a
 * sequence into multiple ones.
 *
 * The external representation of AS_SET or AS_SEQUENCE is:
 *   1 byte:	segment type
 *   1 byte:	number of element in the segment
 *   n entries:	the ASnumbers in the segment, 2 or 4 bytes each
 *
 * In terms of internal representation, it might be more efficient
 * useful to store segments as either an AS_SET, or a sequence of size 1,
 * and then do the necessary grouping on output.
 *
 * The current implementation allows both forms.
 *
 * Note that the external representation (provided by encode()) returns
 * a malloc'ed chunk of memory which must be freed by the caller.
 *
 * RFC 3065 (Autonomous System Confederations for BGP) introduced two
 * additional segment types:
 *
 * AS_CONFED_SEQUENCE: ordered set of Member AS Numbers
 * in the local confederation that the UPDATE message has traversed
 *
 * AS_CONFED_SET: unordered set of Member AS Numbers in
 * the local confederation that the UPDATE message has traversed
 *  
 *
 * 4-byte AS numbers need to be handled carefully.  There are two main
 * cases to consider: when we are configured to do 4-byte AS numbers,
 * and when we are not.
 *
 * When we are not configured to do 4-byte AS numbers all AS path
 * information received from peers will be 2-byte AS numbers.  If
 * they're configured to do 4-byte AS numbers, they will send us an
 * AS_PATH and an AS4_PATH, but if we're not configured to do 4-byte
 * AS numbers, we shouldn't care.  We don't merge the two; we
 * propagate AS4_PATH unchanged, and do the normal stuff with the
 * regular AS_PATH.  The AS4_PATH is actually stored internally in an
 * UnknownAttribute, as we don't need to know anything about it.
 *
 * When we are configured to do 4-byte AS numbers, we can tell from
 * the negotiated capabilities whether a peer is sending 4-byte AS
 * numbers or 2-byte AS numbers.  If a peer is sending 4-byte AS
 * numbers, these arrive in the AS_PATH attribute, and we store them
 * internally in an AS4Path, but stored in the ASPathAttribute, NOT in
 * the AS4PathAttribute.  If a peer is sending 2-byte AS numbers in
 * AS_PATH, he may also propagate an AS4_PATH.  We merge these on
 * input, cross-validate the two, and store the results just like we
 * would if we'd received a 4-byte AS_PATH.  So, whatever our peer
 * does, the only place we have AS path information that we use for
 * the decision process is in the ASPathAttribute.
 *
 * If we're doing 4-byte AS numbers and our peer is doing 4-byte AS
 * numbers, then all routes we send him will contain 4-byte AS_PATH
 * attributes.  We just need to make sure we use the right encode()
 * method.  If our peer is not doing 4-byte AS numbers, then we send a
 * 2-byte AS_PATH, substituting AS_TRANS for any AS numbers that can't
 * be represented in 2 bytes.  We also construct an AS4_PATH from our
 * ASPathAttribute, and send that too.
 */

// AS Path Values
enum ASPathSegType {
    AS_NONE = 0,	// not initialized
    AS_SET = 1,
    AS_SEQUENCE = 2,
    AS_CONFED_SEQUENCE = 3,
    AS_CONFED_SET = 4
};

/**
 * Parent class for ASPath elements, which can be either ASSet or ASSequence.
 */
class ASSegment {
public:
    typedef list<AsNum> ASLIST;
    typedef ASLIST::iterator iterator;
    typedef ASLIST::const_iterator const_iterator;
    typedef ASLIST::const_reverse_iterator const_reverse_iterator;

    /**
     * Constructor of an empty ASSegment
     */
    ASSegment(ASPathSegType t = AS_NONE) : _type(t)	{
    }

    /**
     * constructor from external representation will just decode
     * the chunk of data received, and convert to internal representation.
     * In fact, the external representation is quite simple and effective.
     *
     * _type is d[0], l is d[1], entries follow.
     */
    ASSegment(const uint8_t* d) throw(CorruptMessage)	{
	decode(d);
    }

    /**
     * Copy constructor
     */
    ASSegment(const ASSegment& a) :
	_type(a._type), _aslist(a._aslist)		{}

    /**
     * The destructor has nothing to do, the underlying container will
     * take care of the thing.
     */
    ~ASSegment()					{}

    /**
     * reset whatever is currently contained in the object.
     */
    void clear()					{
	_type = AS_NONE;
	_aslist.clear();
    }

    /**
     * @return the path length, which is 1 for an AS_SET,
     * and the length of the sequence for an AS_SEQUENCE
     */
    size_t path_length() const				{
	if (_type == AS_SET || _type == AS_CONFED_SET)
	    return 1;
	else if (_type == AS_SEQUENCE || _type == AS_CONFED_SEQUENCE)
	    return _aslist.size();
	else
	    return 0; // XXX should not be called!
    }

    size_t as_size() const				{
	return _aslist.size();
    }

    /**
     * Add AsNum at the end of the segment (order is irrelevant
     * for AS_SET but important for AS_SEQUENCE)
     * This is used when initializing from a string.
     */
    void add_as(const AsNum& n)				{
	debug_msg("Number of As entries %u\n", XORP_UINT_CAST(_aslist.size()));
	_aslist.push_back(n);
    }

    /**
     * Add AsNum at the beginning of the segment (order is irrelevant
     * for AS_SET but important for AS_SEQUENCE).
     * This is used e.g. when a node is adding its AsNum to the sequence.
     */
    void prepend_as(const AsNum& n)			{
	debug_msg("Number of As entries %u\n", XORP_UINT_CAST(_aslist.size()));
	_aslist.push_front(n);
    }

    /**
     * Check if a given AsNum is contained in the segment.
     */
    bool contains(const AsNum& as_num) const		{
	const_iterator iter;

	for (iter = _aslist.begin(); iter != _aslist.end(); ++iter)
	    if (*iter == as_num)
		return true;
	return false;
    }

    const AsNum& first_asnum() const;

    /**
     * find the n'th AS number in the segment 
     */
    const AsNum& as_num(int n) const			{
	const_iterator i = _aslist.begin();

	while (n--)
	    i++;

	return *i;
    }

    /**
     * Convert the external representation into the internal one.
     * _type is d[0], _entries is d[1], entries follow.
     */
    void decode(const uint8_t *d) throw(CorruptMessage);

    /**
     * Convert from internal to external representation.
     * If we do not pass a buffer (buf = 0), then the routine will
     * allocate a new one; otherwise, len indicates the size of the
     * input buffer, which must be large enough to store the encoding.
     * @return the pointer to the buffer, len is the actual size.
     */
    const uint8_t *encode(size_t &len, uint8_t *buf) const;

    /**
     * @return the size of the list on the wire.
     */
    size_t wire_size() const			{
	return 2 + 2 * _aslist.size();
    }

    /**
     * @return fancy string representation of the segment
     */
    string str() const;

    /**
     * @return compact string representation of the segment
     */
    string short_str() const;

    /**
     * compares internal representations for equality.
     */
    bool operator==(const ASSegment& him) const;

    /**
     * Compares internal representations for <.
     */
    bool operator<(const ASSegment& him) const;

    ASPathSegType type() const			{ return _type; }
    void set_type(ASPathSegType t)		{ _type = t; }

    size_t encode_for_mib(uint8_t* buf, size_t buf_size) const;

    /**
     * returns true if the AS segment does not lose information when
     * represented entirely as two-byte AS numbers 
     */
    bool two_byte_compatible() const;

protected:
    ASPathSegType	_type;
    ASLIST		_aslist;
};


/* subsclass of ASSegment to handle encoding and decoding of 4-byte AS
   numbers from a AS4_PATH attribute */
class AS4Segment : public ASSegment {
public:
    AS4Segment(const uint8_t* d) throw(CorruptMessage) { decode(d); }
    /**
     * Convert the external representation into the internal one.
     * _type is d[0], _entries is d[1], entries follow.
     */
    void decode(const uint8_t *d) throw(CorruptMessage);

    /**
     * Convert from internal to external representation.
     * If we do not pass a buffer (buf = 0), then the routine will
     * allocate a new one; otherwise, len indicates the size of the
     * input buffer, which must be large enough to store the encoding.
     * @return the pointer to the buffer, len is the actual size.
     */
    const uint8_t *encode(size_t &len, uint8_t *buf) const;

    /**
     * @return the size of the list on the wire.
     */
    size_t wire_size() const			{
	return 2 + 4 * _aslist.size();
    }
private:
    /* no storage, as this is handled by the underlying ASSegment */
};

/**
 * An ASPath is a list of ASSegments, each of which can be an AS_SET,
 * AS_CONFED_SET, AS_SEQUENCE, or an AS_CONFED_SEQUENCE.
 */
class ASPath {
public:
    typedef list <ASSegment>::const_iterator const_iterator;
    typedef list <ASSegment>::iterator iterator;

    ASPath() : _num_segments(0), _path_len(0)		{}

    /**
     * Initialize from a string in the format
     *		1,2,(3,4,5),6,7,8,(9,10,11),12,13
     */
    ASPath(const char *as_path) throw(InvalidString);

    /**
     * construct from received data
     */
    ASPath(const uint8_t* d, size_t len) throw(CorruptMessage) {
	decode(d, len); 
    }

    /**
     * construct an aggregate from two ASPaths
     */
    ASPath(const ASPath &asp1, const ASPath &asp2);

    /**
     * Copy constructor
     */
    ASPath(const ASPath &a) : _segments(a._segments), 
	_num_segments(a._num_segments), _path_len(a._path_len) {}

    ~ASPath()						{}

    void add_segment(const ASSegment& s);
    void prepend_segment(const ASSegment& s);

    size_t path_length() const				{ return _path_len; }

    bool contains(const AsNum& as_num) const		{
	const_iterator i = _segments.begin();
	for (; i != _segments.end(); ++i)
	    if ((*i).contains(as_num))
		return true;
	return false;
    }

    const AsNum& first_asnum() const			{
	XLOG_ASSERT(!_segments.empty());
	return _segments.front().first_asnum();
    }

    string str() const;
    string short_str() const;

    const ASSegment& segment(size_t n) const		{
	if (n < _num_segments) {
	    const_iterator iter = _segments.begin();
	    for (u_int i = 0; i<n; i++)
		++iter;
	    return (*iter);
        }
	XLOG_FATAL("Segment %u doesn't exist.", (uint32_t)n);
	xorp_throw(InvalidString, "segment invalid n\n");
    }

    size_t num_segments() const			{ return _num_segments; }

    /**
     * Convert from internal to external representation, with the
     * correct representation for the original AS_PATH attribute.  If
     * there are any 4-byte AS numbers, they will be encoded as
     * AS_TRAN.
     *
     * If we do not pass a buffer (buf = 0), then the routine will
     * allocate a new one; otherwise, len indicates the size of the
     * input buffer, which must be large enough to store the encoding.
     * @return the pointer to the buffer, len is the actual size.
     */
    const uint8_t *encode(size_t &len, uint8_t *buf) const;

    /**
     * @return the size of the list on the wire.
     * XXX this should be made more efficient.
     */
    size_t wire_size() const;

    /**
     * Add the As number to the begining of the AS_SEQUENCE that starts
     * the As path, or if the ASPath starts with an AS_SET, then add a
     * new AS_SEQUENCE with the new AsNum to the start of the ASPath
     */
    void prepend_as(const AsNum &asn);

    /**
     * Add the As number to the begining of the AS_CONFED_SEQUENCE
     * that starts the As path, or if the ASPath does not start with
     * an AS_CONFED_SEQUENCE, then add a new AS_CONFED_SEQUENCE with
     * the new AsNum to the start of the ASPath
     */
    void prepend_confed_as(const AsNum &asn);

    /**
     * remove all confederation segments from aspath 
     */
    void remove_confed_segments();

    /**
     * @return true if the AS_PATH Contains confederation segments.
     */
    bool contains_confed_segments() const;

    ASPath& operator=(const ASPath& him);

    bool operator==(const ASPath& him) const;

    bool operator<(const ASPath& him) const;

    void encode_for_mib(vector<uint8_t>& aspath) const;

    /**
     * returns true if the AS path does not lose information when
     * represented entirely as two-byte AS numbers */
    bool two_byte_compatible() const;

    /**
     * Merge an AS4Path into a 2-byte AS Path.  Both paths will end up
     * containing the same data
     *
     * param as4path the AS4_PATH to be merged.  
     */
    void merge_as4_path(AS4Path& as4_path);

protected:
    /**
     * internal representation
     */
    list <ASSegment>	_segments;
    size_t		_num_segments;
    size_t		_path_len;

private:
    /**
     * populate an ASPath from received data. Only used in the constructor.
     */
    void decode(const uint8_t *d, size_t len) throw(CorruptMessage);
};

/* subclass to handle 4-byte AS encoding and decoding */
class AS4Path : public ASPath {
public:
    /**
     * Construct from received data from 4-byte peer.
     */
    AS4Path(const uint8_t* d, size_t len) throw(CorruptMessage);

    /**
     * Initialize from a string in the format
     *		3.1,2,(3,10.4,5),6,7,8,(9,10,11),12,13
     */
    AS4Path(const char *as_path) throw(InvalidString) 
	: ASPath(as_path)
    {};

#if 0
    /**
     * Construct from received data from 2-byte peer.  This needs to
     * take the regular ASPath in addition to the AS4_PATH data,
     * because it needs to cross-validate the two.
     */
    AS4Path(const uint8_t* d, size_t len, const ASPath& as_path)
	throw(CorruptMessage);

#endif

    /**
     * Convert from internal to external representation, with the
     * correct representation for the original AS4_PATH attribute.
     *
     * If we do not pass a buffer (buf = 0), then the routine will
     * allocate a new one; otherwise, len indicates the size of the
     * input buffer, which must be large enough to store the encoding.
     * @return the pointer to the buffer, len is the actual size.
     */
    const uint8_t *encode(size_t &len, uint8_t *buf) const;

    size_t wire_size() const;

    void cross_validate(const ASPath& as_path);

private:
    /**
     * populate an ASPath from received data. Only used in the constructor.
     */
    void decode(const uint8_t *d, size_t len) throw(CorruptMessage);
    void pad_segment(const ASSegment& old_seg, ASSegment& new_seg);
    void do_patchup(const ASPath& as_path);
};

#endif // __BGP_ASPATH_HH__
