// -*- 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
//
// 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



// #define DEBUG_LOGGING
// #define DEBUG_PRINT_FUNCTION_NAME

#include "ospf_module.h"

#include "libxorp/xorp.h"
#include "libxorp/debug.h"
#include "libxorp/xlog.h"
#include "libxorp/callback.hh"
#include "libxorp/ipv4.hh"
#include "libxorp/ipv6.hh"
#include "libxorp/ipnet.hh"
#include "libxorp/status_codes.h"
#include "libxorp/service.hh"
#include "libxorp/eventloop.hh"






#include "libproto/spt.hh"

#include "ospf.hh"
#include "packet.hh"
#include "delay_queue.hh"
#include "vertex.hh"
#include "area_router.hh"
#include "auth.hh"
#include "peer.hh"
#include "peer_manager.hh"

#ifndef VLINK_DEF_TTL
#define VLINK_DEF_TTL 64    // Default TTL for virtual link packets.
#endif

template <typename A>
PeerManager<A>::~PeerManager()
{
    // Remove all the areas, this should cause all the peers to be
    // removed. Every call to destroy_area_router will change _areas,
    // so call begin again each time.
    for(;;) {
	typename map<OspfTypes::AreaID, AreaRouter<A> *>::iterator i;
	i = _areas.begin();
	if (i == _areas.end())
	    break;
	destroy_area_router((*i).first);
    }
    XLOG_ASSERT(_pmap.empty());
    XLOG_ASSERT(_peers.empty());
    XLOG_ASSERT(_areas.empty());
}

template <typename A>
bool
PeerManager<A>::check_area_type(OspfTypes::AreaID area,
				OspfTypes::AreaType area_type)
{
    debug_msg("Area %s Type %s\n", pr_id(area).c_str(), 
	      pp_area_type(area_type).c_str());

    bool allowed = true;

    if (OspfTypes::BACKBONE == area) {
	switch(area_type) {
	case OspfTypes::NORMAL:
	    break;
	case OspfTypes::STUB:
	    /*FALLTHROUGH*/
	case OspfTypes::NSSA:
	    allowed = false;
	    break;
	}
    }

    return allowed;
}

template <typename A>
bool
PeerManager<A>::create_area_router(OspfTypes::AreaID area,
				   OspfTypes::AreaType area_type,
				   bool permissive)
{
    debug_msg("Area %s Type %s\n", pr_id(area).c_str(), 
	      pp_area_type(area_type).c_str());

    // Check this area doesn't already exist.
    if (0 != _areas.count(area)) {
	XLOG_WARNING("Area %s already exists\n", pr_id(area).c_str());
	if (permissive)
	    return true;
	return false;
    }

    if (!check_area_type(area, area_type)) {
	XLOG_ERROR("Area %s cannot be %s", pr_id(area).c_str(), 
		   pp_area_type(area_type).c_str());
	return false;
    }

    track_area_count(area_type, true /* increment */);

    bool old_border_router_state = area_border_router_p();

    _areas[area] = new AreaRouter<A>(_ospf, area, area_type);
    _areas[area]->startup();
    
    // If we just became a border router force an updated Router-LSA
    // to be generated by the first area.
    // XXX Should subsume the refreshing of the Router-LSA into the
    // generic area border router transition method.
    if (area_border_router_p() != old_border_router_state) {
	if (!_ospf.get_testing()) {
	    refresh_router_lsas();
	    area_border_router_transition(true /* up */);
	}
    }

    // Inform this area if any virtual links are configured.
    list<OspfTypes::RouterID> rids;
    _vlink.get_router_ids(area, rids);
    list<OspfTypes::RouterID>::const_iterator i;
    for (i = rids.begin(); i != rids.end(); i++)
	transit_area_virtual_link(*i, area);

    return true;
}

template <typename A>
AreaRouter<A> *
PeerManager<A>::get_area_router(OspfTypes::AreaID area)
{
    debug_msg("Area %s\n", pr_id(area).c_str());

    // Check this area exists.
    if (0 == _areas.count(area)) {
	XLOG_ERROR("Area %s doesn't exist\n", pr_id(area).c_str());
	return 0;
    }

    return _areas[area];
}

template <typename A>
bool
PeerManager<A>::change_area_router_type(OspfTypes::AreaID area,
					OspfTypes::AreaType area_type)
{
    debug_msg("Area %s Type %s\n", pr_id(area).c_str(), 
	      pp_area_type(area_type).c_str());

    // Verify this area exists.
    if (0 == _areas.count(area)) {
	XLOG_ERROR("Area %s doesn't exist", pr_id(area).c_str());
	return false;
    }

    if (_areas[area]->get_area_type() == area_type)
	return true;

    if (!check_area_type(area, area_type)) {
	XLOG_ERROR("Area %s cannot be %s", pr_id(area).c_str(), 
		   pp_area_type(area_type).c_str());
	return false;
    }

    track_area_count(_areas[area]->get_area_type(), false /* decrement */);
    track_area_count(area_type, true /* increment */);

    _areas[area]->change_area_router_type(area_type);

    // Notify all peers that the type of this area has changed. The
    // area to peer mapping is not held so all peers must be notified.
    // When the correct area is found set the new options for the
    // hello packet.
    typename map<OspfTypes::PeerID, PeerOut<A> *>::iterator i;
    for(i = _peers.begin(); i != _peers.end(); i++)
	if ((*i).second->change_area_router_type(area, area_type))
	    (*i).second->set_options(area, compute_options(area_type));

    return true;
}

template <typename A>
bool
PeerManager<A>::destroy_area_router(OspfTypes::AreaID area)
{
    debug_msg("Area %s\n", pr_id(area).c_str());

    // Verify this area exists.
    if (0 == _areas.count(area)) {
	XLOG_ERROR("Area %s doesn't exist\n", pr_id(area).c_str());
	return false;
    }

    track_area_count(_areas[area]->get_area_type(), false /* decrement */);

    _areas[area]->shutdown();

    // Notify the peers that this area is being removed. If this is
    // the only area that the peer belonged to the peer can signify
    // this and the peer can be removed.
    typename map<OspfTypes::PeerID, PeerOut<A> *>::iterator i;
    for(i = _peers.begin(); i != _peers.end();)
	if ((*i).second->remove_area(area)) {
	    delete_peer((*i).first);
	    i = _peers.begin();
	} else
	    i++;

    bool old_border_router_state = area_border_router_p();

    delete _areas[area];
    _areas.erase(_areas.find(area));

    // If we are no longer a border router force an updated Router-LSA
    // in the existing area.
    // XXX Should subsume the refreshing of the Router-LSA into the
    // generic area border router transition method.
    if (area_border_router_p() != old_border_router_state) {
	if (!_ospf.get_testing()) {
	    refresh_router_lsas();
	    area_border_router_transition(false /* down */);
	}
    }

    // Flag to the virtual link code that this area is going away.
    _vlink.area_removed(area);

    return true;
}

