// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-
// vim:set sts=4 ts=8:
// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.
#ident "$XORP: xorp/rib/rib.cc,v 1.63 2007/02/16 22:47:08 pavlin Exp $"
#include "rib_module.h"
#include "libxorp/xorp.h"
#include "libxorp/xlog.h"
#include "libxorp/debug.h"
#include "libxorp/c_format.hh"
#include "register_server.hh"
#include "rib_manager.hh"
#include "rib.hh"
// ----------------------------------------------------------------------------
// Inline table utility methods
template <typename A>
struct table_has_name {
inline table_has_name(const string& name) : _n(name) {}
inline bool operator() (const RouteTable<A>* rt) const {
return rt->tablename() == _n;
}
private:
const string& _n;
};
template <typename A, typename T>
struct table_has_name_and_type {
inline table_has_name_and_type(const string& name) : _n(name) {}
inline bool operator() (const RouteTable<A>* rt) const {
const T* t = dynamic_cast<const T*>(rt);
return (t != 0) && (rt->tablename() == _n);
}
private:
const string& _n;
};
template <typename A>
inline RouteTable<A>*
RIB<A>::find_table(const string& tablename)
{
typename list<RouteTable<A>* >::iterator li;
li = find_if(_tables.begin(), _tables.end(), table_has_name<A>(tablename));
if (li == _tables.end()) {
return NULL;
}
return *li;
}
template <typename A>
inline OriginTable<A>*
RIB<A>::find_table_by_instance(const string& tablename,
const string& target_class,
const string& target_instance)
{
typename map<string, OriginTable<A>* >::iterator mi;
mi = _routing_protocol_instances.find(tablename + " "
+ target_class + " "
+ target_instance);
if (mi == _routing_protocol_instances.end()) {
return NULL;
}
return mi->second;
}
template <typename A>
Protocol*
RIB<A>::find_protocol(const string& protocol)
{
typename map<string, Protocol* >::iterator mi = _protocols.find(protocol);
if (mi == _protocols.end()) {
return NULL;
}
return mi->second;
}
template <typename A>
inline int
RIB<A>::add_table(RouteTable<A>* table)
{
const string& tablename = table->tablename();
if (find_table(tablename) != NULL) {
XLOG_WARNING("add_table: table %s already exists", tablename.c_str());
return XORP_ERROR;
}
_tables.push_back(table);
return XORP_OK;
}
template <typename A>
inline int
RIB<A>::remove_table(const string& tablename)
{
typename list<RouteTable<A>* >::iterator li;
li = find_if(_tables.begin(), _tables.end(), table_has_name<A>(tablename));
if (li == _tables.end()) {
XLOG_WARNING("remove_table: table %s doesn't exist",
tablename.c_str());
return XORP_ERROR;
}
_tables.erase(li);
return XORP_OK;
}
template <typename A>
inline uint32_t
RIB<A>::admin_distance(const string& tablename)
{
map<string, uint32_t>::iterator mi;
mi = _admin_distances.find(tablename);
if (mi == _admin_distances.end()) {
XLOG_ERROR("Administrative distance of \"%s\" unknown.",
tablename.c_str());
return _admin_distances["unknown"];
}
return mi->second;
}
template <typename A>
inline Vif*
RIB<A>::find_vif(const A& addr)
{
map<string, Vif>::iterator iter;
for (iter = _vifs.begin(); iter != _vifs.end(); ++iter) {
Vif& vif = iter->second;
if (! vif.is_underlying_vif_up())
continue; // XXX: ignore vifs that are not up
if (vif.is_my_addr(addr))
return &vif;
if (vif.is_p2p() && vif.is_same_p2p(addr))
return &vif;
}
return NULL;
}
template <typename A>
inline IPExternalNextHop<A>*
RIB<A>::find_external_nexthop(const A& addr)
{
typename map<A, IPExternalNextHop<A> >::iterator mi;
mi = _external_nexthops.find(addr);
if (mi == _external_nexthops.end())
return NULL;
return &mi->second;
}
template <typename A>
inline IPPeerNextHop<A>*
RIB<A>::find_peer_nexthop(const A& addr)
{
typename map<A, IPPeerNextHop<A> >::iterator mi;
mi = _peer_nexthops.find(addr);
if (mi == _peer_nexthops.end())
return NULL;
return &mi->second;
}
template <typename A>
inline IPExternalNextHop<A>*
RIB<A>::find_or_create_external_nexthop(const A& addr)
{
IPExternalNextHop<A>* nexthop = find_external_nexthop(addr);
if (nexthop != NULL)
return nexthop;
typedef map<A, IPExternalNextHop<A> > C; // for convenience
typename C::value_type vt(addr, IPExternalNextHop<A>(addr));
typename C::iterator iter;
iter = _external_nexthops.insert(_external_nexthops.end(), vt);
return &iter->second;
}
template <typename A>
inline IPPeerNextHop<A>*
RIB<A>::find_or_create_peer_nexthop(const A& addr)
{
IPPeerNextHop<A>* nexthop = find_peer_nexthop(addr);
if (nexthop != NULL)
return nexthop;
typedef map<A, IPPeerNextHop<A> > C; // for convenience
typename C::value_type vt(addr, addr);
typename C::iterator iter;
iter = _peer_nexthops.insert(_peer_nexthops.end(), vt);
return &iter->second;
}
// ----------------------------------------------------------------------------
// Naming utility methods
static inline string
redist_tablename(const string& from_table)
{
return "Redist:" + from_table;
}
// ----------------------------------------------------------------------------
// RIB class
template <typename A>
RIB<A>::RIB(RibTransportType t, RibManager& rib_manager, EventLoop& eventloop)
: _rib_manager(rib_manager),
_eventloop(eventloop),
_final_table(NULL),
_register_table(NULL),
_errors_are_fatal(false),
_policy_redist_table(NULL)
{
if (t == UNICAST) {
_multicast = false;
} else if (t == MULTICAST) {
_multicast = true;
} else {
XLOG_FATAL("Unknown RibTransportType.");
}
// TODO: XXX: don't use hard-coded values below!
_admin_distances["connected"] = 0;
_admin_distances["static"] = 1;
_admin_distances["eigrp-summary"] = 5;
_admin_distances["ebgp"] = 20;
_admin_distances["eigrp-internal"] = 90;
_admin_distances["igrp"] = 100;
_admin_distances["ospf"] = 110;
_admin_distances["is-is"] = 115;
_admin_distances["rip"] = 120;
_admin_distances["eigrp-external"] = 170;
_admin_distances["ibgp"] = 200;
_admin_distances["fib2mrib"] = 254;
_admin_distances["unknown"] = 255;
}
template <typename A>
RIB<A>::~RIB()
{
while (_tables.empty() == false) {
delete _tables.front();
_tables.pop_front();
}
}
template <typename A>
list<string>
RIB<A>::registered_protocol_names() const
{
list<string> names;
map<string, Protocol* >::const_iterator iter;
for (iter = _protocols.begin(); iter != _protocols.end(); ++iter) {
names.push_back(iter->first);
}
return (names);
}
template <typename A>
void
RIB<A>::initialize(RegisterServer& register_server)
{
if (initialize_register(register_server) != XORP_OK) {
XLOG_FATAL("Could not initialize register table for %s",
name().c_str());
}
if (initialize_policy_redist() != XORP_OK) {
XLOG_FATAL("Could not initialize policy redistribution table for %s",
name().c_str());
}
//
// XXX: we must initialize the final RedistTable after the
// RegistTable has been initialized.
//
if (initialize_redist_all("all") != XORP_OK) {
XLOG_FATAL("Could not initialize all-protocol redistribution "
"table for %s",
name().c_str());
}
if (add_igp_table("connected", "", "") != XORP_OK) {
XLOG_FATAL("Could not add igp table \"connected\" for %s",
name().c_str());
}
}
template <typename A>
int
RIB<A>::initialize_policy_redist()
{
if (_register_table == NULL) {
XLOG_ERROR("Register table is not yet initialized");
return XORP_ERROR;
}
if (_policy_redist_table != NULL) {
return XORP_OK; // done already
}
_policy_redist_table =
new PolicyRedistTable<A>(_register_table, _rib_manager.xrl_router(),
_rib_manager.policy_redist_map(),
_multicast);
if (add_table(_policy_redist_table) != XORP_OK) {
delete _policy_redist_table;
_policy_redist_table = NULL;
return XORP_ERROR;
}
if (_final_table == NULL || _final_table == _register_table)
_final_table = _policy_redist_table;
return XORP_OK;
}
template <typename A>
int
RIB<A>::initialize_redist_all(const string& all)
{
if (_policy_redist_table == NULL) {
XLOG_ERROR("Policy redist table is not yet initialized");
return XORP_ERROR;
}
if (find_table(redist_tablename(all)) != NULL) {
return XORP_OK; // RedistTable already exists, no sweat
}
RedistTable<A>* r;
r = new RedistTable<A>(redist_tablename(all), _policy_redist_table);
if (add_table(r) != XORP_OK) {
delete r;
return XORP_ERROR;
}
//
// Set the RedistTable as the final table
//
if (_final_table == NULL || _final_table == _policy_redist_table)
_final_table = r;
return XORP_OK;
}
template <typename A>
int
RIB<A>::initialize_register(RegisterServer& register_server)
{
if (_register_table != NULL) {
XLOG_WARNING("Register table already initialized.");
return XORP_ERROR;
}
RegisterTable<A>* rt;
rt = new RegisterTable<A>("RegisterTable", register_server, _multicast);
if (add_table(rt) != XORP_OK) {
XLOG_WARNING("Add RegisterTable failed.");
delete rt;
return XORP_ERROR;
}
_register_table = rt;
if (_final_table == NULL) {
_final_table = _register_table;
} else {
_final_table->replumb(NULL, _register_table);
_register_table->set_next_table(_final_table);
}
return XORP_OK;
}
template <typename A>
int
RIB<A>::new_origin_table(const string& tablename,
const string& target_class,
const string& target_instance,
uint32_t admin_distance,
ProtocolType protocol_type)
{
OriginTable<A>* ot = new OriginTable<A>(tablename, admin_distance,
protocol_type, _eventloop);
if (ot == NULL || add_table(ot) != XORP_OK) {
XLOG_WARNING("Could not add origin table %s", tablename.c_str());
delete ot;
return XORP_ERROR;
}
if (_final_table == NULL) {
_final_table = ot;
}
//
// Store the XRL target instance, so we know which OriginTable to
// shutdown if the routing protocol dies.
//
if (!target_instance.empty()) {
_rib_manager.register_interest_in_target(target_class);
_routing_protocol_instances[tablename + " "
+ target_class + " "
+ target_instance] = ot;
}
return XORP_OK;
}
template <typename A>
int
RIB<A>::add_connected_route(const Vif& vif,
const IPNet<A>& net,
const A& nexthop_addr,
const A& peer_addr)
{
//
// XXX: the connected routes are added with the
// best possible metric (0).
//
add_route("connected", net, nexthop_addr, "", "", 0, PolicyTags());
if (vif.is_p2p() && (peer_addr != A::ZERO()) && (! net.contains(peer_addr))) {
add_route("connected", IPNet<A>(peer_addr, A::addr_bitlen()),
peer_addr, "", "", 0, PolicyTags());
}
return XORP_OK;
}
template <typename A>
int
RIB<A>::delete_connected_route(const Vif& vif, const IPNet<A>& net,
const A& peer_addr)
{
delete_route("connected", net);
if (vif.is_p2p() && (peer_addr != A::ZERO()) && (! net.contains(peer_addr))) {
delete_route("connected", IPNet<A>(peer_addr, A::addr_bitlen()));
}
return XORP_OK;
}
template <typename A>
int
RIB<A>::new_vif(const string& vifname, const Vif& vif)
{
debug_msg("RIB::new_vif: %s\n", vifname.c_str());
if (_vifs.find(vifname) != _vifs.end())
return XORP_ERROR;
// Can't use _vifs[vifname] = vif because no Vif() constructor
map<string, Vif>::value_type v(vifname, vif);
_vifs.insert(_vifs.end(), v);
// We need to add the routes from the VIF to the connected table
map<string, Vif>::iterator iter = _vifs.find(vifname);
XLOG_ASSERT(iter != _vifs.end());
Vif* new_vif = &(iter->second);
XLOG_ASSERT(new_vif != NULL);
if (vif.is_underlying_vif_up()) {
//
// Add the directly connected routes associated with this vif
//
list<VifAddr>::const_iterator ai;
for (ai = new_vif->addr_list().begin();
ai != new_vif->addr_list().end();
++ai) {
if (ai->addr().af() != A::af())
continue;
IPNet<A> subnet_addr;
A addr, peer_addr;
ai->subnet_addr().get(subnet_addr);
ai->addr().get(addr);
ai->peer_addr().get(peer_addr);
add_connected_route(*new_vif, subnet_addr, addr, peer_addr);
}
}
return XORP_OK;
}
template <typename A>
int
RIB<A>::delete_vif(const string& vifname)
{
debug_msg("RIB::delete_vif: %s\n", vifname.c_str());
map<string, Vif>::iterator vi = _vifs.find(vifname);
if (vi == _vifs.end()) {
return XORP_ERROR;
}
Vif& vif = vi->second;
if (vif.is_underlying_vif_up()) {
//
// Delete the directly connected routes associated with this vif
//
list<VifAddr>::const_iterator ai;
for (ai = vif.addr_list().begin(); ai != vif.addr_list().end(); ++ai) {
if (ai->addr().af() != A::af())
continue;
IPNet<A> subnet_addr;
A peer_addr;
ai->subnet_addr().get(subnet_addr);
ai->peer_addr().get(peer_addr);
delete_connected_route(vif, subnet_addr, peer_addr);
}
}
_vifs.erase(vi);
return XORP_OK;
}
template <typename A>
int
RIB<A>::set_vif_flags(const string& vifname,
bool is_p2p,
bool is_loopback,
bool is_multicast,
bool is_broadcast,
bool is_up,
uint32_t mtu)
{
map<string, Vif>::iterator vi = _vifs.find(vifname);
if (vi == _vifs.end()) {
XLOG_ERROR("Attempting to set flags to non-existant Vif \"%s\"",
vifname.c_str());
return XORP_ERROR;
}
Vif& vif = vi->second;
bool old_is_up = vif.is_underlying_vif_up();
vif.set_p2p(is_p2p);
vif.set_loopback(is_loopback);
vif.set_multicast_capable(is_multicast);
vif.set_broadcast_capable(is_broadcast);
vif.set_underlying_vif_up(is_up);
vif.set_mtu(mtu);
if (old_is_up == is_up)
return XORP_OK;
list<VifAddr>::const_iterator ai;
if (is_up) {
//
// Add all connected routes
//
for (ai = vif.addr_list().begin(); ai != vif.addr_list().end(); ++ai) {
if (ai->addr().af() != A::af())
continue;
IPNet<A> subnet_addr;
A addr, peer_addr;
ai->subnet_addr().get(subnet_addr);
ai->addr().get(addr);
ai->peer_addr().get(peer_addr);
add_connected_route(vif, subnet_addr, addr, peer_addr);
}
}
if (! is_up) {
//
// Delete all connected routes
//
for (ai = vif.addr_list().begin(); ai != vif.addr_list().end(); ++ai) {
if (ai->addr().af() != A::af())
continue;
IPNet<A> subnet_addr;
A peer_addr;
ai->subnet_addr().get(subnet_addr);
ai->peer_addr().get(peer_addr);
delete_connected_route(vif, subnet_addr, peer_addr);
}
}
return XORP_OK;
}
template <typename A>
int
RIB<A>::add_vif_address(const string& vifname,
const A& addr,
const IPNet<A>& subnet,
const A& broadcast_addr,
const A& peer_addr)
{
map<string, Vif>::iterator vi = _vifs.find(vifname);
if (vi == _vifs.end()) {
XLOG_ERROR("Attempting to add address to non-existant Vif \"%s\"",
vifname.c_str());
return XORP_ERROR;
}
Vif& vif = vi->second;
vif.add_address(VifAddr(addr, subnet, broadcast_addr, peer_addr));
if (vif.is_underlying_vif_up())
add_connected_route(vif, subnet, addr, peer_addr);
return XORP_OK;
}
template <typename A>
int
RIB<A>::delete_vif_address(const string& vifname,
const A& addr)
{
map<string, Vif>::iterator vi = _vifs.find(vifname);
if (vi == _vifs.end()) {
XLOG_ERROR("Attempting to delete address from non-existant Vif \"%s\"",
vifname.c_str());
return XORP_ERROR;
}
Vif& vif = vi->second;
list<VifAddr>::const_iterator ai;
for (ai = vif.addr_list().begin(); ai != vif.addr_list().end(); ++ai) {
const IPvX& ipvx = ai->addr();
if (ipvx.af() != A::af())
continue;
if (ipvx != IPvX(addr))
continue;
IPNet<A> subnet_addr;
A peer_addr;
ai->subnet_addr().get(subnet_addr);
ai->peer_addr().get(peer_addr);
vif.delete_address(ipvx);
if (vif.is_underlying_vif_up())
delete_connected_route(vif, subnet_addr, peer_addr);
return XORP_OK;
}
return XORP_ERROR;
}
template <typename A>
int
RIB<A>::add_route(const string& tablename,
const IPNet<A>& net,
const A& nexthop_addr,
const string& ifname,
const string& vifname,
uint32_t metric,
const PolicyTags& policytags)
{
RouteTable<A>* rt = find_table(tablename);
if (rt == NULL) {
if (_errors_are_fatal) {
XLOG_FATAL("Attempting to add route to unknown table \"%s\".",
tablename.c_str());
} else {
XLOG_ERROR("Attempting to add route to unknown table \"%s\".",
tablename.c_str());
return XORP_ERROR;
}
}
Protocol* protocol = find_protocol(tablename);
if (protocol == NULL) {
if (_errors_are_fatal) {
XLOG_FATAL("Attempting to add route with unknown protocol \"%s\".",
tablename.c_str());
} else {
XLOG_ERROR("Attempting to add route with unknown protocol \"%s\".",
tablename.c_str());
return XORP_ERROR;
}
}
OriginTable<A>* ot = dynamic_cast<OriginTable<A>* >(rt);
if (ot == NULL) {
if (_errors_are_fatal) {
XLOG_FATAL("Attempting to add route to table \"%s\" that is not "
"an origin table.", tablename.c_str());
} else {
XLOG_ERROR("Attempting to add route to table \"%s\" that is not "
"an origin table.", tablename.c_str());
return XORP_ERROR;
}
}
if (! vifname.empty()) {
//
// Add a route with explicitly specified network interface
//
map<string, Vif>::iterator iter = _vifs.find(vifname);
if (iter == _vifs.end()) {
XLOG_ERROR("Attempting to add route to table \"%s\" "
"(prefix %s next-hop %s ifname %s vifname %s): "
"no such network interface",
tablename.c_str(), net.str().c_str(),
nexthop_addr.str().c_str(),
ifname.c_str(), vifname.c_str());
return XORP_ERROR;
}
Vif* vif = &iter->second;
IPNextHop<A>* nexthop = find_or_create_peer_nexthop(nexthop_addr);
ot->add_route(IPRouteEntry<A>(net, vif, nexthop, *protocol,
metric, policytags));
flush();
return XORP_OK;
}
//
// Find the vif so we can see if the nexthop is directly connected
//
Vif* vif = NULL;
IPNextHop<A>* nexthop = NULL;
do {
//
// Search for a route to a directly-connected destination
//
const IPRouteEntry<A>* re = _final_table->lookup_route(nexthop_addr);
if (re != NULL) {
// We found a route for the nexthop
vif = re->vif();
if ((vif != NULL)
&& (vif->is_underlying_vif_up())
&& (vif->is_same_subnet(IPvXNet(re->net()))
|| vif->is_same_p2p(IPvX(nexthop_addr)))) {
debug_msg("**directly connected route found for nexthop\n");
break;
}
}
//
// We failed to find a route, or the route wasn't for a
// directly-connected destination. One final possibility is that
// we are trying to add a route for a directly connected subnet,
// so we need to test all the Vifs to see if this is the case.
//
vif = find_vif(nexthop_addr);
debug_msg("Vif %p\n", vif);
break;
} while (false);
if (vif == NULL) {
debug_msg("**not directly connected route found for nexthop\n");
//
// XXX: If the route came from an IGP, then we must have
// a directly-connected interface toward the next-hop router.
//
if (protocol->protocol_type() == IGP) {
XLOG_ERROR("Attempting to add IGP route to table \"%s\" "
"(prefix %s next-hop %s): no directly connected "
"interface toward the next-hop router",
tablename.c_str(), net.str().c_str(),
nexthop_addr.str().c_str());
return XORP_ERROR;
}
}
if (vif != NULL) {
nexthop = find_or_create_peer_nexthop(nexthop_addr);
} else {
nexthop = find_or_create_external_nexthop(nexthop_addr);
}
XLOG_ASSERT(nexthop->addr() == nexthop_addr);
//
// Only accept the least significant 16 bits of metric.
//
if (metric > 0xffff) {
XLOG_WARNING("Protocol metric value %u is greater than 0xffff from "
"table \"%s\"", XORP_UINT_CAST(metric),
tablename.c_str());
metric &= 0xffff;
}
//
// Add the route
//
ot->add_route(IPRouteEntry<A>(net, vif, nexthop,
*protocol, metric, policytags));
flush();
return XORP_OK;
}
template <typename A>
int
RIB<A>::replace_route(const string& tablename,
const IPNet<A>& net,
const A& nexthop_addr,
const string& ifname,
const string& vifname,
uint32_t metric,
const PolicyTags& policytags)
{
RouteTable<A>* rt = find_table(tablename);
if (NULL == rt)
return XORP_ERROR; // Table does not exist
OriginTable<A>* ot = dynamic_cast<OriginTable<A>* >(rt);
if (NULL == ot)
return XORP_ERROR; // Table is not an origin table
int response = ot->delete_route(net);
if (response != XORP_OK)
return response;
response = add_route(tablename, net, nexthop_addr, ifname, vifname,
metric, policytags);
// No need to flush here, as add_route will do it for us.
return response;
}
template <typename A>
int
RIB<A>::delete_route(const string& tablename, const IPNet<A>& net)
{
RouteTable<A>* rt = find_table(tablename);
if (NULL == rt)
return XORP_ERROR; // Table does not exist
OriginTable<A>* ot = dynamic_cast<OriginTable<A>* >(rt);
if (NULL == ot)
return XORP_ERROR; // Table is not an origin table
int result = ot->delete_route(net);
flush();
return result;
}
template <typename A>
void
RIB<A>::flush()
{
if (_register_table != NULL)
_register_table->flush();
if (_final_table != NULL && _final_table != _register_table)
_final_table->flush();
}
template <typename A>
int
RIB<A>::verify_route(const A& lookup_addr,
const string& ifname,
const A& nexthop_addr,
uint32_t metric,
RibVerifyType matchtype)
{
const IPRouteEntry<A>* re;
// 1. Check for an expected route miss.
// 2. Check table entry validity and existence.
re = _final_table->lookup_route(lookup_addr);
if (re == NULL || re->vif() == NULL) {
if (matchtype == RibVerifyType(MISS)) {
debug_msg("****ROUTE MISS SUCCESSFULLY VERIFIED****\n");
return XORP_OK;
}
if (re == NULL) {
debug_msg("RouteVerify: Route Lookup failed\n");
} else {
debug_msg("Route lookup returned NULL vif: %s\n",
re->str().c_str());
}
return XORP_ERROR;
}
#ifdef notyet
// 3. Check for discard (blackhole) routes.
// XXX: re->vif() must be non-NULL and valid. Revisit this in future.
DiscardNextHop* dnh = dynamic_cast<DiscardNextHop*>(re->nexthop());
if (matchtype == RibVerifyType(DISCARD)) {
if (dnh == NULL) {
debug_msg("Next hop is not a DiscardNextHop");
return XORP_ERROR;
} else {
debug_msg("****DISCARD ROUTE SUCCESSFULLY VERIFIED****\n");
return XORP_OK;
}
}
#endif
// 4. Check for protocol level routes (specifically IP).
IPNextHop<A>* route_nexthop = dynamic_cast<IPNextHop<A>* >(re->nexthop());
if (route_nexthop == NULL) {
debug_msg("Next hop is not an IPNextHop\n");
return XORP_ERROR;
} else if ((nexthop_addr != route_nexthop->addr())) {
debug_msg("NextHop: Exp: %s != Got: %s\n",
nexthop_addr.str().c_str(),
route_nexthop->addr().str().c_str());
return XORP_ERROR;
} else {
debug_msg("NextHop: Exp: %s != Got: %s\n",
nexthop_addr.str().c_str(),
route_nexthop->addr().str().c_str());
}
if (ifname != re->vif()->name()) {
XLOG_ERROR("Interface \"%s\" does not match expected \"%s\".",
re->vif()->str().c_str(), ifname.c_str());
return XORP_ERROR;
} else {
debug_msg("Ifname: Exp: %s == Got: %s\n",
ifname.c_str(),
re->vif()->name().c_str());
}
if (metric != re->metric()) {
XLOG_ERROR("Metric \"%u\" does not match expected \"%u\".",
XORP_UINT_CAST(re->metric()), XORP_UINT_CAST(metric));
return XORP_ERROR;
} else {
debug_msg("Metric: Exp: %u == Got: %u\n",
XORP_UINT_CAST(metric),
XORP_UINT_CAST(re->metric()));
}
debug_msg("****IP ROUTE SUCCESSFULLY VERIFIED****\n");
return XORP_OK;
}
template <typename A>
const A&
RIB<A>::lookup_route(const A& lookupaddr)
{
debug_msg("looking up %s\n", lookupaddr.str().c_str());
const IPRouteEntry<A>* re = _final_table->lookup_route(lookupaddr);
// Case 1: Route miss. Return the null IP address.
// Vif cannot be NULL for a valid route.
if (re == NULL || re->vif() == NULL)
return A::ZERO();
#ifdef notyet
DiscardNextHop* discard_nexthop =
dynamic_cast<DiscardNextHop* >(re->nexthop());
// Case 2: Discard route. Return the loopback address.
if (discard_nexthop != NULL)
return A::LOOPBACK();
IPNextHop<A>* ip_nexthop = dynamic_cast<IPNextHop<A>* >(re->nexthop());
// Case 3: IP protocol route. Return the nexthop address.
if (ip_nexthop != NULL)
return ip_nexthop->addr();
// Default: Return the null IP address.
return A::ZERO();
#else
// Default: Assume the route points to a resolved IPNextHop.
IPNextHop<A>* route_nexthop = static_cast<IPNextHop<A>* >(re->nexthop());
return route_nexthop->addr();
#endif
}
template <typename A>
RouteRange<A>*
RIB<A>::route_range_lookup(const A& lookupaddr)
{
return _final_table->lookup_route_range(lookupaddr);
}
template <typename A>
RouteRegister<A>*
RIB<A>::route_register(const A& lookupaddr, const string& module)
{
debug_msg("registering %s\n", lookupaddr.str().c_str());
return _register_table->register_route_range(lookupaddr, module);
}
template <typename A>
int
RIB<A>::route_deregister(const IPNet<A>& subnet, const string& module)
{
debug_msg("deregistering %s\n", subnet.str().c_str());
return _register_table->deregister_route_range(subnet, module);
}
template <typename A>
RedistTable<A>*
RIB<A>::protocol_redist_table(const string& protocol)
{
RouteTable<A>* rt = find_table(redist_tablename(protocol));
if (rt != NULL) {
return dynamic_cast<RedistTable<A>*>(rt);
}
return NULL;
}
template <typename A>
int
RIB<A>::add_igp_table(const string& tablename,
const string& target_class,
const string& target_instance)
{
debug_msg("add_igp_table %s\n", tablename.c_str());
int r = add_origin_table(tablename, target_class, target_instance, IGP);
if (r != XORP_OK)
return r;
// XXX For now we unconditionally plumb a RedistTable behind each
// OriginTable. We do this because the RedistTable needs to track
// the routes within the OriginTable in order to be able to render
// a dump when another protocol requests redistribution.
r = add_redist_table(tablename);
if (r != XORP_OK) {
delete_origin_table(tablename, target_class, target_instance);
return r;
}
RouteTable<A>* rt = find_table(redist_tablename(tablename));
XLOG_ASSERT(rt != NULL);
if (tablename == "connected") {
r = add_policy_connected_table(rt->tablename());
if (r != XORP_OK) {
delete_origin_table(tablename, target_class, target_instance);
//
// XXX: we don't delete the redist table but keep it around,
// because delete_origin_table() does similar.
//
return r;
}
}
return r;
}
template <typename A>
int
RIB<A>::add_egp_table(const string& tablename,
const string& target_class,
const string& target_instance)
{
debug_msg("add_egp_table %s\n", tablename.c_str());
int r = add_origin_table(tablename, target_class, target_instance, EGP);
if (r != XORP_OK) {
return r;
}
#if 0
// XXX For now we unconditionally plumb a RedistTable behind each
// OriginTable. We need this because the RedistTable needs to
// track the routes within the OriginTable in order to be able to
// render a dump when another protocol requests redistribution.
r = add_redist_table(tablename);
if (r != XORP_OK) {
delete_origin_table(tablename, target_class, target_instance);
return r;
}
RouteTable<A>* rt = find_table(redist_tablename(tablename));
XLOG_ASSERT(rt != NULL);
#endif
return r;
}
template <typename A>
int
RIB<A>::add_policy_connected_table(const string& parent_tablename)
{
RouteTable<A>* parent = find_table(parent_tablename);
if (parent == NULL) {
XLOG_WARNING("add_policy_connected_table: parent table %s does not exist",
parent_tablename.c_str());
return XORP_ERROR;
}
if (find_table(PolicyConnectedTable<A>::table_name) != NULL)
return XORP_OK;
PolicyConnectedTable<A>* pt =
new PolicyConnectedTable<A>(parent, _rib_manager.policy_filters());
if (add_table(pt) != XORP_OK) {
delete pt;
return XORP_ERROR;
}
return XORP_OK;
}
template <typename A>
int
RIB<A>::add_redist_table(const string& parent_tablename)
{
RouteTable<A>* p = find_table(parent_tablename);
if (p == NULL) {
XLOG_WARNING("add_redist_table: parent table %s does not exist",
parent_tablename.c_str());
return XORP_ERROR;
}
if (find_table(redist_tablename(parent_tablename)) != NULL) {
return XORP_OK; // RedistTable already exists, no sweat
}
RedistTable<A>* r;
r = new RedistTable<A>(redist_tablename(parent_tablename), p);
if (add_table(r) != XORP_OK) {
delete r;
return XORP_ERROR;
}
return XORP_OK;
}
//
// All the magic is in add_origin_table.
// TODO: XXX: split into smaller units (??)
template <typename A>
int
RIB<A>::add_origin_table(const string& tablename,
const string& target_class,
const string& target_instance,
ProtocolType protocol_type)
{
debug_msg("add_origin_table %s type: %dn\n",
tablename.c_str(), protocol_type);
Protocol* protocol = find_protocol(tablename);
if (protocol == NULL) {
protocol = new Protocol(tablename, protocol_type, 0);
_protocols[tablename] = protocol;
} else {
protocol->increment_genid();
}
// Check if table exists and check type if so
RouteTable<A>* rt = find_table(tablename);
if (rt != NULL) {
OriginTable<A>* ot = dynamic_cast<OriginTable<A>* >(rt);
if (ot == NULL) {
XLOG_ERROR("add_origin_table: table \"%s\" already exists, but is "
"not is an OriginTable.", tablename.c_str());
return XORP_ERROR;
} else {
//
// Table already exists, hence use it.
//
//
// Store the XRL target instance, so we know which OriginTable to
// shutdown if the routing protocol dies.
//
if (!target_instance.empty()) {
_rib_manager.register_interest_in_target(target_class);
_routing_protocol_instances[tablename + " "
+ target_class + " "
+ target_instance] = ot;
}
return XORP_OK;
}
}
if (new_origin_table(tablename, target_class, target_instance,
admin_distance(tablename), protocol_type)
!= XORP_OK) {
debug_msg("new_origin_table failed\n");
return XORP_ERROR;
}
OriginTable<A>* new_table = static_cast<OriginTable<A>* >(find_table(tablename));
if (_final_table == new_table) {
//
// This is the first table, so no need to plumb anything.
//
debug_msg("first table\n");
return XORP_OK;
}
//
// There are existing tables, so we need to do some plumbing
//
// Three possibilities:
// 1. there are existing EGP tables but no existing IGP table
// 2. there are both existing EGP and existing IGP tables
// 3. there are existing IGP tables but no existing EGP table
//
// If we're adding an IGP table:
// we can handle 2 and 3 the same, adding a MergedTable, but 1
// requires we construct an ExtInt table instead.
//
// If we're adding an EGP table:
// we can handle 1 and 2 the same, adding a MergedTable, but 3
// requires we construct an ExtInt table instead.
//
// First step: find out what tables already exist
RouteTable<A>* igp_table = NULL;
RouteTable<A>* egp_table = NULL;
ExtIntTable<A>* ei_table = NULL;
typename list<RouteTable<A>* >::iterator li;
for (li = _tables.begin(); li != _tables.end(); ++li) {
// XXX: skip the new table!
RouteTable<A>* current = *li;
if (current == new_table) {
continue;
}
OriginTable<A>* ot = dynamic_cast<OriginTable<A>* >(current);
if (ot != NULL) {
if (ot->protocol_type() == IGP) {
igp_table = ot;
} else if (ot->protocol_type() == EGP) {
egp_table = ot;
} else {
XLOG_UNREACHABLE();
}
continue;
} else {
if (ei_table == NULL)
ei_table = dynamic_cast<ExtIntTable<A>* >(current);
}
}
//
// Depending on which tables already exist, we may need to create
// a MergedTable or an ExtInt table.
//
if (((igp_table == NULL) && (protocol_type == IGP)) ||
((egp_table == NULL) && (protocol_type == EGP))) {
//
// Sanity check: we've found an ExtIntTable, when there
// weren't both IGP and EGP tables.
//
XLOG_ASSERT(ei_table == NULL);
if ((egp_table == NULL) && (igp_table == NULL)) {
//
// There are tables, but neither IGP or EGP origin tables
// Therefore the final table must be a RedistTable
// or a RegisterTable.
//
if ((_final_table->type() != REDIST_TABLE)
&& (_final_table->type() != POLICY_REDIST_TABLE)
&& (_final_table->type() != REGISTER_TABLE)) {
XLOG_UNREACHABLE();
}
//
// There may be existing single-parent tables before the
// final table such as RegisterTable - track back to be
// first of them.
//
RouteTable<A>* rt = track_back(_final_table,
REDIST_TABLE
| POLICY_REDIST_TABLE
| REGISTER_TABLE);
//
// Plumb our new table in ahead of the first single-parent
// table.
//
rt->replumb(NULL, new_table);
new_table->set_next_table(rt);
return XORP_OK;
}
//
// We are going to need to create an ExtInt table.
//
// Find the appropriate existng table to be a parent
// of the ExtIntTable.
//
RouteTable<A>* next_table = track_back(_final_table,
REDIST_TABLE
| POLICY_REDIST_TABLE
| REGISTER_TABLE);
RouteTable<A>* existing_table = next_table->parent();
if (protocol_type == IGP) {
ei_table = new ExtIntTable<A>(existing_table, new_table);
} else {
ei_table = new ExtIntTable<A>(new_table, existing_table);
}
// XXX: Added table to list of resources (was not done previously)
if (add_table(ei_table) != XORP_OK) {
XLOG_ERROR("Failed to add ExtIntTable \"%s\".",
ei_table->tablename().c_str());
delete ei_table;
return XORP_ERROR;
}
if (_final_table->type() & (REDIST_TABLE | POLICY_REDIST_TABLE |
REGISTER_TABLE)) {
ei_table->set_next_table(next_table);
next_table->replumb(existing_table, ei_table);
} else {
_final_table = ei_table;
}
return XORP_OK;
}
//
// We're going to need to create a MergedTable
//
RouteTable<A>* existing_table = (protocol_type == IGP) ? igp_table : egp_table;
RouteTable<A>* next_table = existing_table->next_table();
// Skip past any RedistTables
RouteTable<A>* new_prev_table = track_forward(existing_table,
(REDIST_TABLE |
POLICY_CONNECTED_TABLE));
if (new_prev_table != existing_table) {
existing_table = new_prev_table;
next_table = existing_table->next_table();
}
MergedTable<A>* merged_table = new MergedTable<A>(existing_table,
new_table);
if (merged_table == NULL || add_table(merged_table) != XORP_OK) {
delete merged_table;
return XORP_ERROR;
}
merged_table->set_next_table(next_table);
if (next_table != NULL)
next_table->replumb(existing_table, merged_table);
//
// It's possible existing_table was the last table - if so, then it
// isn't anymore.
//
if (_final_table == existing_table)
_final_table = merged_table;
return XORP_OK;
}
template <typename A>
int
RIB<A>::delete_igp_table(const string& tablename,
const string& target_class,
const string& target_instance)
{
return delete_origin_table(tablename, target_class, target_instance);
}
template <typename A>
int
RIB<A>::delete_egp_table(const string& tablename,
const string& target_class,
const string& target_instance)
{
return delete_origin_table(tablename, target_class, target_instance);
}
template <class A>
int
RIB<A>::delete_origin_table(const string& tablename,
const string& target_class,
const string& target_instance)
{
OriginTable<A>* ot = dynamic_cast<OriginTable<A>* >(find_table(tablename));
if (NULL == ot)
return XORP_ERROR;
if (!target_instance.empty()) {
if (find_table_by_instance(tablename, target_class, target_instance)
!= ot) {
XLOG_ERROR("Got delete_origin_table for wrong target name\n");
return XORP_ERROR;
} else {
_routing_protocol_instances.erase(tablename + " "
+ target_class + " "
+ target_instance);
}
}
// Remove all the routes this table used to originate, but keep table
ot->routing_protocol_shutdown();
return XORP_OK;
}
template <class A>
void
RIB<A>::target_death(const string& target_class,
const string& target_instance)
{
string s = " " + target_class + " " + target_instance;
typename map<string, OriginTable<A>* >::iterator iter;
for (iter = _routing_protocol_instances.begin();
iter != _routing_protocol_instances.end();
++iter) {
if (iter->first.find(s) != string::npos) {
// We've found the target.
XLOG_INFO("Received death event for protocol %s shutting down %s",
target_class.c_str(), iter->second->str().c_str());
iter->second->routing_protocol_shutdown();
_routing_protocol_instances.erase(iter);
// No need to go any further.
return;
}
}
}
//
// Given a single-parent table, track back to the last matching table
// before this one.
//
template <typename A>
RouteTable<A>*
RIB<A>::track_back(RouteTable<A>* rt, int typemask) const
{
if (rt == NULL || (rt->type() & typemask) == 0) {
return rt;
}
for (RouteTable<A>* parent = rt->parent();
parent && (parent->type() & typemask);
parent = rt->parent()) {
rt = parent;
}
return rt;
}
//
// Track forward to the last matching table, or return this table if
// the next table doesn't match.
//
template <typename A>
RouteTable<A>*
RIB<A>::track_forward(RouteTable<A>* rt, int typemask) const
{
debug_msg("here1\n");
RouteTable<A>* next;
if (NULL == rt) {
// XXX: not the same test as track back (deliberate ?)
return rt;
}
next = rt->next_table();
debug_msg("here2\n");
while (next != NULL) {
debug_msg("here3\n");
if ((next->type() & typemask) != 0) {
debug_msg("here4 next->type()= %d, typemask=%x\n",
next->type(), typemask);
rt = next;
next = rt->next_table();
} else {
debug_msg("here5\n");
return rt;
}
}
debug_msg("here6\n");
return rt;
}
template <typename A>
void
RIB<A>::print_rib() const
{
#ifdef DEBUG_LOGGING
typename list<RouteTable<A>* >::const_iterator pair;
pair = _tables.begin();
// XXX: this is printf not debug_msg for a reason.
printf("==============================================================\n");
while (pair != _tables.end()) {
RouteTable<A>* rt = *pair;
printf("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n");
printf(rt->str().c_str());
rt = rt->next_table();
while (rt != NULL) {
printf(rt->str().c_str());
rt = rt->next_table();
}
++pair;
}
printf("==============================================================\n");
#endif // DEBUG_LOGGING
}
template <typename A>
string
RIB<A>::name() const
{
return c_format("%s %s RIB",
(_multicast) ? "Multicast" : "Unicast",
A::ip_version_str().c_str());
}
template <typename A>
void
RIB<A>::push_routes()
{
RouteTable<A>* rt = find_table(PolicyConnectedTable<A>::table_name);
XLOG_ASSERT(rt != NULL);
PolicyConnectedTable<A>* pct = dynamic_cast<PolicyConnectedTable<A>*>(rt);
XLOG_ASSERT(pct != NULL);
pct->push_routes();
}
template class RIB<IPv4>;
template class RIB<IPv6>;
syntax highlighted by Code2HTML, v. 0.9.1