blob: 154e8d5518ceab81244f0019a1b076f9e2ea22ce [file] [log] [blame]
/* Routing Information Base.
* Copyright (C) 1997, 98, 99, 2001 Kunihiro Ishiguro
*
* This file is part of GNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* GNU Zebra 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. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU Zebra; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <zebra.h>
#include "prefix.h"
#include "table.h"
#include "memory.h"
#include "str.h"
#include "command.h"
#include "if.h"
#include "log.h"
#include "sockunion.h"
#include "linklist.h"
#include "thread.h"
#include "workqueue.h"
#include "prefix.h"
#include "routemap.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
#include "zebra/zserv.h"
#include "zebra/redistribute.h"
#include "zebra/debug.h"
/* Default rtm_table for all clients */
extern struct zebra_t zebrad;
/* Hold time for RIB process, should be very minimal.
* it is useful to able to set it otherwise for testing, hence exported
* as global here for test-rig code.
*/
int rib_process_hold_time = 10;
/* Each route type's string and default distance value. */
static const struct
{
int key;
int distance;
} route_info[ZEBRA_ROUTE_MAX] =
{
[ZEBRA_ROUTE_SYSTEM] = {ZEBRA_ROUTE_SYSTEM, 0},
[ZEBRA_ROUTE_KERNEL] = {ZEBRA_ROUTE_KERNEL, 0},
[ZEBRA_ROUTE_CONNECT] = {ZEBRA_ROUTE_CONNECT, 0},
[ZEBRA_ROUTE_STATIC] = {ZEBRA_ROUTE_STATIC, 1},
[ZEBRA_ROUTE_RIP] = {ZEBRA_ROUTE_RIP, 120},
[ZEBRA_ROUTE_RIPNG] = {ZEBRA_ROUTE_RIPNG, 120},
[ZEBRA_ROUTE_OSPF] = {ZEBRA_ROUTE_OSPF, 110},
[ZEBRA_ROUTE_OSPF6] = {ZEBRA_ROUTE_OSPF6, 110},
[ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115},
[ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */},
[ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95},
/* no entry/default: 150 */
};
/* Vector for routing table. */
static vector vrf_vector;
/* Allocate new VRF. */
static struct vrf *
vrf_alloc (const char *name)
{
struct vrf *vrf;
vrf = XCALLOC (MTYPE_VRF, sizeof (struct vrf));
/* Put name. */
if (name)
vrf->name = XSTRDUP (MTYPE_VRF_NAME, name);
/* Allocate routing table and static table. */
vrf->table[AFI_IP][SAFI_UNICAST] = route_table_init ();
vrf->table[AFI_IP6][SAFI_UNICAST] = route_table_init ();
vrf->stable[AFI_IP][SAFI_UNICAST] = route_table_init ();
vrf->stable[AFI_IP6][SAFI_UNICAST] = route_table_init ();
vrf->table[AFI_IP][SAFI_MULTICAST] = route_table_init ();
vrf->table[AFI_IP6][SAFI_MULTICAST] = route_table_init ();
vrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init ();
vrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init ();
return vrf;
}
/* Lookup VRF by identifier. */
struct vrf *
vrf_lookup (u_int32_t id)
{
return vector_lookup (vrf_vector, id);
}
/* Initialize VRF. */
static void
vrf_init (void)
{
struct vrf *default_table;
/* Allocate VRF vector. */
vrf_vector = vector_init (1);
/* Allocate default main table. */
default_table = vrf_alloc ("Default-IP-Routing-Table");
/* Default table index must be 0. */
vector_set_index (vrf_vector, 0, default_table);
}
/* Lookup route table. */
struct route_table *
vrf_table (afi_t afi, safi_t safi, u_int32_t id)
{
struct vrf *vrf;
vrf = vrf_lookup (id);
if (! vrf)
return NULL;
return vrf->table[afi][safi];
}
/* Lookup static route table. */
struct route_table *
vrf_static_table (afi_t afi, safi_t safi, u_int32_t id)
{
struct vrf *vrf;
vrf = vrf_lookup (id);
if (! vrf)
return NULL;
return vrf->stable[afi][safi];
}
/* Add nexthop to the end of the list. */
static void
nexthop_add (struct rib *rib, struct nexthop *nexthop)
{
struct nexthop *last;
for (last = rib->nexthop; last && last->next; last = last->next)
;
if (last)
last->next = nexthop;
else
rib->nexthop = nexthop;
nexthop->prev = last;
rib->nexthop_num++;
}
/* Delete specified nexthop from the list. */
static void
nexthop_delete (struct rib *rib, struct nexthop *nexthop)
{
if (nexthop->next)
nexthop->next->prev = nexthop->prev;
if (nexthop->prev)
nexthop->prev->next = nexthop->next;
else
rib->nexthop = nexthop->next;
rib->nexthop_num--;
}
/* Free nexthop. */
static void
nexthop_free (struct nexthop *nexthop)
{
if (nexthop->ifname)
XFREE (0, nexthop->ifname);
XFREE (MTYPE_NEXTHOP, nexthop);
}
struct nexthop *
nexthop_ifindex_add (struct rib *rib, unsigned int ifindex)
{
struct nexthop *nexthop;
nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IFINDEX;
nexthop->ifindex = ifindex;
nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
nexthop_ifname_add (struct rib *rib, char *ifname)
{
struct nexthop *nexthop;
nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IFNAME;
nexthop->ifname = XSTRDUP (0, ifname);
nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src)
{
struct nexthop *nexthop;
nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV4;
nexthop->gate.ipv4 = *ipv4;
if (src)
nexthop->src.ipv4 = *src;
nexthop_add (rib, nexthop);
return nexthop;
}
struct nexthop *
nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4,
struct in_addr *src, unsigned int ifindex)
{
struct nexthop *nexthop;
nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
nexthop->gate.ipv4 = *ipv4;
if (src)
nexthop->src.ipv4 = *src;
nexthop->ifindex = ifindex;
nexthop_add (rib, nexthop);
return nexthop;
}
#ifdef HAVE_IPV6
struct nexthop *
nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6)
{
struct nexthop *nexthop;
nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV6;
nexthop->gate.ipv6 = *ipv6;
nexthop_add (rib, nexthop);
return nexthop;
}
static struct nexthop *
nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
char *ifname)
{
struct nexthop *nexthop;
nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME;
nexthop->gate.ipv6 = *ipv6;
nexthop->ifname = XSTRDUP (0, ifname);
nexthop_add (rib, nexthop);
return nexthop;
}
static struct nexthop *
nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6,
unsigned int ifindex)
{
struct nexthop *nexthop;
nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
nexthop->gate.ipv6 = *ipv6;
nexthop->ifindex = ifindex;
nexthop_add (rib, nexthop);
return nexthop;
}
#endif /* HAVE_IPV6 */
struct nexthop *
nexthop_blackhole_add (struct rib *rib)
{
struct nexthop *nexthop;
nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
SET_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE);
nexthop_add (rib, nexthop);
return nexthop;
}
/* If force flag is not set, do not modify falgs at all for uninstall
the route from FIB. */
static int
nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set,
struct route_node *top)
{
struct prefix_ipv4 p;
struct route_table *table;
struct route_node *rn;
struct rib *match;
struct nexthop *newhop;
if (nexthop->type == NEXTHOP_TYPE_IPV4)
nexthop->ifindex = 0;
if (set)
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
/* Make lookup prefix. */
memset (&p, 0, sizeof (struct prefix_ipv4));
p.family = AF_INET;
p.prefixlen = IPV4_MAX_PREFIXLEN;
p.prefix = nexthop->gate.ipv4;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
return 0;
rn = route_node_match (table, (struct prefix *) &p);
while (rn)
{
route_unlock_node (rn);
/* If lookup self prefix return immediately. */
if (rn == top)
return 0;
/* Pick up selected route. */
for (match = rn->info; match; match = match->next)
{
if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
continue;
if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
break;
}
/* If there is no selected route or matched route is EGP, go up
tree. */
if (! match
|| match->type == ZEBRA_ROUTE_BGP)
{
do {
rn = rn->parent;
} while (rn && rn->info == NULL);
if (rn)
route_lock_node (rn);
}
else
{
if (match->type == ZEBRA_ROUTE_CONNECT)
{
/* Directly point connected route. */
newhop = match->nexthop;
if (newhop && nexthop->type == NEXTHOP_TYPE_IPV4)
nexthop->ifindex = newhop->ifindex;
return 1;
}
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
{
for (newhop = match->nexthop; newhop; newhop = newhop->next)
if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
&& ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE))
{
if (set)
{
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
nexthop->rtype = newhop->type;
if (newhop->type == NEXTHOP_TYPE_IPV4 ||
newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
nexthop->rgate.ipv4 = newhop->gate.ipv4;
if (newhop->type == NEXTHOP_TYPE_IFINDEX
|| newhop->type == NEXTHOP_TYPE_IFNAME
|| newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
nexthop->rifindex = newhop->ifindex;
}
return 1;
}
return 0;
}
else
{
return 0;
}
}
}
return 0;
}
#ifdef HAVE_IPV6
/* If force flag is not set, do not modify falgs at all for uninstall
the route from FIB. */
static int
nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set,
struct route_node *top)
{
struct prefix_ipv6 p;
struct route_table *table;
struct route_node *rn;
struct rib *match;
struct nexthop *newhop;
if (nexthop->type == NEXTHOP_TYPE_IPV6)
nexthop->ifindex = 0;
if (set)
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
/* Make lookup prefix. */
memset (&p, 0, sizeof (struct prefix_ipv6));
p.family = AF_INET6;
p.prefixlen = IPV6_MAX_PREFIXLEN;
p.prefix = nexthop->gate.ipv6;
/* Lookup table. */
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
if (! table)
return 0;
rn = route_node_match (table, (struct prefix *) &p);
while (rn)
{
route_unlock_node (rn);
/* If lookup self prefix return immediately. */
if (rn == top)
return 0;
/* Pick up selected route. */
for (match = rn->info; match; match = match->next)
{
if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
continue;
if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
break;
}
/* If there is no selected route or matched route is EGP, go up
tree. */
if (! match
|| match->type == ZEBRA_ROUTE_BGP)
{
do {
rn = rn->parent;
} while (rn && rn->info == NULL);
if (rn)
route_lock_node (rn);
}
else
{
if (match->type == ZEBRA_ROUTE_CONNECT)
{
/* Directly point connected route. */
newhop = match->nexthop;
if (newhop && nexthop->type == NEXTHOP_TYPE_IPV6)
nexthop->ifindex = newhop->ifindex;
return 1;
}
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
{
for (newhop = match->nexthop; newhop; newhop = newhop->next)
if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
&& ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE))
{
if (set)
{
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
nexthop->rtype = newhop->type;
if (newhop->type == NEXTHOP_TYPE_IPV6
|| newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
|| newhop->type == NEXTHOP_TYPE_IPV6_IFNAME)
nexthop->rgate.ipv6 = newhop->gate.ipv6;
if (newhop->type == NEXTHOP_TYPE_IFINDEX
|| newhop->type == NEXTHOP_TYPE_IFNAME
|| newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
|| newhop->type == NEXTHOP_TYPE_IPV6_IFNAME)
nexthop->rifindex = newhop->ifindex;
}
return 1;
}
return 0;
}
else
{
return 0;
}
}
}
return 0;
}
#endif /* HAVE_IPV6 */
struct rib *
rib_match_ipv4 (struct in_addr addr)
{
struct prefix_ipv4 p;
struct route_table *table;
struct route_node *rn;
struct rib *match;
struct nexthop *newhop;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
return 0;
memset (&p, 0, sizeof (struct prefix_ipv4));
p.family = AF_INET;
p.prefixlen = IPV4_MAX_PREFIXLEN;
p.prefix = addr;
rn = route_node_match (table, (struct prefix *) &p);
while (rn)
{
route_unlock_node (rn);
/* Pick up selected route. */
for (match = rn->info; match; match = match->next)
{
if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
continue;
if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
break;
}
/* If there is no selected route or matched route is EGP, go up
tree. */
if (! match
|| match->type == ZEBRA_ROUTE_BGP)
{
do {
rn = rn->parent;
} while (rn && rn->info == NULL);
if (rn)
route_lock_node (rn);
}
else
{
if (match->type == ZEBRA_ROUTE_CONNECT)
/* Directly point connected route. */
return match;
else
{
for (newhop = match->nexthop; newhop; newhop = newhop->next)
if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
return match;
return NULL;
}
}
}
return NULL;
}
struct rib *
rib_lookup_ipv4 (struct prefix_ipv4 *p)
{
struct route_table *table;
struct route_node *rn;
struct rib *match;
struct nexthop *nexthop;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
return 0;
rn = route_node_lookup (table, (struct prefix *) p);
/* No route for this prefix. */
if (! rn)
return NULL;
/* Unlock node. */
route_unlock_node (rn);
for (match = rn->info; match; match = match->next)
{
if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
continue;
if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
break;
}
if (! match || match->type == ZEBRA_ROUTE_BGP)
return NULL;
if (match->type == ZEBRA_ROUTE_CONNECT)
return match;
for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next)
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
return match;
return NULL;
}
/*
* This clone function, unlike its original rib_lookup_ipv4(), checks
* if specified IPv4 route record (prefix/mask -> gate) exists in
* the whole RIB and has ZEBRA_FLAG_SELECTED set.
*
* Return values:
* -1: error
* 0: exact match found
* 1: a match was found with a different gate
* 2: connected route found
* 3: no matches found
*/
int
rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate)
{
struct route_table *table;
struct route_node *rn;
struct rib *match;
struct nexthop *nexthop;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
return ZEBRA_RIB_LOOKUP_ERROR;
/* Scan the RIB table for exactly matching RIB entry. */
rn = route_node_lookup (table, (struct prefix *) p);
/* No route for this prefix. */
if (! rn)
return ZEBRA_RIB_NOTFOUND;
/* Unlock node. */
route_unlock_node (rn);
/* Find out if a "selected" RR for the discovered RIB entry exists ever. */
for (match = rn->info; match; match = match->next)
{
if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
continue;
if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
break;
}
/* None such found :( */
if (!match)
return ZEBRA_RIB_NOTFOUND;
if (match->type == ZEBRA_ROUTE_CONNECT)
return ZEBRA_RIB_FOUND_CONNECTED;
/* Ok, we have a cood candidate, let's check it's nexthop list... */
for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next)
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
{
/* We are happy with either direct or recursive hexthop */
if (nexthop->gate.ipv4.s_addr == qgate->sin.sin_addr.s_addr ||
nexthop->rgate.ipv4.s_addr == qgate->sin.sin_addr.s_addr)
return ZEBRA_RIB_FOUND_EXACT;
else
{
if (IS_ZEBRA_DEBUG_RIB)
{
char gate_buf[INET_ADDRSTRLEN], rgate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN];
inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN);
inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, rgate_buf, INET_ADDRSTRLEN);
inet_ntop (AF_INET, &qgate->sin.sin_addr.s_addr, qgate_buf, INET_ADDRSTRLEN);
zlog_debug ("%s: qgate == %s, gate == %s, rgate == %s", __func__, qgate_buf, gate_buf, rgate_buf);
}
return ZEBRA_RIB_FOUND_NOGATE;
}
}
return ZEBRA_RIB_NOTFOUND;
}
#ifdef HAVE_IPV6
struct rib *
rib_match_ipv6 (struct in6_addr *addr)
{
struct prefix_ipv6 p;
struct route_table *table;
struct route_node *rn;
struct rib *match;
struct nexthop *newhop;
/* Lookup table. */
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
if (! table)
return 0;
memset (&p, 0, sizeof (struct prefix_ipv6));
p.family = AF_INET6;
p.prefixlen = IPV6_MAX_PREFIXLEN;
IPV6_ADDR_COPY (&p.prefix, addr);
rn = route_node_match (table, (struct prefix *) &p);
while (rn)
{
route_unlock_node (rn);
/* Pick up selected route. */
for (match = rn->info; match; match = match->next)
{
if (CHECK_FLAG (match->status, RIB_ENTRY_REMOVED))
continue;
if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
break;
}
/* If there is no selected route or matched route is EGP, go up
tree. */
if (! match
|| match->type == ZEBRA_ROUTE_BGP)
{
do {
rn = rn->parent;
} while (rn && rn->info == NULL);
if (rn)
route_lock_node (rn);
}
else
{
if (match->type == ZEBRA_ROUTE_CONNECT)
/* Directly point connected route. */
return match;
else
{
for (newhop = match->nexthop; newhop; newhop = newhop->next)
if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
return match;
return NULL;
}
}
}
return NULL;
}
#endif /* HAVE_IPV6 */
#define RIB_SYSTEM_ROUTE(R) \
((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT)
/* This function verifies reachability of one given nexthop, which can be
* numbered or unnumbered, IPv4 or IPv6. The result is unconditionally stored
* in nexthop->flags field. If the 4th parameter, 'set', is non-zero,
* nexthop->ifindex will be updated appropriately as well.
* An existing route map can turn (otherwise active) nexthop into inactive, but
* not vice versa.
*
* The return value is the final value of 'ACTIVE' flag.
*/
static unsigned
nexthop_active_check (struct route_node *rn, struct rib *rib,
struct nexthop *nexthop, int set)
{
struct interface *ifp;
route_map_result_t ret = RMAP_MATCH;
extern char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1];
struct route_map *rmap;
int family;
family = 0;
switch (nexthop->type)
{
case NEXTHOP_TYPE_IFINDEX:
ifp = if_lookup_by_index (nexthop->ifindex);
if (ifp && if_is_operative(ifp))
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
break;
case NEXTHOP_TYPE_IPV6_IFNAME:
family = AFI_IP6;
case NEXTHOP_TYPE_IFNAME:
ifp = if_lookup_by_name (nexthop->ifname);
if (ifp && if_is_operative(ifp))
{
if (set)
nexthop->ifindex = ifp->ifindex;
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
}
else
{
if (set)
nexthop->ifindex = 0;
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
}
break;
case NEXTHOP_TYPE_IPV4:
case NEXTHOP_TYPE_IPV4_IFINDEX:
family = AFI_IP;
if (nexthop_active_ipv4 (rib, nexthop, set, rn))
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
break;
#ifdef HAVE_IPV6
case NEXTHOP_TYPE_IPV6:
family = AFI_IP6;
if (nexthop_active_ipv6 (rib, nexthop, set, rn))
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
break;
case NEXTHOP_TYPE_IPV6_IFINDEX:
family = AFI_IP6;
if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6))
{
ifp = if_lookup_by_index (nexthop->ifindex);
if (ifp && if_is_operative(ifp))
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
}
else
{
if (nexthop_active_ipv6 (rib, nexthop, set, rn))
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
}
break;
#endif /* HAVE_IPV6 */
case NEXTHOP_TYPE_BLACKHOLE:
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
break;
default:
break;
}
if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
return 0;
if (RIB_SYSTEM_ROUTE(rib) ||
(family == AFI_IP && rn->p.family != AF_INET) ||
(family == AFI_IP6 && rn->p.family != AF_INET6))
return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
rmap = 0;
if (rib->type >= 0 && rib->type < ZEBRA_ROUTE_MAX &&
proto_rm[family][rib->type])
rmap = route_map_lookup_by_name (proto_rm[family][rib->type]);
if (!rmap && proto_rm[family][ZEBRA_ROUTE_MAX])
rmap = route_map_lookup_by_name (proto_rm[family][ZEBRA_ROUTE_MAX]);
if (rmap) {
ret = route_map_apply(rmap, &rn->p, RMAP_ZEBRA, nexthop);
}
if (ret == RMAP_DENYMATCH)
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
}
/* Iterate over all nexthops of the given RIB entry and refresh their
* ACTIVE flag. rib->nexthop_active_num is updated accordingly. If any
* nexthop is found to toggle the ACTIVE flag, the whole rib structure
* is flagged with ZEBRA_FLAG_CHANGED. The 4th 'set' argument is
* transparently passed to nexthop_active_check().
*
* Return value is the new number of active nexthops.
*/
static int
nexthop_active_update (struct route_node *rn, struct rib *rib, int set)
{
struct nexthop *nexthop;
unsigned int prev_active, prev_index, new_active;
rib->nexthop_active_num = 0;
UNSET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
{
prev_active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
prev_index = nexthop->ifindex;
if ((new_active = nexthop_active_check (rn, rib, nexthop, set)))
rib->nexthop_active_num++;
if (prev_active != new_active ||
prev_index != nexthop->ifindex)
SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
}
return rib->nexthop_active_num;
}
static void
rib_install_kernel (struct route_node *rn, struct rib *rib)
{
int ret = 0;
struct nexthop *nexthop;
switch (PREFIX_FAMILY (&rn->p))
{
case AF_INET:
ret = kernel_add_ipv4 (&rn->p, rib);
break;
#ifdef HAVE_IPV6
case AF_INET6:
ret = kernel_add_ipv6 (&rn->p, rib);
break;
#endif /* HAVE_IPV6 */
}
/* This condition is never met, if we are using rt_socket.c */
if (ret < 0)
{
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
}
}
/* Uninstall the route from kernel. */
static int
rib_uninstall_kernel (struct route_node *rn, struct rib *rib)
{
int ret = 0;
struct nexthop *nexthop;
switch (PREFIX_FAMILY (&rn->p))
{
case AF_INET:
ret = kernel_delete_ipv4 (&rn->p, rib);
break;
#ifdef HAVE_IPV6
case AF_INET6:
ret = kernel_delete_ipv6 (&rn->p, rib);
break;
#endif /* HAVE_IPV6 */
}
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
return ret;
}
/* Uninstall the route from kernel. */
static void
rib_uninstall (struct route_node *rn, struct rib *rib)
{
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
{
redistribute_delete (&rn->p, rib);
if (! RIB_SYSTEM_ROUTE (rib))
rib_uninstall_kernel (rn, rib);
UNSET_FLAG (rib->flags, ZEBRA_FLAG_SELECTED);
}
}
static void rib_unlink (struct route_node *, struct rib *);
/* Core function for processing routing information base. */
static void
rib_process (struct route_node *rn)
{
struct rib *rib;
struct rib *next;
struct rib *fib = NULL;
struct rib *select = NULL;
struct rib *del = NULL;
int installed = 0;
struct nexthop *nexthop = NULL;
char buf[INET6_ADDRSTRLEN];
assert (rn);
if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q)
inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
for (rib = rn->info; rib; rib = next)
{
/* The next pointer is saved, because current pointer
* may be passed to rib_unlink() in the middle of iteration.
*/
next = rib->next;
/* Currently installed rib. */
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
{
assert (fib == NULL);
fib = rib;
}
/* Unlock removed routes, so they'll be freed, bar the FIB entry,
* which we need to do do further work with below.
*/
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
{
if (rib != fib)
{
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: %s/%d: rn %p, removing rib %p", __func__,
buf, rn->p.prefixlen, rn, rib);
rib_unlink (rn, rib);
}
else
del = rib;
continue;
}
/* Skip unreachable nexthop. */
if (! nexthop_active_update (rn, rib, 0))
continue;
/* Infinit distance. */
if (rib->distance == DISTANCE_INFINITY)
continue;
/* Newly selected rib, the common case. */
if (!select)
{
select = rib;
continue;
}
/* filter route selection in following order:
* - connected beats other types
* - lower distance beats higher
* - lower metric beats higher for equal distance
* - last, hence oldest, route wins tie break.
*/
/* Connected routes. Pick the last connected
* route of the set of lowest metric connected routes.
*/
if (rib->type == ZEBRA_ROUTE_CONNECT)
{
if (select->type != ZEBRA_ROUTE_CONNECT
|| rib->metric <= select->metric)
select = rib;
continue;
}
else if (select->type == ZEBRA_ROUTE_CONNECT)
continue;
/* higher distance loses */
if (rib->distance > select->distance)
continue;
/* lower wins */
if (rib->distance < select->distance)
{
select = rib;
continue;
}
/* metric tie-breaks equal distance */
if (rib->metric <= select->metric)
select = rib;
} /* for (rib = rn->info; rib; rib = next) */
/* After the cycle is finished, the following pointers will be set:
* select --- the winner RIB entry, if any was found, otherwise NULL
* fib --- the SELECTED RIB entry, if any, otherwise NULL
* del --- equal to fib, if fib is queued for deletion, NULL otherwise
* rib --- NULL
*/
/* Same RIB entry is selected. Update FIB and finish. */
if (select && select == fib)
{
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: %s/%d: Updating existing route, select %p, fib %p",
__func__, buf, rn->p.prefixlen, select, fib);
if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED))
{
redistribute_delete (&rn->p, select);
if (! RIB_SYSTEM_ROUTE (select))
rib_uninstall_kernel (rn, select);
/* Set real nexthop. */
nexthop_active_update (rn, select, 1);
if (! RIB_SYSTEM_ROUTE (select))
rib_install_kernel (rn, select);
redistribute_add (&rn->p, select);
}
else if (! RIB_SYSTEM_ROUTE (select))
{
/* Housekeeping code to deal with
race conditions in kernel with linux
netlink reporting interface up before IPv4 or IPv6 protocol
is ready to add routes.
This makes sure the routes are IN the kernel.
*/
for (nexthop = select->nexthop; nexthop; nexthop = nexthop->next)
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
{
installed = 1;
break;
}
if (! installed)
rib_install_kernel (rn, select);
}
goto end;
}
/* At this point we either haven't found the best RIB entry or it is
* different from what we currently intend to flag with SELECTED. In both
* cases, if a RIB block is present in FIB, it should be withdrawn.
*/
if (fib)
{
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: %s/%d: Removing existing route, fib %p", __func__,
buf, rn->p.prefixlen, fib);
redistribute_delete (&rn->p, fib);
if (! RIB_SYSTEM_ROUTE (fib))
rib_uninstall_kernel (rn, fib);
UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED);
/* Set real nexthop. */
nexthop_active_update (rn, fib, 1);
}
/* Regardless of some RIB entry being SELECTED or not before, now we can
* tell, that if a new winner exists, FIB is still not updated with this
* data, but ready to be.
*/
if (select)
{
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: %s/%d: Adding route, select %p", __func__, buf,
rn->p.prefixlen, select);
/* Set real nexthop. */
nexthop_active_update (rn, select, 1);
if (! RIB_SYSTEM_ROUTE (select))
rib_install_kernel (rn, select);
SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED);
redistribute_add (&rn->p, select);
}
/* FIB route was removed, should be deleted */
if (del)
{
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: %s/%d: Deleting fib %p, rn %p", __func__, buf,
rn->p.prefixlen, del, rn);
rib_unlink (rn, del);
}
end:
if (IS_ZEBRA_DEBUG_RIB_Q)
zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn);
}
/* Take a list of route_node structs and return 1, if there was a record
* picked from it and processed by rib_process(). Don't process more,
* than one RN record; operate only in the specified sub-queue.
*/
static unsigned int
process_subq (struct list * subq, u_char qindex)
{
struct listnode *lnode = listhead (subq);
struct route_node *rnode;
if (!lnode)
return 0;
rnode = listgetdata (lnode);
rib_process (rnode);
if (rnode->info) /* The first RIB record is holding the flags bitmask. */
UNSET_FLAG (((struct rib *)rnode->info)->rn_status, RIB_ROUTE_QUEUED(qindex));
#if 0
else
{
zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
__func__, rnode, rnode->lock);
zlog_backtrace(LOG_DEBUG);
}
#endif
route_unlock_node (rnode);
list_delete_node (subq, lnode);
return 1;
}
/* Dispatch the meta queue by picking, processing and unlocking the next RN from
* a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and data
* is pointed to the meta queue structure.
*/
static wq_item_status
meta_queue_process (struct work_queue *dummy, void *data)
{
struct meta_queue * mq = data;
unsigned i;
for (i = 0; i < MQ_SIZE; i++)
if (process_subq (mq->subq[i], i))
{
mq->size--;
break;
}
return mq->size ? WQ_REQUEUE : WQ_SUCCESS;
}
/* Map from rib types to queue type (priority) in meta queue */
static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = {
[ZEBRA_ROUTE_SYSTEM] = 4,
[ZEBRA_ROUTE_KERNEL] = 0,
[ZEBRA_ROUTE_CONNECT] = 0,
[ZEBRA_ROUTE_STATIC] = 1,
[ZEBRA_ROUTE_RIP] = 2,
[ZEBRA_ROUTE_RIPNG] = 2,
[ZEBRA_ROUTE_OSPF] = 2,
[ZEBRA_ROUTE_OSPF6] = 2,
[ZEBRA_ROUTE_ISIS] = 2,
[ZEBRA_ROUTE_BGP] = 3,
[ZEBRA_ROUTE_HSLS] = 4,
[ZEBRA_ROUTE_BABEL] = 2,
};
/* Look into the RN and queue it into one or more priority queues,
* increasing the size for each data push done.
*/
static void
rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn)
{
struct rib *rib;
char buf[INET6_ADDRSTRLEN];
if (IS_ZEBRA_DEBUG_RIB_Q)
inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
for (rib = rn->info; rib; rib = rib->next)
{
u_char qindex = meta_queue_map[rib->type];
/* Invariant: at this point we always have rn->info set. */
if (CHECK_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex)))
{
if (IS_ZEBRA_DEBUG_RIB_Q)
zlog_debug ("%s: %s/%d: rn %p is already queued in sub-queue %u",
__func__, buf, rn->p.prefixlen, rn, qindex);
continue;
}
SET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED(qindex));
listnode_add (mq->subq[qindex], rn);
route_lock_node (rn);
mq->size++;
if (IS_ZEBRA_DEBUG_RIB_Q)
zlog_debug ("%s: %s/%d: queued rn %p into sub-queue %u",
__func__, buf, rn->p.prefixlen, rn, qindex);
}
}
/* Add route_node to work queue and schedule processing */
static void
rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
{
char buf[INET_ADDRSTRLEN];
assert (zebra && rn);
if (IS_ZEBRA_DEBUG_RIB_Q)
inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
/* Pointless to queue a route_node with no RIB entries to add or remove */
if (!rn->info)
{
zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
__func__, rn, rn->lock);
zlog_backtrace(LOG_DEBUG);
return;
}
if (IS_ZEBRA_DEBUG_RIB_Q)
zlog_info ("%s: %s/%d: work queue added", __func__, buf, rn->p.prefixlen);
assert (zebra);
if (zebra->ribq == NULL)
{
zlog_err ("%s: work_queue does not exist!", __func__);
return;
}
/*
* The RIB queue should normally be either empty or holding the only
* work_queue_item element. In the latter case this element would
* hold a pointer to the meta queue structure, which must be used to
* actually queue the route nodes to process. So create the MQ
* holder, if necessary, then push the work into it in any case.
* This semantics was introduced after 0.99.9 release.
*/
if (!zebra->ribq->items->count)
work_queue_add (zebra->ribq, zebra->mq);
rib_meta_queue_add (zebra->mq, rn);
if (IS_ZEBRA_DEBUG_RIB_Q)
zlog_debug ("%s: %s/%d: rn %p queued", __func__, buf, rn->p.prefixlen, rn);
return;
}
/* Create new meta queue.
A destructor function doesn't seem to be necessary here.
*/
static struct meta_queue *
meta_queue_new (void)
{
struct meta_queue *new;
unsigned i;
new = XCALLOC (MTYPE_WORK_QUEUE, sizeof (struct meta_queue));
assert(new);
for (i = 0; i < MQ_SIZE; i++)
{
new->subq[i] = list_new ();
assert(new->subq[i]);
}
return new;
}
/* initialise zebra rib work queue */
static void
rib_queue_init (struct zebra_t *zebra)
{
assert (zebra);
if (! (zebra->ribq = work_queue_new (zebra->master,
"route_node processing")))
{
zlog_err ("%s: could not initialise work queue!", __func__);
return;
}
/* fill in the work queue spec */
zebra->ribq->spec.workfunc = &meta_queue_process;
zebra->ribq->spec.errorfunc = NULL;
/* XXX: TODO: These should be runtime configurable via vty */
zebra->ribq->spec.max_retries = 3;
zebra->ribq->spec.hold = rib_process_hold_time;
if (!(zebra->mq = meta_queue_new ()))
{
zlog_err ("%s: could not initialise meta queue!", __func__);
return;
}
return;
}
/* RIB updates are processed via a queue of pointers to route_nodes.
*
* The queue length is bounded by the maximal size of the routing table,
* as a route_node will not be requeued, if already queued.
*
* RIBs are submitted via rib_addnode or rib_delnode which set minimal
* state, or static_install_ipv{4,6} (when an existing RIB is updated)
* and then submit route_node to queue for best-path selection later.
* Order of add/delete state changes are preserved for any given RIB.
*
* Deleted RIBs are reaped during best-path selection.
*
* rib_addnode
* |-> rib_link or unset RIB_ENTRY_REMOVE |->Update kernel with
* |-------->| | best RIB, if required
* | |
* static_install->|->rib_addqueue...... -> rib_process
* | |
* |-------->| |-> rib_unlink
* |-> set RIB_ENTRY_REMOVE |
* rib_delnode (RIB freed)
*
*
* Queueing state for a route_node is kept in the head RIB entry, this
* state must be preserved as and when the head RIB entry of a
* route_node is changed by rib_unlink / rib_link. A small complication,
* but saves having to allocate a dedicated object for this.
*
* Refcounting (aka "locking" throughout the GNU Zebra and Quagga code):
*
* - route_nodes: refcounted by:
* - RIBs attached to route_node:
* - managed by: rib_link/unlink
* - route_node processing queue
* - managed by: rib_addqueue, rib_process.
*
*/
/* Add RIB to head of the route node. */
static void
rib_link (struct route_node *rn, struct rib *rib)
{
struct rib *head;
char buf[INET6_ADDRSTRLEN];
assert (rib && rn);
route_lock_node (rn); /* rn route table reference */
if (IS_ZEBRA_DEBUG_RIB)
{
inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
zlog_debug ("%s: %s/%d: rn %p, rib %p", __func__,
buf, rn->p.prefixlen, rn, rib);
}
head = rn->info;
if (head)
{
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: %s/%d: new head, rn_status copied over", __func__,
buf, rn->p.prefixlen);
head->prev = rib;
/* Transfer the rn status flags to the new head RIB */
rib->rn_status = head->rn_status;
}
rib->next = head;
rn->info = rib;
rib_queue_add (&zebrad, rn);
}
static void
rib_addnode (struct route_node *rn, struct rib *rib)
{
/* RIB node has been un-removed before route-node is processed.
* route_node must hence already be on the queue for processing..
*/
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
{
if (IS_ZEBRA_DEBUG_RIB)
{
char buf[INET6_ADDRSTRLEN];
inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
zlog_debug ("%s: %s/%d: rn %p, un-removed rib %p",
__func__, buf, rn->p.prefixlen, rn, rib);
}
UNSET_FLAG (rib->status, RIB_ENTRY_REMOVED);
return;
}
rib_link (rn, rib);
}
static void
rib_unlink (struct route_node *rn, struct rib *rib)
{
struct nexthop *nexthop, *next;
char buf[INET6_ADDRSTRLEN];
assert (rn && rib);
if (IS_ZEBRA_DEBUG_RIB)
{
inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
zlog_debug ("%s: %s/%d: rn %p, rib %p",
__func__, buf, rn->p.prefixlen, rn, rib);
}
if (rib->next)
rib->next->prev = rib->prev;
if (rib->prev)
rib->prev->next = rib->next;
else
{
rn->info = rib->next;
if (rn->info)
{
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: %s/%d: rn %p, rib %p, new head copy",
__func__, buf, rn->p.prefixlen, rn, rib);
rib->next->rn_status = rib->rn_status;
}
}
/* free RIB and nexthops */
for (nexthop = rib->nexthop; nexthop; nexthop = next)
{
next = nexthop->next;
nexthop_free (nexthop);
}
XFREE (MTYPE_RIB, rib);
route_unlock_node (rn); /* rn route table reference */
}
static void
rib_delnode (struct route_node *rn, struct rib *rib)
{
if (IS_ZEBRA_DEBUG_RIB)
{
char buf[INET6_ADDRSTRLEN];
inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
zlog_debug ("%s: %s/%d: rn %p, rib %p, removing", __func__,
buf, rn->p.prefixlen, rn, rib);
}
SET_FLAG (rib->status, RIB_ENTRY_REMOVED);
rib_queue_add (&zebrad, rn);
}
int
rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
struct in_addr *gate, struct in_addr *src,
unsigned int ifindex, u_int32_t vrf_id,
u_int32_t metric, u_char distance, safi_t safi)
{
struct rib *rib;
struct rib *same = NULL;
struct route_table *table;
struct route_node *rn;
struct nexthop *nexthop;
/* Lookup table. */
table = vrf_table (AFI_IP, safi, 0);
if (! table)
return 0;
/* Make it sure prefixlen is applied to the prefix. */
apply_mask_ipv4 (p);
/* Set default distance by route type. */
if (distance == 0)
{
if ((unsigned)type >= sizeof(route_info) / sizeof(route_info[0]))
distance = 150;
else
distance = route_info[type].distance;
/* iBGP distance is 200. */
if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP))
distance = 200;
}
/* Lookup route node.*/
rn = route_node_get (table, (struct prefix *) p);
/* If same type of route are installed, treat it as a implicit
withdraw. */
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type != type)
continue;
if (rib->type != ZEBRA_ROUTE_CONNECT)
{
same = rib;
break;
}
/* Duplicate connected route comes in. */
else if ((nexthop = rib->nexthop) &&
nexthop->type == NEXTHOP_TYPE_IFINDEX &&
nexthop->ifindex == ifindex &&
!CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
{
rib->refcnt++;
return 0 ;
}
}
/* Allocate new rib structure. */
rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
rib->type = type;
rib->distance = distance;
rib->flags = flags;
rib->metric = metric;
rib->table = vrf_id;
rib->nexthop_num = 0;
rib->uptime = time (NULL);
/* Nexthop settings. */
if (gate)
{
if (ifindex)
nexthop_ipv4_ifindex_add (rib, gate, src, ifindex);
else
nexthop_ipv4_add (rib, gate, src);
}
else
nexthop_ifindex_add (rib, ifindex);
/* If this route is kernel route, set FIB flag to the route. */
if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT)
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
/* Link new rib to node.*/
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: calling rib_addnode (%p, %p)", __func__, rn, rib);
rib_addnode (rn, rib);
/* Free implicit route.*/
if (same)
{
if (IS_ZEBRA_DEBUG_RIB)
zlog_debug ("%s: calling rib_delnode (%p, %p)", __func__, rn, rib);
rib_delnode (rn, same);
}
route_unlock_node (rn);
return 0;
}
/* This function dumps the contents of a given RIB entry into
* standard debug log. Calling function name and IP prefix in
* question are passed as 1st and 2nd arguments.
*/
void rib_dump (const char * func, const struct prefix_ipv4 * p, const struct rib * rib)
{
char straddr1[INET_ADDRSTRLEN], straddr2[INET_ADDRSTRLEN];
struct nexthop *nexthop;
inet_ntop (AF_INET, &p->prefix, straddr1, INET_ADDRSTRLEN);
zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr1, p->prefixlen);
zlog_debug
(
"%s: refcnt == %lu, uptime == %lu, type == %u, table == %d",
func,
rib->refcnt,
(unsigned long) rib->uptime,
rib->type,
rib->table
);
zlog_debug
(
"%s: metric == %u, distance == %u, flags == %u, status == %u",
func,
rib->metric,
rib->distance,
rib->flags,
rib->status
);
zlog_debug
(
"%s: nexthop_num == %u, nexthop_active_num == %u, nexthop_fib_num == %u",
func,
rib->nexthop_num,
rib->nexthop_active_num,
rib->nexthop_fib_num
);
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
{
inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, straddr1, INET_ADDRSTRLEN);
inet_ntop (AF_INET, &nexthop->rgate.ipv4.s_addr, straddr2, INET_ADDRSTRLEN);
zlog_debug
(
"%s: NH %s (%s) with flags %s%s%s",
func,
straddr1,
straddr2,
(CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""),
(CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? "FIB " : ""),
(CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) ? "RECURSIVE" : "")
);
}
zlog_debug ("%s: dump complete", func);
}
/* This is an exported helper to rtm_read() to dump the strange
* RIB entry found by rib_lookup_ipv4_route()
*/
void rib_lookup_and_dump (struct prefix_ipv4 * p)
{
struct route_table *table;
struct route_node *rn;
struct rib *rib;
char prefix_buf[INET_ADDRSTRLEN];
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
{
zlog_err ("%s: vrf_table() returned NULL", __func__);
return;
}
inet_ntop (AF_INET, &p->prefix.s_addr, prefix_buf, INET_ADDRSTRLEN);
/* Scan the RIB table for exactly matching RIB entry. */
rn = route_node_lookup (table, (struct prefix *) p);
/* No route for this prefix. */
if (! rn)
{
zlog_debug ("%s: lookup failed for %s/%d", __func__, prefix_buf, p->prefixlen);
return;
}
/* Unlock node. */
route_unlock_node (rn);
/* let's go */
for (rib = rn->info; rib; rib = rib->next)
{
zlog_debug
(
"%s: rn %p, rib %p: %s, %s",
__func__,
rn,
rib,
(CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED) ? "removed" : "NOT removed"),
(CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? "selected" : "NOT selected")
);
rib_dump (__func__, p, rib);
}
}
/* Check if requested address assignment will fail due to another
* route being installed by zebra in FIB already. Take necessary
* actions, if needed: remove such a route from FIB and deSELECT
* corresponding RIB entry. Then put affected RN into RIBQ head.
*/
void rib_lookup_and_pushup (struct prefix_ipv4 * p)
{
struct route_table *table;
struct route_node *rn;
struct rib *rib;
unsigned changed = 0;
if (NULL == (table = vrf_table (AFI_IP, SAFI_UNICAST, 0)))
{
zlog_err ("%s: vrf_table() returned NULL", __func__);
return;
}
/* No matches would be the simplest case. */
if (NULL == (rn = route_node_lookup (table, (struct prefix *) p)))
return;
/* Unlock node. */
route_unlock_node (rn);
/* Check all RIB entries. In case any changes have to be done, requeue
* the RN into RIBQ head. If the routing message about the new connected
* route (generated by the IP address we are going to assign very soon)
* comes before the RIBQ is processed, the new RIB entry will join
* RIBQ record already on head. This is necessary for proper revalidation
* of the rest of the RIB.
*/
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) &&
! RIB_SYSTEM_ROUTE (rib))
{
changed = 1;
if (IS_ZEBRA_DEBUG_RIB)
{
char buf[INET_ADDRSTRLEN];
inet_ntop (rn->p.family, &p->prefix, buf, INET_ADDRSTRLEN);
zlog_debug ("%s: freeing way for connected prefix %s/%d", __func__, buf, p->prefixlen);
rib_dump (__func__, (struct prefix_ipv4 *)&rn->p, rib);
}
rib_uninstall (rn, rib);
}
}
if (changed)
rib_queue_add (&zebrad, rn);
}
int
rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi)
{
struct route_table *table;
struct route_node *rn;
struct rib *same;
struct nexthop *nexthop;
/* Lookup table. */
table = vrf_table (AFI_IP, safi, 0);
if (! table)
return 0;
/* Make it sure prefixlen is applied to the prefix. */
apply_mask_ipv4 (p);
/* Set default distance by route type. */
if (rib->distance == 0)
{
rib->distance = route_info[rib->type].distance;
/* iBGP distance is 200. */
if (rib->type == ZEBRA_ROUTE_BGP
&& CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP))
rib->distance = 200;
}
/* Lookup route node.*/
rn = route_node_get (table, (struct prefix *) p);
/* If same type of route are installed, treat it as a implicit
withdraw. */
for (same = rn->info; same; same = same->next)
{
if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED))
continue;
if (same->type == rib->type && same->table == rib->table
&& same->type != ZEBRA_ROUTE_CONNECT)
break;
}
/* If this route is kernel route, set FIB flag to the route. */
if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT)
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
/* Link new rib to node.*/
rib_addnode (rn, rib);
if (IS_ZEBRA_DEBUG_RIB)
{
zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry",
__func__, rn, rib);
rib_dump (__func__, p, rib);
}
/* Free implicit route.*/
if (same)
{
if (IS_ZEBRA_DEBUG_RIB)
{
zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry",
__func__, rn, same);
rib_dump (__func__, p, same);
}
rib_delnode (rn, same);
}
route_unlock_node (rn);
return 0;
}
/* XXX factor with rib_delete_ipv6 */
int
rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p,
struct in_addr *gate, unsigned int ifindex, u_int32_t vrf_id, safi_t safi)
{
struct route_table *table;
struct route_node *rn;
struct rib *rib;
struct rib *fib = NULL;
struct rib *same = NULL;
struct nexthop *nexthop;
char buf1[INET_ADDRSTRLEN];
char buf2[INET_ADDRSTRLEN];
/* Lookup table. */
table = vrf_table (AFI_IP, safi, 0);
if (! table)
return 0;
/* Apply mask. */
apply_mask_ipv4 (p);
if (IS_ZEBRA_DEBUG_KERNEL && gate)
zlog_debug ("rib_delete_ipv4(): route delete %s/%d via %s ifindex %d",
inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
inet_ntoa (*gate),
ifindex);
/* Lookup route node. */
rn = route_node_lookup (table, (struct prefix *) p);
if (! rn)
{
if (IS_ZEBRA_DEBUG_KERNEL)
{
if (gate)
zlog_debug ("route %s/%d via %s ifindex %d doesn't exist in rib",
inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
inet_ntop (AF_INET, gate, buf2, INET_ADDRSTRLEN),
ifindex);
else
zlog_debug ("route %s/%d ifindex %d doesn't exist in rib",
inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
ifindex);
}
return ZEBRA_ERR_RTNOEXIST;
}
/* Lookup same type route. */
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
fib = rib;
if (rib->type != type)
continue;
if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) &&
nexthop->type == NEXTHOP_TYPE_IFINDEX)
{
if (nexthop->ifindex != ifindex)
continue;
if (rib->refcnt)
{
rib->refcnt--;
route_unlock_node (rn);
route_unlock_node (rn);
return 0;
}
same = rib;
break;
}
/* Make sure that the route found has the same gateway. */
else if (gate == NULL ||
((nexthop = rib->nexthop) &&
(IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate) ||
IPV4_ADDR_SAME (&nexthop->rgate.ipv4, gate))))
{
same = rib;
break;
}
}
/* If same type of route can't be found and this message is from
kernel. */
if (! same)
{
if (fib && type == ZEBRA_ROUTE_KERNEL)
{
/* Unset flags. */
for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next)
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED);
}
else
{
if (IS_ZEBRA_DEBUG_KERNEL)
{
if (gate)
zlog_debug ("route %s/%d via %s ifindex %d type %d doesn't exist in rib",
inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
inet_ntop (AF_INET, gate, buf2, INET_ADDRSTRLEN),
ifindex,
type);
else
zlog_debug ("route %s/%d ifindex %d type %d doesn't exist in rib",
inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
ifindex,
type);
}
route_unlock_node (rn);
return ZEBRA_ERR_RTNOEXIST;
}
}
if (same)
rib_delnode (rn, same);
route_unlock_node (rn);
return 0;
}
/* Install static route into rib. */
static void
static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
{
struct rib *rib;
struct route_node *rn;
struct route_table *table;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
return;
/* Lookup existing route */
rn = route_node_get (table, p);
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
break;
}
if (rib)
{
/* Same distance static route is there. Update it with new
nexthop. */
route_unlock_node (rn);
switch (si->type)
{
case STATIC_IPV4_GATEWAY:
nexthop_ipv4_add (rib, &si->gate.ipv4, NULL);
break;
case STATIC_IPV4_IFNAME:
nexthop_ifname_add (rib, si->gate.ifname);
break;
case STATIC_IPV4_BLACKHOLE:
nexthop_blackhole_add (rib);
break;
}
rib_queue_add (&zebrad, rn);
}
else
{
/* This is new static route. */
rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
rib->type = ZEBRA_ROUTE_STATIC;
rib->distance = si->distance;
rib->metric = 0;
rib->nexthop_num = 0;
switch (si->type)
{
case STATIC_IPV4_GATEWAY:
nexthop_ipv4_add (rib, &si->gate.ipv4, NULL);
break;
case STATIC_IPV4_IFNAME:
nexthop_ifname_add (rib, si->gate.ifname);
break;
case STATIC_IPV4_BLACKHOLE:
nexthop_blackhole_add (rib);
break;
}
/* Save the flags of this static routes (reject, blackhole) */
rib->flags = si->flags;
/* Link this rib to the tree. */
rib_addnode (rn, rib);
}
}
static int
static_ipv4_nexthop_same (struct nexthop *nexthop, struct static_ipv4 *si)
{
if (nexthop->type == NEXTHOP_TYPE_IPV4
&& si->type == STATIC_IPV4_GATEWAY
&& IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->gate.ipv4))
return 1;
if (nexthop->type == NEXTHOP_TYPE_IFNAME
&& si->type == STATIC_IPV4_IFNAME
&& strcmp (nexthop->ifname, si->gate.ifname) == 0)
return 1;
if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE
&& si->type == STATIC_IPV4_BLACKHOLE)
return 1;
return 0;
}
/* Uninstall static route from RIB. */
static void
static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si)
{
struct route_node *rn;
struct rib *rib;
struct nexthop *nexthop;
struct route_table *table;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (! table)
return;
/* Lookup existing route with type and distance. */
rn = route_node_lookup (table, p);
if (! rn)
return;
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
break;
}
if (! rib)
{
route_unlock_node (rn);
return;
}
/* Lookup nexthop. */
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
if (static_ipv4_nexthop_same (nexthop, si))
break;
/* Can't find nexthop. */
if (! nexthop)
{
route_unlock_node (rn);
return;
}
/* Check nexthop. */
if (rib->nexthop_num == 1)
rib_delnode (rn, rib);
else
{
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
rib_uninstall (rn, rib);
nexthop_delete (rib, nexthop);
nexthop_free (nexthop);
rib_queue_add (&zebrad, rn);
}
/* Unlock node. */
route_unlock_node (rn);
}
/* Add static route into static route configuration. */
int
static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
u_char flags, u_char distance, u_int32_t vrf_id)
{
u_char type = 0;
struct route_node *rn;
struct static_ipv4 *si;
struct static_ipv4 *pp;
struct static_ipv4 *cp;
struct static_ipv4 *update = NULL;
struct route_table *stable;
/* Lookup table. */
stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id);
if (! stable)
return -1;
/* Lookup static route prefix. */
rn = route_node_get (stable, p);
/* Make flags. */
if (gate)
type = STATIC_IPV4_GATEWAY;
else if (ifname)
type = STATIC_IPV4_IFNAME;
else
type = STATIC_IPV4_BLACKHOLE;
/* Do nothing if there is a same static route. */
for (si = rn->info; si; si = si->next)
{
if (type == si->type
&& (! gate || IPV4_ADDR_SAME (gate, &si->gate.ipv4))
&& (! ifname || strcmp (ifname, si->gate.ifname) == 0))
{
if (distance == si->distance)
{
route_unlock_node (rn);
return 0;
}
else
update = si;
}
}
/* Distance changed. */
if (update)
static_delete_ipv4 (p, gate, ifname, update->distance, vrf_id);
/* Make new static route structure. */
si = XCALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4));
si->type = type;
si->distance = distance;
si->flags = flags;
if (gate)
si->gate.ipv4 = *gate;
if (ifname)
si->gate.ifname = XSTRDUP (0, ifname);
/* Add new static route information to the tree with sort by
distance value and gateway address. */
for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next)
{
if (si->distance < cp->distance)
break;
if (si->distance > cp->distance)
continue;
if (si->type == STATIC_IPV4_GATEWAY && cp->type == STATIC_IPV4_GATEWAY)
{
if (ntohl (si->gate.ipv4.s_addr) < ntohl (cp->gate.ipv4.s_addr))
break;
if (ntohl (si->gate.ipv4.s_addr) > ntohl (cp->gate.ipv4.s_addr))
continue;
}
}
/* Make linked list. */
if (pp)
pp->next = si;
else
rn->info = si;
if (cp)
cp->prev = si;
si->prev = pp;
si->next = cp;
/* Install into rib. */
static_install_ipv4 (p, si);
return 1;
}
/* Delete static route from static route configuration. */
int
static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname,
u_char distance, u_int32_t vrf_id)
{
u_char type = 0;
struct route_node *rn;
struct static_ipv4 *si;
struct route_table *stable;
/* Lookup table. */
stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id);
if (! stable)
return -1;
/* Lookup static route prefix. */
rn = route_node_lookup (stable, p);
if (! rn)
return 0;
/* Make flags. */
if (gate)
type = STATIC_IPV4_GATEWAY;
else if (ifname)
type = STATIC_IPV4_IFNAME;
else
type = STATIC_IPV4_BLACKHOLE;
/* Find same static route is the tree */
for (si = rn->info; si; si = si->next)
if (type == si->type
&& (! gate || IPV4_ADDR_SAME (gate, &si->gate.ipv4))
&& (! ifname || strcmp (ifname, si->gate.ifname) == 0))
break;
/* Can't find static route. */
if (! si)
{
route_unlock_node (rn);
return 0;
}
/* Install into rib. */
static_uninstall_ipv4 (p, si);
/* Unlink static route from linked list. */
if (si->prev)
si->prev->next = si->next;
else
rn->info = si->next;
if (si->next)
si->next->prev = si->prev;
route_unlock_node (rn);
/* Free static route configuration. */
if (ifname)
XFREE (0, si->gate.ifname);
XFREE (MTYPE_STATIC_IPV4, si);
route_unlock_node (rn);
return 1;
}
#ifdef HAVE_IPV6
static int
rib_bogus_ipv6 (int type, struct prefix_ipv6 *p,
struct in6_addr *gate, unsigned int ifindex, int table)
{
if (type == ZEBRA_ROUTE_CONNECT && IN6_IS_ADDR_UNSPECIFIED (&p->prefix)) {
#if defined (MUSICA) || defined (LINUX)
/* IN6_IS_ADDR_V4COMPAT(&p->prefix) */
if (p->prefixlen == 96)
return 0;
#endif /* MUSICA */
return 1;
}
if (type == ZEBRA_ROUTE_KERNEL && IN6_IS_ADDR_UNSPECIFIED (&p->prefix)
&& p->prefixlen == 96 && gate && IN6_IS_ADDR_UNSPECIFIED (gate))
{
kernel_delete_ipv6_old (p, gate, ifindex, 0, table);
return 1;
}
return 0;
}
int
rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p,
struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id,
u_int32_t metric, u_char distance, safi_t safi)
{
struct rib *rib;
struct rib *same = NULL;
struct route_table *table;
struct route_node *rn;
struct nexthop *nexthop;
/* Lookup table. */
table = vrf_table (AFI_IP6, safi, 0);
if (! table)
return 0;
/* Make sure mask is applied. */
apply_mask_ipv6 (p);
/* Set default distance by route type. */
if (!distance)
distance = route_info[type].distance;
if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP))
distance = 200;
/* Filter bogus route. */
if (rib_bogus_ipv6 (type, p, gate, ifindex, 0))
return 0;
/* Lookup route node.*/
rn = route_node_get (table, (struct prefix *) p);
/* If same type of route are installed, treat it as a implicit
withdraw. */
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type != type)
continue;
if (rib->type != ZEBRA_ROUTE_CONNECT)
{
same = rib;
break;
}
else if ((nexthop = rib->nexthop) &&
nexthop->type == NEXTHOP_TYPE_IFINDEX &&
nexthop->ifindex == ifindex)
{
rib->refcnt++;
return 0;
}
}
/* Allocate new rib structure. */
rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
rib->type = type;
rib->distance = distance;
rib->flags = flags;
rib->metric = metric;
rib->table = vrf_id;
rib->nexthop_num = 0;
rib->uptime = time (NULL);
/* Nexthop settings. */
if (gate)
{
if (ifindex)
nexthop_ipv6_ifindex_add (rib, gate, ifindex);
else
nexthop_ipv6_add (rib, gate);
}
else
nexthop_ifindex_add (rib, ifindex);
/* If this route is kernel route, set FIB flag to the route. */
if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT)
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
/* Link new rib to node.*/
rib_addnode (rn, rib);
/* Free implicit route.*/
if (same)
rib_delnode (rn, same);
route_unlock_node (rn);
return 0;
}
/* XXX factor with rib_delete_ipv6 */
int
rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p,
struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id, safi_t safi)
{
struct route_table *table;
struct route_node *rn;
struct rib *rib;
struct rib *fib = NULL;
struct rib *same = NULL;
struct nexthop *nexthop;
char buf1[INET6_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
/* Apply mask. */
apply_mask_ipv6 (p);
/* Lookup table. */
table = vrf_table (AFI_IP6, safi, 0);
if (! table)
return 0;
/* Lookup route node. */
rn = route_node_lookup (table, (struct prefix *) p);
if (! rn)
{
if (IS_ZEBRA_DEBUG_KERNEL)
{
if (gate)
zlog_debug ("route %s/%d via %s ifindex %d doesn't exist in rib",
inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
p->prefixlen,
inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN),
ifindex);
else
zlog_debug ("route %s/%d ifindex %d doesn't exist in rib",
inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
p->prefixlen,
ifindex);
}
return ZEBRA_ERR_RTNOEXIST;
}
/* Lookup same type route. */
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED))
continue;
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
fib = rib;
if (rib->type != type)
continue;
if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) &&
nexthop->type == NEXTHOP_TYPE_IFINDEX)
{
if (nexthop->ifindex != ifindex)
continue;
if (rib->refcnt)
{
rib->refcnt--;
route_unlock_node (rn);
route_unlock_node (rn);
return 0;
}
same = rib;
break;
}
/* Make sure that the route found has the same gateway. */
else if (gate == NULL ||
((nexthop = rib->nexthop) &&
(IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate) ||
IPV6_ADDR_SAME (&nexthop->rgate.ipv6, gate))))
{
same = rib;
break;
}
}
/* If same type of route can't be found and this message is from
kernel. */
if (! same)
{
if (fib && type == ZEBRA_ROUTE_KERNEL)
{
/* Unset flags. */
for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next)
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED);
}
else
{
if (IS_ZEBRA_DEBUG_KERNEL)
{
if (gate)
zlog_debug ("route %s/%d via %s ifindex %d type %d doesn't exist in rib",
inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
p->prefixlen,
inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN),
ifindex,
type);
else
zlog_debug ("route %s/%d ifindex %d type %d doesn't exist in rib",
inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
p->prefixlen,
ifindex,
type);
}
route_unlock_node (rn);
return ZEBRA_ERR_RTNOEXIST;
}
}
if (same)
rib_delnode (rn, same);
route_unlock_node (rn);
return 0;
}
/* Install static route into rib. */
static void
static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
{
struct rib *rib;
struct route_table *table;
struct route_node *rn;
/* Lookup table. */
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
if (! table)
return;
/* Lookup existing route */
rn = route_node_get (table, p);
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
break;
}
if (rib)
{
/* Same distance static route is there. Update it with new
nexthop. */
route_unlock_node (rn);
switch (si->type)
{
case STATIC_IPV6_GATEWAY:
nexthop_ipv6_add (rib, &si->ipv6);
break;
case STATIC_IPV6_IFNAME:
nexthop_ifname_add (rib, si->ifname);
break;
case STATIC_IPV6_GATEWAY_IFNAME:
nexthop_ipv6_ifname_add (rib, &si->ipv6, si->ifname);
break;
}
rib_queue_add (&zebrad, rn);
}
else
{
/* This is new static route. */
rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
rib->type = ZEBRA_ROUTE_STATIC;
rib->distance = si->distance;
rib->metric = 0;
rib->nexthop_num = 0;
switch (si->type)
{
case STATIC_IPV6_GATEWAY:
nexthop_ipv6_add (rib, &si->ipv6);
break;
case STATIC_IPV6_IFNAME:
nexthop_ifname_add (rib, si->ifname);
break;
case STATIC_IPV6_GATEWAY_IFNAME:
nexthop_ipv6_ifname_add (rib, &si->ipv6, si->ifname);
break;
}
/* Save the flags of this static routes (reject, blackhole) */
rib->flags = si->flags;
/* Link this rib to the tree. */
rib_addnode (rn, rib);
}
}
static int
static_ipv6_nexthop_same (struct nexthop *nexthop, struct static_ipv6 *si)
{
if (nexthop->type == NEXTHOP_TYPE_IPV6
&& si->type == STATIC_IPV6_GATEWAY
&& IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->ipv6))
return 1;
if (nexthop->type == NEXTHOP_TYPE_IFNAME
&& si->type == STATIC_IPV6_IFNAME
&& strcmp (nexthop->ifname, si->ifname) == 0)
return 1;
if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
&& si->type == STATIC_IPV6_GATEWAY_IFNAME
&& IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->ipv6)
&& strcmp (nexthop->ifname, si->ifname) == 0)
return 1;
return 0;
}
static void
static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si)
{
struct route_table *table;
struct route_node *rn;
struct rib *rib;
struct nexthop *nexthop;
/* Lookup table. */
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
if (! table)
return;
/* Lookup existing route with type and distance. */
rn = route_node_lookup (table, (struct prefix *) p);
if (! rn)
return;
for (rib = rn->info; rib; rib = rib->next)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance)
break;
}
if (! rib)
{
route_unlock_node (rn);
return;
}
/* Lookup nexthop. */
for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
if (static_ipv6_nexthop_same (nexthop, si))
break;
/* Can't find nexthop. */
if (! nexthop)
{
route_unlock_node (rn);
return;
}
/* Check nexthop. */
if (rib->nexthop_num == 1)
{
rib_delnode (rn, rib);
}
else
{
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
rib_uninstall (rn, rib);
nexthop_delete (rib, nexthop);
nexthop_free (nexthop);
rib_queue_add (&zebrad, rn);
}
/* Unlock node. */
route_unlock_node (rn);
}
/* Add static route into static route configuration. */
int
static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
const char *ifname, u_char flags, u_char distance,
u_int32_t vrf_id)
{
struct route_node *rn;
struct static_ipv6 *si;
struct static_ipv6 *pp;
struct static_ipv6 *cp;
struct route_table *stable;
/* Lookup table. */
stable = vrf_static_table (AFI_IP6, SAFI_UNICAST, vrf_id);
if (! stable)
return -1;
if (!gate &&
(type == STATIC_IPV6_GATEWAY || type == STATIC_IPV6_GATEWAY_IFNAME))
return -1;
if (!ifname &&
(type == STATIC_IPV6_GATEWAY_IFNAME || type == STATIC_IPV6_IFNAME))
return -1;
/* Lookup static route prefix. */
rn = route_node_get (stable, p);
/* Do nothing if there is a same static route. */
for (si = rn->info; si; si = si->next)
{
if (distance == si->distance
&& type == si->type
&& (! gate || IPV6_ADDR_SAME (gate, &si->ipv6))
&& (! ifname || strcmp (ifname, si->ifname) == 0))
{
route_unlock_node (rn);
return 0;
}
}
/* Make new static route structure. */
si = XCALLOC (MTYPE_STATIC_IPV6, sizeof (struct static_ipv6));
si->type = type;
si->distance = distance;
si->flags = flags;
switch (type)
{
case STATIC_IPV6_GATEWAY:
si->ipv6 = *gate;
break;
case STATIC_IPV6_IFNAME:
si->ifname = XSTRDUP (0, ifname);
break;
case STATIC_IPV6_GATEWAY_IFNAME:
si->ipv6 = *gate;
si->ifname = XSTRDUP (0, ifname);
break;
}
/* Add new static route information to the tree with sort by
distance value and gateway address. */
for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next)
{
if (si->distance < cp->distance)
break;
if (si->distance > cp->distance)
continue;
}
/* Make linked list. */
if (pp)
pp->next = si;
else
rn->info = si;
if (cp)
cp->prev = si;
si->prev = pp;
si->next = cp;
/* Install into rib. */
static_install_ipv6 (p, si);
return 1;
}
/* Delete static route from static route configuration. */
int
static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate,
const char *ifname, u_char distance, u_int32_t vrf_id)
{
struct route_node *rn;
struct static_ipv6 *si;
struct route_table *stable;
/* Lookup table. */
stable = vrf_static_table (AFI_IP6, SAFI_UNICAST, vrf_id);
if (! stable)
return -1;
/* Lookup static route prefix. */
rn = route_node_lookup (stable, p);
if (! rn)
return 0;
/* Find same static route is the tree */
for (si = rn->info; si; si = si->next)
if (distance == si->distance
&& type == si->type
&& (! gate || IPV6_ADDR_SAME (gate, &si->ipv6))
&& (! ifname || strcmp (ifname, si->ifname) == 0))
break;
/* Can't find static route. */
if (! si)
{
route_unlock_node (rn);
return 0;
}
/* Install into rib. */
static_uninstall_ipv6 (p, si);
/* Unlink static route from linked list. */
if (si->prev)
si->prev->next = si->next;
else
rn->info = si->next;
if (si->next)
si->next->prev = si->prev;
/* Free static route configuration. */
if (ifname)
XFREE (0, si->ifname);
XFREE (MTYPE_STATIC_IPV6, si);
return 1;
}
#endif /* HAVE_IPV6 */
/* RIB update function. */
void
rib_update (void)
{
struct route_node *rn;
struct route_table *table;
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
if (rn->info)
rib_queue_add (&zebrad, rn);
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
if (rn->info)
rib_queue_add (&zebrad, rn);
}
/* Remove all routes which comes from non main table. */
static void
rib_weed_table (struct route_table *table)
{
struct route_node *rn;
struct rib *rib;
struct rib *next;
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
for (rib = rn->info; rib; rib = next)
{
next = rib->next;
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->table != zebrad.rtm_table_default &&
rib->table != RT_TABLE_MAIN)
rib_delnode (rn, rib);
}
}
/* Delete all routes from non main table. */
void
rib_weed_tables (void)
{
rib_weed_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_weed_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
/* Delete self installed routes after zebra is relaunched. */
static void
rib_sweep_table (struct route_table *table)
{
struct route_node *rn;
struct rib *rib;
struct rib *next;
int ret = 0;
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
for (rib = rn->info; rib; rib = next)
{
next = rib->next;
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type == ZEBRA_ROUTE_KERNEL &&
CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELFROUTE))
{
ret = rib_uninstall_kernel (rn, rib);
if (! ret)
rib_delnode (rn, rib);
}
}
}
/* Sweep all RIB tables. */
void
rib_sweep_route (void)
{
rib_sweep_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_sweep_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
/* Remove specific by protocol routes from 'table'. */
static unsigned long
rib_score_proto_table (u_char proto, struct route_table *table)
{
struct route_node *rn;
struct rib *rib;
struct rib *next;
unsigned long n = 0;
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
for (rib = rn->info; rib; rib = next)
{
next = rib->next;
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type == proto)
{
rib_delnode (rn, rib);
n++;
}
}
return n;
}
/* Remove specific by protocol routes. */
unsigned long
rib_score_proto (u_char proto)
{
return rib_score_proto_table (proto, vrf_table (AFI_IP, SAFI_UNICAST, 0))
+rib_score_proto_table (proto, vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
/* Close RIB and clean up kernel routes. */
static void
rib_close_table (struct route_table *table)
{
struct route_node *rn;
struct rib *rib;
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
for (rib = rn->info; rib; rib = rib->next)
{
if (! RIB_SYSTEM_ROUTE (rib)
&& CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
rib_uninstall_kernel (rn, rib);
}
}
/* Close all RIB tables. */
void
rib_close (void)
{
rib_close_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_close_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
/* Routing information base initialize. */
void
rib_init (void)
{
rib_queue_init (&zebrad);
/* VRF initialization. */
vrf_init ();
}