template <typename A>
bool
PeerManager<A>::area_range_add(OspfTypes::AreaID area, IPNet<A> net,
			       bool advertise)
{
    debug_msg("Area %s Net %s advertise %s\n", pr_id(area).c_str(),
	      cstring(net), bool_c_str(advertise));

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->area_range_add(net, advertise);
}

template <typename A>
bool
PeerManager<A>::area_range_delete(OspfTypes::AreaID area, IPNet<A> net)
{
    debug_msg("Area %s Net %s\n", pr_id(area).c_str(), cstring(net));

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->area_range_delete(net);
}

template <typename A>
bool
PeerManager<A>::area_range_change_state(OspfTypes::AreaID area, IPNet<A> net,
					bool advertise)
{
    debug_msg("Area %s Net %s advertise %s\n", pr_id(area).c_str(),
	      cstring(net), bool_c_str(advertise));

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->area_range_change_state(net, advertise);
}

template <typename A>
bool
PeerManager<A>::get_lsa(const OspfTypes::AreaID area, const uint32_t index,
			bool& valid, bool& toohigh, bool& self,
			vector<uint8_t>& lsa)
{
    debug_msg("Area %s index %u\n", pr_id(area).c_str(), index);

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->get_lsa(index, valid, toohigh, self, lsa);
}

template <typename A>
bool
PeerManager<A>::get_area_list(list<OspfTypes::AreaID>& areas) const
{
    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator i;
    for(i = _areas.begin(); i != _areas.end(); i++)
	areas.push_back((*i).first);

    return true;
}

template <typename A>
bool
PeerManager<A>::get_neighbour_list(list<OspfTypes::NeighbourID>& neighbours)
    const
{
    typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator i;
    for(i = _peers.begin(); i != _peers.end(); i++)
	(*i).second->get_neighbour_list(neighbours);

    return true;
}

template <typename A>
bool
PeerManager<A>::get_neighbour_info(OspfTypes::NeighbourID nid,
				   NeighbourInfo& ninfo) const
{
    list<OspfTypes::NeighbourID> neighbours;

    typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator i;
    for(i = _peers.begin(); i != _peers.end(); i++) {
	(*i).second->get_neighbour_list(neighbours);
	list<OspfTypes::NeighbourID>::const_iterator j;
	for (j = neighbours.begin(); j != neighbours.end(); j++) {
	    if (*j == nid) {
		return (*i).second->get_neighbour_info(nid, ninfo);
	    }
	}
	neighbours.clear();
    }

    return false;
}

template <typename A>
OspfTypes::PeerID
PeerManager<A>::create_peerid(const string& interface, const string& vif)
    throw(BadPeer)
{
    debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str());
    string concat = interface + "/" + vif;

    if (0 != _pmap.count(concat))
	xorp_throw(BadPeer,
		   c_format("Mapping for %s already exists", concat.c_str()));
			    
    OspfTypes::PeerID peerid = _next_peerid++;
    _pmap[concat] = peerid;

    return peerid;
}

template <typename A>
OspfTypes::PeerID
PeerManager<A>::get_peerid(const string& interface, const string& vif)
    throw(BadPeer)
{
    debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str());
    string concat = interface + "/" + vif;

    if (0 == _pmap.count(concat))
	xorp_throw(BadPeer, 
		   c_format("No mapping for %s exists", concat.c_str()));
			    
    return _pmap[concat];
}

template <typename A>
bool
PeerManager<A>::get_interface_vif_by_peerid(OspfTypes::PeerID peerid,
					    string& interface, string& vif)
    const
{
    debug_msg("PeerID %u\n", peerid);

    typename map<string, OspfTypes::PeerID>::const_iterator pi;
    for(pi = _pmap.begin(); pi != _pmap.end(); pi++) {
	if ((*pi).second == peerid) {
	    string concat = (*pi).first;
	    interface = concat.substr(0, concat.find('/'));
	    vif = concat.substr(concat.find('/') + 1, concat.size() - 1);
	    return true;
	}
    }

    return false;
}

template <typename A>
void
PeerManager<A>::destroy_peerid(const string& interface, const string& vif)
    throw(BadPeer)
{
    debug_msg("Interface %s Vif %s\n", interface.c_str(), vif.c_str());
    string concat = interface + "/" + vif;

    if (0 == _pmap.count(concat))
	xorp_throw(BadPeer, 
		   c_format("No mapping for %s exists", concat.c_str()));
			    
    _pmap.erase(_pmap.find(concat));
}

template <typename A>
bool
PeerManager<A>::enabled(const string& interface, const string& vif, A address)
{
    debug_msg("Interface %s Vif %s Address %s\n", interface.c_str(),
	      vif.c_str(), cstring(address));

    switch (_ospf.get_version()) {
    case OspfTypes::V2:
	break;
    case OspfTypes::V3:
	// If the address is set to zero then try and get the
	// link-local address.
	if (A::ZERO() == address) {
	    if (!_ospf.get_link_local_address(interface, vif, address))
		if (_ospf.enabled(interface, vif))
		    XLOG_WARNING("link-local address must be configured "
				 "on %s/%s", interface.c_str(), vif.c_str());
	}
	break;
    }
    return _ospf.enabled(interface, vif, address);
}

template <typename A>
OspfTypes::PeerID
PeerManager<A>::create_peer(const string& interface, const string& vif,
			    A source,
			    OspfTypes::LinkType linktype, 
			    OspfTypes::AreaID area)
    throw(BadPeer)
{
    debug_msg("Interface %s Vif %s source net %s linktype %u area %s\n",
	      interface.c_str(), vif.c_str(),
	      cstring(source),linktype, pr_id(area).c_str());

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router)
	xorp_throw(BadPeer, 
		   c_format("Unknown Area %s", pr_id(area).c_str()));

    OspfTypes::PeerID peerid = create_peerid(interface, vif);

    // If we got this far create_peerid did not throw an exception so
    // this interface/vif is unique.

    _peers[peerid] = new PeerOut<A>(_ospf, interface, vif, peerid,
				    source, linktype, area,
				    area_router->get_area_type());

    // Pass in the option to be sent by the hello packet.
    _peers[peerid]->set_options(area,
				compute_options(area_router->get_area_type()));

    // These two registrations need to be made only once per
    // invocation of OSPF, however, at this point we know that the interface
    // mirror is up and running.
    _ospf.register_vif_status(callback(this,
				       &PeerManager<A>::
				       vif_status_change));
    _ospf.register_address_status(callback(this, &PeerManager<A>::
					   address_status_change));

    area_router->add_peer(peerid);

    // If the interface, vif and source are up the peer will start running.
    _peers[peerid]->set_link_status(enabled(interface, vif, source), "create_peer");

    return peerid;
}

template <typename A>
bool
PeerManager<A>::delete_peer(const OspfTypes::PeerID peerid)
{
    debug_msg("PeerID %u\n", peerid);

    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    delete _peers[peerid];
    _peers.erase(_peers.find(peerid));

    // Tell *all* area routers that this peer is being deleted.
    // It is simpler to do this than hold the reverse mappings.
    typename map<OspfTypes::AreaID, AreaRouter<A> *>::iterator i;
    for(i = _areas.begin(); i != _areas.end(); i++)
	(*i).second->delete_peer(peerid);

    // Remove the interface/vif to PeerID mapping
    typename map<string, OspfTypes::PeerID>::iterator pi;
    for(pi = _pmap.begin(); pi != _pmap.end(); pi++)
	if ((*pi).second == peerid) {
	    _pmap.erase(pi);
	    break;
	}

    return true;
}

template <typename A>
bool
PeerManager<A>::set_state_peer(const OspfTypes::PeerID peerid, bool state)
{
    debug_msg("PeerID %u\n", peerid);

    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    _peers[peerid]->set_state(state);

    return true;
}

template <typename A>
bool
PeerManager<A>::set_link_status_peer(const OspfTypes::PeerID peerid,
				     bool state)
{
    debug_msg("PeerID %u\n", peerid);

    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    _peers[peerid]->set_link_status(state, "PeerManager::set_link_status_peer");

    return true;
}

template <typename A>
bool
PeerManager<A>::add_address_peer(const string& interface, const string& vif,
				 OspfTypes::AreaID area, A addr)
{
    debug_msg("interface %s vif %s area %s address %s\n", interface.c_str(),
	      vif.c_str(), pr_id(area).c_str(), cstring(addr));

    // Get the prefix length.
    uint16_t prefix;
    if (!_ospf.get_prefix_length(interface, vif, addr, prefix)) {
	XLOG_WARNING("Unable to get prefix for %s ", cstring(addr));
	return false;
    }

    // An exception will be thrown if there is a problem.
    OspfTypes::PeerID peerid = get_peerid(interface, vif);

    set<AddressInfo<A> >& info = _peers[peerid]->get_address_info(area);

    info.insert(AddressInfo<A>(addr, prefix));

    recompute_addresses_peer(peerid, area);

    return true;
}

template <typename A>
bool 
PeerManager<A>::remove_address_peer(const OspfTypes::PeerID peerid,
				    OspfTypes::AreaID area, A addr)
{
    debug_msg("PeerID %u, area %s address %s\n", peerid, pr_id(area).c_str(),
	      cstring(addr));

    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    set<AddressInfo<A> >& info = _peers[peerid]->get_address_info(area);

    info.erase(AddressInfo<A>(addr));

    recompute_addresses_peer(peerid, area);

    return true;
}

template <typename A>
bool
PeerManager<A>::set_address_state_peer(const OspfTypes::PeerID peerid,
				       OspfTypes::AreaID area, A addr,
				       bool enable)
{
    debug_msg("PeerID %u, area %s address %s enable %s\n",
	      peerid, pr_id(area).c_str(), cstring(addr), bool_c_str(enable));

    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    set<AddressInfo<A> >& info = _peers[peerid]->get_address_info(area);
    typename set<AddressInfo<A> >:: iterator i = 
	info.find(AddressInfo<A>(addr));

    if (i == info.end()) {
	XLOG_ERROR("Couldn't find %s", cstring(addr));
	return false;
    }
    
    AddressInfo<A> naddr((*i)._address, (*i)._prefix, enable);

    info.erase(i);
    info.insert(naddr);

    recompute_addresses_peer(peerid, area);

    return true;
}

template <typename A>
bool
PeerManager<A>::activate_peer(const string& interface, const string& vif,
			      OspfTypes::AreaID area)
{
    debug_msg("interface %s vif %s area %s\n", interface.c_str(),
	      vif.c_str(), pr_id(area).c_str());

    // An exception will be thrown if there is a problem.
    OspfTypes::PeerID peerid = get_peerid(interface, vif);

    recompute_addresses_peer(peerid, area);

    A source = _peers[peerid]->get_interface_address();
    _peers[peerid]->set_link_status(enabled(interface, vif, source), "activate_peer");

    return true;
}

template <typename A>
bool
PeerManager<A>::update_peer(const string& interface, const string& vif,
			    OspfTypes::AreaID area)
{
    debug_msg("interface %s vif %s area %s\n", interface.c_str(),
	      vif.c_str(), pr_id(area).c_str());

    // Not currently required.

    return true;
}

template <typename A>
bool
PeerManager<A>::recompute_addresses_peer(const OspfTypes::PeerID peerid,
					 OspfTypes::AreaID area)
{
    debug_msg("PeerID %u area %s\n", peerid, pr_id(area).c_str());

    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    set<AddressInfo<A> >& info = _peers[peerid]->get_address_info(area);
    
    // Unconditionally remove all the global addresses that are being
    // advertised.
    _peers[peerid]->remove_all_nets(area);

    // If no addresses have been configured then advertise all the
    // configured addresses.
    if (info.empty()) {
	string interface, vif;
	if (!get_interface_vif_by_peerid(peerid, interface, vif)) {
	    XLOG_ERROR("Unable to find interface/vif associated with "
		       "PeerID %u", peerid);
	    return false;
	}
	// Before trying to get the addresses verify that this
	// interface/vif exists and has a usable address configured.
	if (!enabled(interface, vif, _peers[peerid]->get_interface_address()))
	    return false;
	list<A> addresses;
	if (!_ospf.get_addresses(interface, vif, addresses)) {
	    XLOG_ERROR("Unable to find addresses on %s/%s ", interface.c_str(),
		       vif.c_str());
	    return false;
	}
	typename list<A>::iterator i;
	for (i = addresses.begin(); i != addresses.end(); i++) {
	    if ((*i).is_linklocal_unicast())
		continue;
	    uint16_t interface_prefix_length;
	    if (!_ospf.get_prefix_length(interface, vif, *i,
					 interface_prefix_length)) {
		XLOG_ERROR("Unable to get prefix length for %s", cstring(*i));
		continue;
	    }
	    if (!_peers[peerid]->add_advertise_net(area, (*i),
						   interface_prefix_length)) {
		XLOG_WARNING("Unable to advertise %s in Link-LSA\n",
			     cstring(*i));
	    }
	}
    } else {
	typename set<AddressInfo<A> >::iterator i;
	for (i = info.begin(); i != info.end(); i++) {
	    if ((*i)._enabled) {
		if (!_peers[peerid]->add_advertise_net(area, (*i)._address,
						      (*i)._prefix)) {
		    XLOG_WARNING("Unable to advertise %s in Link-LSA\n",
				 cstring((*i)._address));
		}
	    }
	}
    }

    // Force out a new Link-LSA.
    return _peers[peerid]->update_nets(area);
}

template <typename A>
void
PeerManager<A>::vif_status_change(const string& interface, const string& vif,
				  bool state)
{
    XLOG_WARNING("interface %s vif %s state %s\n",
		 interface.c_str(), vif.c_str(), bool_c_str(state));

    OspfTypes::PeerID peerid;

    // All interface/vif/address changes on the host come through
    // here, ignore the changes that are not for OSPF.
    try {
	peerid = get_peerid(interface, vif);
    } catch(...) {
	return;
    }

    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return;
    }

    _peers[peerid]->set_link_status(state, "PeerManager::vif_status_change");

    return;
}

template <typename A>
void
PeerManager<A>::address_status_change(const string& interface,
				      const string& vif, A source,
				      bool state)
{
    debug_msg("interface %s vif %s address %s state %s\n",
	      interface.c_str(), vif.c_str(), cstring(source),
	      bool_c_str(state));

    OspfTypes::PeerID peerid;

    // All interface/vif/address changes on the host come through
    // here, ignore the changes that are not for OSPF.
    try {
	peerid = get_peerid(interface, vif);
    } catch(...) {
	return;
    }

    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return;
    }

    _peers[peerid]->set_link_status(enabled(interface,
					    vif,
					    _peers[peerid]->
					    get_interface_address()), "address_status_change");

    switch(_ospf.get_version()) {
    case OspfTypes::V2:
	break;
    case OspfTypes::V3:
	list<OspfTypes::AreaID> areas;
	_peers[peerid]->get_areas(areas);
	list<OspfTypes::AreaID>::iterator i;
	for (i = areas.begin(); i != areas.end(); i++)
	    recompute_addresses_peer(peerid, *i);
	break;
    }

    return;
}

template <typename A>
bool
PeerManager<A>::add_neighbour(const OspfTypes::PeerID peerid,
			      OspfTypes::AreaID area,
			      A neighbour_address, OspfTypes::RouterID rid)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->add_neighbour(area, neighbour_address, rid);
}

template <typename A>
bool
PeerManager<A>::remove_neighbour(const OspfTypes::PeerID peerid,
				 OspfTypes::AreaID area,
				 A neighbour_address, OspfTypes::RouterID rid)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->remove_neighbour(area, neighbour_address, rid);
}

template <typename A>
bool
PeerManager<A>::transmit(const string& interface, const string& vif,
			 A dst, A src,
			 uint8_t* data, uint32_t len)
{
    XLOG_TRACE(_ospf.trace()._packets,
	       "PeerManager::transmit  Interface %s Vif %s data %p len %u  src: %s  dst: %s\n",
	       interface.c_str(), vif.c_str(), data, len, src.str().c_str(),
	       dst.str().c_str());

    if (string(VLINK) == interface) {
	string interface;
	string vif;
	if (_vlink.get_physical_interface_vif(src, dst, interface, vif))
	    return _ospf.transmit(interface, vif, dst, src,
                                  VLINK_DEF_TTL, data, len);
	// We didn't find a match fall through.
    }

    return _ospf.transmit(interface, vif, dst, src, -1, data, len);
}

template <typename A>
bool
PeerManager<A>::receive(const string& interface, const string& vif,
			A dst, A src, Packet *packet)
    throw(BadPeer)
{
    XLOG_TRACE(_ospf.trace()._packets,
	       "Interface %s Vif %s src %s dst %s %s\n", interface.c_str(),
	       vif.c_str(), cstring(dst), cstring(src), cstring((*packet)));

    OspfTypes::PeerID peerid = get_peerid(interface, vif);
    XLOG_ASSERT(0 != _peers.count(peerid));
    return _peers[peerid]->receive(dst, src, packet);
}

template <typename A>
bool
PeerManager<A>::clear_database()
{
    // Drop all adjacencies.
    typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator p;
    for(p = _peers.begin(); p != _peers.end(); p++) {
	// If this peer is up then taking it down and bringing it up
	// should drop all adjacencies.
	if( (*p).second->get_state()) {
	    (*p).second->set_state(false);
	    (*p).second->set_state(true);
	}
    }

    // Clear the AS-External-LSA database.
    _external.clear_database();

    // Clear the area databases.
    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator a;
    for (a = _areas.begin(); a != _areas.end(); a++) {
	// Changing the area type of the router will delete the
	// Router-LSA generated by this router and revive it.
	(*a).second->change_area_router_type((*a).second->get_area_type());
    }

    // Recompute the routes in all areas to withdraw the routes and
    // remove virtual links. This will also cause the summary routes
    // to be withdrawn. Should not be required as dropping the
    // adjacencies should prompt a route recomputation.
    routing_recompute_all_areas();

    typename map<IPNet<A>, Summary>::iterator i;
    for(i = _summaries.begin(); i != _summaries.end(); i++)
	XLOG_WARNING("Summary not removed %s %s", cstring(i->first),
		     cstring(i->second._rtentry));

    return true;
}

template <typename A>
bool
PeerManager<A>::queue_lsa(const OspfTypes::PeerID peerid,
			  const OspfTypes::PeerID peer,
			  OspfTypes::NeighbourID nid, Lsa::LsaRef lsar,
			  bool &multicast_on_peer)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->queue_lsa(peer, nid, lsar, multicast_on_peer);
}

template <typename A>
bool
PeerManager<A>::push_lsas(const OspfTypes::PeerID peerid, const char* msg)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->push_lsas(msg);
}

template <typename A>
uint32_t
PeerManager<A>::get_interface_id(const OspfTypes::PeerID peerid)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return 0;
    }

    return _peers[peerid]->get_interface_id();
}

template <typename A>
bool
PeerManager<A>::get_attached_routers(const OspfTypes::PeerID peerid,
				     OspfTypes::AreaID area,
				     list<RouterInfo>& routers)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->get_attached_routers(area, routers);
}

template <typename A>
bool
PeerManager<A>::configured_network(const A address) const
{
    typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator i;
    for(i = _peers.begin(); i != _peers.end(); i++) {
	if ((*i).second->get_interface_prefix_length() != 0) {
	    IPNet<A> net((*i).second->get_interface_address(),
			 (*i).second->get_interface_prefix_length());
	    if (net.contains(address)) 
		return true;
	}
    }

    return false;
}

template <typename A>
bool
PeerManager<A>::known_interface_address(const A address) const
{
    // XXX
    // Note we are only checking the interface addresses of the
    // configured peers. We should be checking the interface addresses
    // with the FEA.

    typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator i;
    for(i = _peers.begin(); i != _peers.end(); i++)
	if ((*i).second->get_interface_address() == address) 
	    return true;

    return false;
}

template <typename A>
bool
PeerManager<A>::neighbours_exchange_or_loading(const OspfTypes::PeerID peerid,
					       OspfTypes::AreaID area)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->neighbours_exchange_or_loading(area);
}

template <typename A>
bool
PeerManager<A>::neighbour_at_least_two_way(const OspfTypes::PeerID peerid,
					   OspfTypes::AreaID area,
					   OspfTypes::RouterID rid,
					   bool& twoway)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->neighbour_at_least_two_way(area, rid, twoway);
}

template <typename A>
bool
PeerManager<A>::get_neighbour_address(const OspfTypes::PeerID peerid,
				      OspfTypes::AreaID area,
				      OspfTypes::RouterID rid,
				      uint32_t interface_id,
				      A& neighbour_address)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->
	get_neighbour_address(area, rid, interface_id, neighbour_address);
}

template <typename A>
bool
PeerManager<A>::on_link_state_request_list(const OspfTypes::PeerID peerid,
					   OspfTypes::AreaID area,
					   const OspfTypes::NeighbourID nid,
					   Lsa::LsaRef lsar)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->on_link_state_request_list(area, nid, lsar);
}

template <typename A>
bool
PeerManager<A>::event_bad_link_state_request(const OspfTypes::PeerID peerid,
					     OspfTypes::AreaID area,
					     const OspfTypes::NeighbourID nid)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->event_bad_link_state_request(area, nid);
}

template <typename A>
bool
PeerManager<A>::send_lsa(const OspfTypes::PeerID peerid,
			 OspfTypes::AreaID area,
			 const OspfTypes::NeighbourID nid, Lsa::LsaRef lsar)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->send_lsa(area, nid, lsar);
}

template <typename A>
void
PeerManager<A>::adjacency_changed(const OspfTypes::PeerID peerid,
				  OspfTypes::RouterID rid,
				  bool up)
{
    if (0 == _peers.count(peerid))
	XLOG_FATAL("Unknown PeerID %u", peerid);
    
    // Is this neighbour a virtual link?
    if (!_peers[peerid]->virtual_link_endpoint(OspfTypes::BACKBONE))
	return;

    OspfTypes::AreaID transit_area;
    if (!_vlink.get_transit_area(rid, transit_area))
	return;

    list<OspfTypes::RouterID> rids;
    _vlink.get_router_ids(transit_area, rids);

    uint32_t fully_adjacent_virtual_links = 0;
    typename list<OspfTypes::RouterID>::const_iterator i;
    for(i = rids.begin(); i != rids.end(); i++) {
	OspfTypes::PeerID peerid = _vlink.get_peerid(*i);
	typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator j;
	j = _peers.find(peerid);
	if(j == _peers.end()) {
	    // A peerid can be removed and the vlink database is not notified.
	    // Happens during shutdown.
	    XLOG_WARNING("Peer not found %d", peerid);
	    continue;
	}
	if ((*j).second->virtual_link_endpoint(OspfTypes::BACKBONE))
	    fully_adjacent_virtual_links++;
    }

    // Only care if there are no fully adjacent virtual links left or
    // this is the first fully adjacent virtual link.
    switch(fully_adjacent_virtual_links) {
    case 0:
	XLOG_ASSERT(!up);
	break;
    case 1:
	XLOG_ASSERT(up);
	break;
    default:
	return;
    }

    AreaRouter<A> *area_router = get_area_router(transit_area);
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(transit_area).c_str());
	return;
    }

    area_router->refresh_router_lsa();
}

template <typename A>
void
PeerManager<A>::area_border_router_transition(bool up) const
{
    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator i;
    for (i = _areas.begin(); i != _areas.end(); i++) {
	(*i).second->area_border_router_transition(up);
    }
}

template <typename A>
void
PeerManager<A>::refresh_router_lsas() const
{
    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator i;
    for (i = _areas.begin(); i != _areas.end(); i++) {
	(*i).second->refresh_router_lsa();
    }
}

template <typename A>
bool
PeerManager<A>::create_virtual_peer(OspfTypes::RouterID rid)
{
    string ifname;
    string vifname;

    if (!_vlink.get_interface_vif(rid, ifname, vifname)) {
	XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str());
	return false;
    }

    OspfTypes::PeerID peerid;
    try {
	peerid = create_peer(ifname, vifname, A::ZERO(),
			     OspfTypes::VirtualLink, OspfTypes::BACKBONE);
    } catch(XorpException& e) {
	XLOG_ERROR("%s", cstring(e));
	return false;
    }

    if (!_vlink.add_peerid(rid, peerid)) {
	XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str());
	return false;
    }

    return true;
}

template <typename A>
bool
PeerManager<A>::delete_virtual_peer(OspfTypes::RouterID rid)
{
    OspfTypes::PeerID peerid = _vlink.get_peerid(rid);
    if (OspfTypes::ALLPEERS != peerid) {
	try {
	    delete_peer(peerid);
	} catch(XorpException& e) {
	    XLOG_ERROR("%s", cstring(e));
	}
	// This PeerID has now been deleted so remove it from the record.
	// This is not strictly necessary as we are about to delete
	// this virtual link, but is is possible that in the future
	// removing the virtual link from the area router may cause an
	// upcall.
	_vlink.add_peerid(rid, OspfTypes::ALLPEERS);
    }

    return true;
}

template <typename A>
bool
PeerManager<A>::virtual_link_endpoint(OspfTypes::AreaID area) const
{
    list<OspfTypes::RouterID> rids;
    _vlink.get_router_ids(area, rids);

    typename list<OspfTypes::RouterID>::const_iterator i;
    for(i = rids.begin(); i != rids.end(); i++) {
	OspfTypes::PeerID peerid = _vlink.get_peerid(*i);
	typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator j;
	j = _peers.find(peerid);
	if(j == _peers.end()) {
	    // A peerid can be removed and the vlink database is not notified.
	    // Happens during shutdown.
	    XLOG_WARNING("Peer not found %d", peerid);
	    continue;
	}
	if ((*j).second->virtual_link_endpoint(OspfTypes::BACKBONE))
	    return true;
    }

    return false;
}

template <typename A> 
bool
PeerManager<A>::create_virtual_link(OspfTypes::RouterID rid)
{
    XLOG_TRACE(_ospf.trace()._virtual_link,
	       "Create virtual link rid %s\n", pr_id(rid).c_str());

    if (!_vlink.create_vlink(rid))
	return false;

    return create_virtual_peer(rid);
}

template <typename A> 
bool 
PeerManager<A>::transit_area_virtual_link(OspfTypes::RouterID rid,
					  OspfTypes::AreaID transit_area)
{
    XLOG_TRACE(_ospf.trace()._virtual_link,
	       "Add transit area to virtual link rid %s transit area %s\n",
	       pr_id(rid).c_str(),
	       pr_id(transit_area).c_str());

    OspfTypes::AreaID oarea;
    if (!_vlink.get_transit_area(rid, oarea))
	return false;

    // Has the current transit area been told about this router ID.
    bool notified = _vlink.get_transit_area_notified(rid);

    if (oarea == transit_area) {
	if (notified)
	    return true;
	AreaRouter<A> *area = get_area_router(transit_area);
	if (0 == area)
	    return false;
	// Might be a stub area turning us down.
	if (!area->add_virtual_link(rid))
	    return false;
	_vlink.set_transit_area_notified(rid, true);
	return true;
    }

    // We are now dealing with two separate areas.

    if (!_vlink.set_transit_area(rid, transit_area))
	return false;

    if (notified) {
	if (OspfTypes::BACKBONE != oarea) {
	    AreaRouter<A> *parea = get_area_router(oarea);
	    if (parea)
		parea->remove_virtual_link(rid);
	}
    }

    AreaRouter<A> *area = get_area_router(transit_area);
    _vlink.set_transit_area_notified(rid, false);
    if (0 == area) {
	return false;
    }
    if (!area->add_virtual_link(rid)) {
	return false;
    }
    _vlink.set_transit_area_notified(rid, true);

    return true;
}

template <typename A> 
bool 
PeerManager<A>::delete_virtual_link(OspfTypes::RouterID rid)
{
    XLOG_TRACE(_ospf.trace()._virtual_link,
	       "Delete virtual link rid %s\n", pr_id(rid).c_str());

    delete_virtual_peer(rid);

    // If a transit area is configured then remove this virtual link
    // from that area.
    OspfTypes::AreaID transit_area;
    if (!_vlink.get_transit_area(rid, transit_area)) {
	XLOG_WARNING("Couldn't find rid %s", pr_id(rid).c_str());
	return false;
    }

    if (OspfTypes::BACKBONE != transit_area) {
	AreaRouter<A> *area = get_area_router(transit_area);
	// Having no associated area is perfectly legal.
	if (0 != area) {
	    area->remove_virtual_link(rid);
	}
    }

    return _vlink.delete_vlink(rid);
}

template <typename A> 
void
PeerManager<A>::up_virtual_link(OspfTypes::RouterID rid, A source,
				uint16_t interface_cost, A destination)
{
    XLOG_TRACE(_ospf.trace()._virtual_link,
	       "Virtual link up rid %s source %s cost %d destination %s\n",
	       pr_id(rid).c_str(),
	       cstring(source), interface_cost, cstring(destination));

    if (!_vlink.add_address(rid, source, destination)) {
	XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str());
	return;
    }

    string ifname;
    string vifname;
    if (!_vlink.get_interface_vif(rid, ifname, vifname)) {
	XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str());
	return;
    }

    OspfTypes::PeerID peerid = _vlink.get_peerid(rid);

    // Scan through the peers and find the interface and vif that
    // match the source address.
    typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator i;
    for(i = _peers.begin(); i != _peers.end(); i++) {
	if ((*i).second->match(source, ifname, vifname)) {
	    if (!_vlink.set_physical_interface_vif(rid, ifname, vifname))
		XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str());
	    break;
	}
    }

    if (!set_interface_address(peerid, source))
	return;

    if (!set_interface_cost(peerid, OspfTypes::BACKBONE, interface_cost))
	return;

    if (!add_neighbour(peerid, OspfTypes::BACKBONE, destination, rid))
	return;
    
    if (!set_state_peer(peerid, true))
	return;

    if (!set_link_status_peer(peerid, true))
	return;
}

template <typename A> 
void
PeerManager<A>::down_virtual_link(OspfTypes::RouterID rid)

{
    XLOG_TRACE(_ospf.trace()._virtual_link,
	       "Virtual link down rid %s\n", pr_id(rid).c_str());

    OspfTypes::PeerID peerid = _vlink.get_peerid(rid);
    if (OspfTypes::ALLPEERS == peerid) {
	XLOG_WARNING("No peer found when dropping virtual link %s",
		     pr_id(rid).c_str());
	return;
    }

    if (!set_state_peer(peerid, false))
	return;

    A source, destination;
    if (!_vlink.get_address(rid, source, destination)) {
	XLOG_FATAL("Router ID not found %s", pr_id(rid).c_str());
	return;
    }

    if (!remove_neighbour(peerid, OspfTypes::BACKBONE, destination, rid))
	return;
}

template <typename A> 
bool
PeerManager<A>::receive_virtual_link(A dst, A src, Packet *packet)
{
    XLOG_TRACE(_ospf.trace()._virtual_link,
	       "Virtual link receive dest %s src %s packet %s\n",
	       cstring(dst), cstring(src), cstring(*packet));

    OspfTypes::PeerID peerid = _vlink.get_peerid(dst, src);
    if (OspfTypes::ALLPEERS == peerid)
	return false;
    XLOG_ASSERT(0 != _peers.count(peerid));
    return _peers[peerid]->receive(dst, src, packet);

    return false;
}

template <typename A> 
uint32_t
PeerManager<A>::compute_options(OspfTypes::AreaType area_type)

{
    // Set/UnSet E-Bit.
    Options options(_ospf.get_version(), 0);
    switch(area_type) {
    case OspfTypes::NORMAL:
	options.set_e_bit(true);
	options.set_n_bit(false);
	break;
    case OspfTypes::STUB:
	options.set_e_bit(false);
	options.set_n_bit(false);
	break;
    case OspfTypes::NSSA:
	options.set_e_bit(false);
	options.set_n_bit(true);
	break;
    }

    switch (_ospf.get_version()) {
    case OspfTypes::V2:
	break;
    case OspfTypes::V3:
	// XXX - Unconditionally set the router bit which means that
	// we want to participate in the routing, the passive and
	// loopback settings should be taken into account, when
	// setting this value.
	options.set_r_bit(true);
	options.set_v6_bit(true);
	break;
    }

    return options.get_options();
}

#if	0
template <typename A> 
bool
PeerManager<A>::set_options(const OspfTypes::PeerID peerid,
			    OspfTypes::AreaID area,
			    uint32_t options)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_options(area, options);
}
#endif

template <typename A>
void
PeerManager<A>::router_id_changing()
{
    typename map<OspfTypes::PeerID, PeerOut<A> *>::const_iterator i;
    for(i = _peers.begin(); i != _peers.end(); i++)
	(*i).second->router_id_changing();
}

template <typename A> 
bool
PeerManager<A>::set_interface_address(const OspfTypes::PeerID peerid,
				      A address)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_interface_address(address);
}

template <typename A>
bool
PeerManager<A>::set_hello_interval(const OspfTypes::PeerID peerid,
				   OspfTypes::AreaID area,
				   uint16_t hello_interval)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_hello_interval(area, hello_interval);
}

template <typename A> 
bool
PeerManager<A>::set_router_priority(const OspfTypes::PeerID peerid,
				    OspfTypes::AreaID area,
				    uint8_t priority)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_router_priority(area, priority);
}

template <typename A>
bool
PeerManager<A>::set_router_dead_interval(const OspfTypes::PeerID peerid,
					 OspfTypes::AreaID area, 
					 uint32_t router_dead_interval)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->
	set_router_dead_interval(area, router_dead_interval);
}

template <typename A>
bool
PeerManager<A>::set_interface_cost(const OspfTypes::PeerID peerid, 
				   OspfTypes::AreaID /*area*/,
				   uint16_t interface_cost)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_interface_cost(interface_cost);
}

template <typename A>
bool
PeerManager<A>::set_retransmit_interval(const OspfTypes::PeerID peerid, 
					OspfTypes::AreaID area,
					uint16_t retransmit_interval)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_retransmit_interval(area, retransmit_interval);
}

template <typename A>
bool
PeerManager<A>::set_inftransdelay(const OspfTypes::PeerID peerid, 
				   OspfTypes::AreaID /*area*/,
				   uint16_t inftransdelay)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_inftransdelay(inftransdelay);
}

template <typename A>
bool
PeerManager<A>::set_simple_authentication_key(const OspfTypes::PeerID peerid,
					      OspfTypes::AreaID	area,
					      const string&	password,
					      string&		error_msg)
{
    if (0 == _peers.count(peerid)) {
	error_msg = c_format("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_simple_authentication_key(area, password,
							 error_msg);
}

template <typename A>
bool
PeerManager<A>::delete_simple_authentication_key(const OspfTypes::PeerID
						 peerid,
						 OspfTypes::AreaID area,
						 string&	error_msg)
{
    if (0 == _peers.count(peerid)) {
	error_msg = c_format("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->delete_simple_authentication_key(area, error_msg);
}

template <typename A>
bool
PeerManager<A>::set_md5_authentication_key(const OspfTypes::PeerID peerid,
					   OspfTypes::AreaID	area,
					   uint8_t		key_id,
					   const string&	password,
					   const TimeVal&	start_timeval,
					   const TimeVal&	end_timeval,
					   const TimeVal&	max_time_drift,
					   string&		error_msg)
{
    if (0 == _peers.count(peerid)) {
	error_msg = c_format("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_md5_authentication_key(area, key_id, password,
						      start_timeval,
						      end_timeval,
						      max_time_drift,
						      error_msg);
}

template <typename A>
bool
PeerManager<A>::delete_md5_authentication_key(const OspfTypes::PeerID peerid,
					      OspfTypes::AreaID	area,
					      uint8_t		key_id,
					      string&		error_msg)
{
    if (0 == _peers.count(peerid)) {
	error_msg = c_format("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->delete_md5_authentication_key(area, key_id,
							 error_msg);
}

template <typename A>
bool
PeerManager<A>::set_passive(const OspfTypes::PeerID peerid,
			    OspfTypes::AreaID area,
			    bool passive, bool host)
{
    if (0 == _peers.count(peerid)) {
	XLOG_ERROR("Unknown PeerID %u", peerid);
	return false;
    }

    return _peers[peerid]->set_passive(area, passive, host);
}

template <typename A>
bool
PeerManager<A>::originate_default_route(OspfTypes::AreaID area, bool enable)
{
    debug_msg("Area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable));

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->originate_default_route(enable);
}

template <typename A>
bool
PeerManager<A>::stub_default_cost(OspfTypes::AreaID area, uint32_t cost)
{
    debug_msg("Area %s cost %u\n", pr_id(area).c_str(), cost);

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->stub_default_cost(cost);
}

template <typename A>
bool
PeerManager<A>::summaries(OspfTypes::AreaID area, bool enable)
{
    debug_msg("Area %s enable %s\n", pr_id(area).c_str(), bool_c_str(enable));

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->summaries(enable);
}

template <typename A>
void
PeerManager<A>::track_area_count(OspfTypes::AreaType area_type, bool up)
{
    int delta = up ? 1 : -1;

    switch(area_type) {
    case OspfTypes::NORMAL:
	_normal_cnt += delta;
	break;
    case OspfTypes::STUB:
	_stub_cnt += delta;
	break;
    case OspfTypes::NSSA:
	_nssa_cnt += delta;
	break;
    }
}

template <typename A>
uint32_t
PeerManager<A>::area_count(OspfTypes::AreaType area_type) const
{
    switch(area_type) {
    case OspfTypes::NORMAL:
	return _normal_cnt;
	break;
    case OspfTypes::STUB:
	return _stub_cnt;
	break;
    case OspfTypes::NSSA:
	return _nssa_cnt;
	break;
    }

    XLOG_UNREACHABLE();

    return 0;
}

template <typename A>
bool
PeerManager<A>::internal_router_p() const
{
    // True if are connected to only one area.
    return 1 == _areas.size() ? true : false;
}

template <typename A>
bool
PeerManager<A>::area_border_router_p() const
{
    // True if this router is connected to multiple areas..
    return 1 < _areas.size() ? true : false;
}

static const OspfTypes::AreaID BACKBONE = OspfTypes::BACKBONE;

template <typename A>
bool
PeerManager<A>::backbone_router_p() const
{
    // True if one of the areas the router is connected to is the
    // backbone area.
    // XXX - The line below should be OspfTypes::BACKBONE the gcc34
    // compiler rejected this hence the local declaration.
    return 1 == _areas.count(BACKBONE) ? true : false;
}

template <typename A>
bool
PeerManager<A>::as_boundary_router_p() const
{
    return _external.as_boundary_router_p();
}

template <typename A>
void
PeerManager<A>::external_push_routes()
{
    _external.push_routes();
}

template <typename A>
void
PeerManager<A>::external_suppress_lsas(OspfTypes::AreaID area)
{
    _external.suppress_lsas(area);
}

template <typename A>
void
PeerManager<A>::routing_recompute_all_areas()
{
    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator i;
    for (i = _areas.begin(); i != _areas.end(); i++)
	if ((*i).first == BACKBONE) {
	    (*i).second->routing_total_recompute();
	    break;
	}

    // Once the backbone is recomputed any transit areas will also be
    // recomputed; so they don't need to be computed again.
    for (i = _areas.begin(); i != _areas.end(); i++)
	if ((*i).first != BACKBONE) {
	    if (!(*i).second->get_transit_capability())
		(*i).second->routing_total_recompute();
	    break;
	}
}

template <typename A>
void
PeerManager<A>::routing_recompute_all_transit_areas()
{
    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator i;
    for (i = _areas.begin(); i != _areas.end(); i++)
	if ((*i).first != BACKBONE)
	    if ((*i).second->get_transit_capability())
		(*i).second->routing_total_recompute();
}

template <typename A>
bool
PeerManager<A>::summary_candidate(OspfTypes::AreaID area, IPNet<A> net,
				  RouteEntry<A>& rt)
{
    debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(),
	      cstring(net), cstring(rt));

    if (_ospf.get_testing())
	return false;

    // RFC 2328 Section 12.4.3. Summary-LSAs
    // Select routes that are candidate for summarisation.

    bool candidate = false;

//     if (rt.get_directly_connected()) {
// 	debug_msg("Rejected directly connected route\n");
// 	return false;
//     }

    switch (rt.get_destination_type()) {
    case OspfTypes::Router:
	if (rt.get_as_boundary_router())
	    candidate = true;
	break;
    case OspfTypes::Network:
	candidate = true;
	break;
    }

    if (!candidate) {
	debug_msg("Rejected not an AS boundary or Network\n");
	return false;
    }

    switch (rt.get_path_type()) {
    case RouteEntry<A>::intra_area:
    case RouteEntry<A>::inter_area:
	candidate = true;
	break;
    case RouteEntry<A>::type1:
    case RouteEntry<A>::type2:
	candidate = false;
	break;
    }

    debug_msg("%s\n", candidate ? "Accepted" :
	      "Rejected not an intra/inter area route");

    return candidate;
}

template <typename A>
void
PeerManager<A>::summary_announce(OspfTypes::AreaID area, IPNet<A> net,
				 RouteEntry<A>& rt)
{
    debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(),
	      cstring(net), cstring(rt));

    if (!summary_candidate(area, net, rt))
	return;

    _external.suppress_route_announce(area, net, rt);

    // Save this route for possible later replay.
    XLOG_ASSERT(0 == _summaries.count(net));
    Summary s(area, rt);
    _summaries.insert(make_pair(net, s));

    if (!area_border_router_p())
	return;

    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator i;
    for (i = _areas.begin(); i != _areas.end(); i++)
	if ((*i).first != area)
	    (*i).second->summary_announce(area, net, rt, false);
}

template <typename A>
void
PeerManager<A>::summary_withdraw(OspfTypes::AreaID area, IPNet<A> net,
				 RouteEntry<A>& rt)
{
    debug_msg("Area %s net %s\n", pr_id(area).c_str(), cstring(net));

    if (!summary_candidate(area, net, rt))
	return;

    _external.suppress_route_withdraw(area, net, rt);

    // Remove this saved route.
    XLOG_ASSERT(1 == _summaries.count(net));
    _summaries.erase(_summaries.find(net));

    // This is an optimisation that will cause problems if the area
    // remove in the routing table becomes a background task. If we
    // transition from two areas to one area and the routes from the
    // other area are withdrawn in the background this test will stop
    // the withdraws making it through.
//     if (!area_border_router_p())
// 	return;

    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator i;
    for (i = _areas.begin(); i != _areas.end(); i++)
	if ((*i).first != area)
	    (*i).second->summary_withdraw(area, net, rt);
}

template <typename A>
void
PeerManager<A>::summary_replace(OspfTypes::AreaID area, IPNet<A> net,
				RouteEntry<A>& rt,
				RouteEntry<A>& previous_rt,
				OspfTypes::AreaID previous_area)
{
    debug_msg("Area %s net %s rentry %s\n", pr_id(area).c_str(),
	      cstring(net), cstring(rt));

    bool previous = summary_candidate(previous_area, net, previous_rt);
    bool current = summary_candidate(area, net, rt);

    if (current != previous) {
	if (previous)
	    summary_withdraw(previous_area, net, previous_rt);
	if (current)
	    summary_announce(area, net, rt);
	return;
    }

    XLOG_ASSERT(current == previous);

    // Don't need to check for !previous as it is the same as current.
    if (!current)
	return;

    XLOG_ASSERT(current);
    XLOG_ASSERT(previous);

    _external.suppress_route_withdraw(previous_area, net, previous_rt);
    _external.suppress_route_announce(area, net, rt);
    
    XLOG_ASSERT(1 == _summaries.count(net));
    _summaries.erase(_summaries.find(net));
    Summary s(area, rt);
    _summaries.insert(make_pair(net, s));

    typename map<OspfTypes::AreaID, AreaRouter<A> *>::const_iterator i;
    for (i = _areas.begin(); i != _areas.end(); i++) {
	if ((*i).first == area) {
	    if (area != previous_area)
		(*i).second->summary_withdraw(previous_area, net,
					      previous_rt);
	    continue;
	}
	if ((*i).first == previous_area) {
	    if (area != previous_area)
		(*i).second->summary_announce(area, net, rt, false);
	    continue;
	}
 	(*i).second->summary_replace(area, net, rt, previous_rt,previous_area);
    }
}

template <typename A>
void
PeerManager<A>::summary_push(OspfTypes::AreaID area)
{
    debug_msg("Area %s\n", pr_id(area).c_str());

    AreaRouter<A> *area_router = get_area_router(area);
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return;
    }

    if (!area_border_router_p())
	return;

    typename map<IPNet<A>, Summary>::const_iterator i;
    for (i = _summaries.begin(); i != _summaries.end(); i++) {
	IPNet<A> net = (*i).first;
	Summary s = (*i).second;
	if (s._area == area)
	    continue;
	area_router->summary_announce(s._area, net, s._rtentry, true);
    }
}

template <typename A>
bool
PeerManager<A>::area_range_covered(OspfTypes::AreaID area, IPNet<A> net,
				   bool& advertise)
{
    debug_msg("Area %s net %s\n", pr_id(area).c_str(), cstring(net));

    AreaRouter<A> *area_router = get_area_router(area);
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->area_range_covered(net, advertise);
}

template <typename A>
bool
PeerManager<A>::area_range_configured(OspfTypes::AreaID area)
{
    debug_msg("Area %s\n", pr_id(area).c_str());

    AreaRouter<A> *area_router = get_area_router(area);
    if (0 == area_router) {
	XLOG_WARNING("Unknown area %s", pr_id(area).c_str());
	return false;
    }

    return area_router->area_range_configured();
}

template <typename A>
bool
PeerManager<A>::external_announce(OspfTypes::AreaID area, Lsa::LsaRef lsar)
{
    debug_msg("Area %s LSA %s\n", pr_id(area).c_str(), cstring(*lsar));

    return _external.announce(area, lsar);
}

template <typename A>
bool
PeerManager<A>::external_announce(const IPNet<A>& net, const A& nexthop,
				  const uint32_t& metric,
				  const PolicyTags& policytags)
{
    debug_msg("Net %s nexthop %s metric %u\n", cstring(net), cstring(nexthop),
	      metric);

    return _external.announce(net, nexthop, metric, policytags);
}

template <typename A>
bool
PeerManager<A>::external_withdraw(OspfTypes::AreaID area, Lsa::LsaRef lsar)
{
    debug_msg("Area %s LSA %s\n", pr_id(area).c_str(), cstring(*lsar));

    XLOG_UNREACHABLE();

    return true;
}

template <typename A>
bool
PeerManager<A>::external_withdraw(const IPNet<A>& net)
{
    debug_msg("Net %s\n", cstring(net));

    return _external.withdraw(net);
}

template <typename A>
bool
PeerManager<A>::external_announce_complete(OspfTypes::AreaID area)
{
    debug_msg("Area %s\n", pr_id(area).c_str());

    return _external.announce_complete(area);
}

template <typename A>
void
PeerManager<A>::external_push(OspfTypes::AreaID area)
{
    debug_msg("Area %s\n", pr_id(area).c_str());

    AreaRouter<A> *area_router = get_area_router(area);

    // Verify that this area is known.
    if (0 == area_router) {
	XLOG_FATAL("Unknown area %s", pr_id(area).c_str());
    }
    return _external.push(area_router);
}

template class PeerManager<IPv4>;
template class PeerManager<IPv6>;
