#include "workqueue.h"
#include "prefix.h"
#include "routemap.h"
+#include "nexthop.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
#include "zebra/zserv.h"
#include "zebra/redistribute.h"
#include "zebra/debug.h"
+#include "zebra/zebra_fpm.h"
+#include "zebra/zebra_rnh.h"
/* Default rtm_table for all clients */
extern struct zebra_t zebrad;
int rib_process_hold_time = 10;
/* Each route type's string and default distance value. */
-struct
+static const struct
{
int key;
int distance;
-} route_info[] =
-{
- {ZEBRA_ROUTE_SYSTEM, 0},
- {ZEBRA_ROUTE_KERNEL, 0},
- {ZEBRA_ROUTE_CONNECT, 0},
- {ZEBRA_ROUTE_STATIC, 1},
- {ZEBRA_ROUTE_RIP, 120},
- {ZEBRA_ROUTE_RIPNG, 120},
- {ZEBRA_ROUTE_OSPF, 110},
- {ZEBRA_ROUTE_OSPF6, 110},
- {ZEBRA_ROUTE_ISIS, 115},
- {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */}
+} 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 */
};
-\f
+
/* Vector for routing table. */
-vector vrf_vector;
+static vector vrf_vector;
+
+/*
+ * vrf_table_create
+ */
+static void
+vrf_table_create (struct vrf *vrf, afi_t afi, safi_t safi)
+{
+ rib_table_info_t *info;
+ struct route_table *table;
+
+ assert (!vrf->table[afi][safi]);
+
+ table = route_table_init ();
+ vrf->table[afi][safi] = table;
+
+ info = XCALLOC (MTYPE_RIB_TABLE_INFO, sizeof (*info));
+ info->vrf = vrf;
+ info->afi = afi;
+ info->safi = safi;
+ table->info = info;
+}
/* Allocate new VRF. */
static struct vrf *
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_table_create (vrf, AFI_IP, SAFI_UNICAST);
+ vrf_table_create (vrf, AFI_IP6, SAFI_UNICAST);
vrf->stable[AFI_IP][SAFI_UNICAST] = route_table_init ();
vrf->stable[AFI_IP6][SAFI_UNICAST] = route_table_init ();
+ vrf_table_create (vrf, AFI_IP, SAFI_MULTICAST);
+ vrf_table_create (vrf, AFI_IP6, SAFI_MULTICAST);
+ vrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init ();
+ vrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init ();
- return vrf;
-}
+ vrf->rnh_table[AFI_IP] = route_table_init();
+ vrf->rnh_table[AFI_IP6] = route_table_init();
-/* Free VRF. */
-static void
-vrf_free (struct vrf *vrf)
-{
- if (vrf->name)
- XFREE (MTYPE_VRF_NAME, vrf->name);
- XFREE (MTYPE_VRF, vrf);
+ return vrf;
}
/* Lookup VRF by identifier. */
return vector_lookup (vrf_vector, id);
}
-/* Lookup VRF by name. */
-static struct vrf *
-vrf_lookup_by_name (char *name)
-{
- unsigned int i;
- struct vrf *vrf;
-
- for (i = 0; i < vector_active (vrf_vector); i++)
- if ((vrf = vector_slot (vrf_vector, i)) != NULL)
- if (vrf->name && name && strcmp (vrf->name, name) == 0)
- return vrf;
- return NULL;
-}
-
/* Initialize VRF. */
static void
vrf_init (void)
if (! vrf)
return NULL;
+ if( afi >= AFI_MAX || safi >= SAFI_MAX )
+ return NULL;
+
return vrf->table[afi][safi];
}
if (! vrf)
return NULL;
+ if( afi >= AFI_MAX || safi >= SAFI_MAX )
+ return NULL;
+
return vrf->stable[afi][safi];
}
-\f
-/* Add nexthop to the end of the list. */
+
+/* Add nexthop to the end of a nexthop list. */
static void
-nexthop_add (struct rib *rib, struct nexthop *nexthop)
+_nexthop_add (struct nexthop **target, struct nexthop *nexthop)
{
struct nexthop *last;
- for (last = rib->nexthop; last && last->next; last = last->next)
+ for (last = *target; last && last->next; last = last->next)
;
if (last)
last->next = nexthop;
else
- rib->nexthop = nexthop;
+ *target = nexthop;
nexthop->prev = last;
+}
+/* Add nexthop to the end of a rib node's nexthop list */
+void
+nexthop_add (struct rib *rib, struct nexthop *nexthop)
+{
+ _nexthop_add(&rib->nexthop, nexthop);
rib->nexthop_num++;
}
}
/* Free nexthop. */
-static void
+void
nexthop_free (struct nexthop *nexthop)
{
if (nexthop->ifname)
XFREE (0, nexthop->ifname);
+ if (nexthop->resolved)
+ nexthops_free(nexthop->resolved);
XFREE (MTYPE_NEXTHOP, nexthop);
}
+/* Frees a list of nexthops */
+void
+nexthops_free (struct nexthop *nexthop)
+{
+ struct nexthop *nh, *next;
+
+ for (nh = nexthop; nh; nh = next)
+ {
+ next = nh->next;
+ nexthop_free (nh);
+ }
+}
+
struct nexthop *
nexthop_ifindex_add (struct rib *rib, unsigned int ifindex)
{
struct nexthop *nexthop;
- nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
- memset (nexthop, 0, sizeof (struct nexthop));
+ nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IFINDEX;
nexthop->ifindex = ifindex;
{
struct nexthop *nexthop;
- nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
- memset (nexthop, 0, sizeof (struct nexthop));
+ nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IFNAME;
nexthop->ifname = XSTRDUP (0, ifname);
{
struct nexthop *nexthop;
- nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
- memset (nexthop, 0, sizeof (struct nexthop));
+ nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV4;
nexthop->gate.ipv4 = *ipv4;
if (src)
return nexthop;
}
-static struct 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 = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
- memset (nexthop, 0, sizeof (struct nexthop));
+ nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
nexthop->gate.ipv4 = *ipv4;
if (src)
{
struct nexthop *nexthop;
- nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
- memset (nexthop, 0, sizeof (struct nexthop));
+ nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV6;
nexthop->gate.ipv6 = *ipv6;
return nexthop;
}
-static struct nexthop *
+struct nexthop *
nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6,
char *ifname)
{
struct nexthop *nexthop;
- nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
- memset (nexthop, 0, sizeof (struct nexthop));
+ nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME;
nexthop->gate.ipv6 = *ipv6;
nexthop->ifname = XSTRDUP (0, ifname);
return nexthop;
}
-static struct nexthop *
+struct nexthop *
nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6,
unsigned int ifindex)
{
struct nexthop *nexthop;
- nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
- memset (nexthop, 0, sizeof (struct nexthop));
+ nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
nexthop->gate.ipv6 = *ipv6;
nexthop->ifindex = ifindex;
{
struct nexthop *nexthop;
- nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
- memset (nexthop, 0, sizeof (struct nexthop));
+ nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop));
nexthop->type = NEXTHOP_TYPE_BLACKHOLE;
SET_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE);
return nexthop;
}
+/* This method checks whether a recursive nexthop has at
+ * least one resolved nexthop in the fib.
+ */
+int
+nexthop_has_fib_child(struct nexthop *nexthop)
+{
+ struct nexthop *nh;
+
+ if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ return 0;
+
+ for (nh = nexthop->resolved; nh; nh = nh->next)
+ if (CHECK_FLAG (nh->flags, NEXTHOP_FLAG_FIB))
+ return 1;
+
+ return 0;
+}
+
/* If force flag is not set, do not modify falgs at all for uninstall
the route from FIB. */
static int
struct route_table *table;
struct route_node *rn;
struct rib *match;
+ int resolved;
struct nexthop *newhop;
+ struct nexthop *resolved_hop;
if (nexthop->type == NEXTHOP_TYPE_IPV4)
nexthop->ifindex = 0;
if (set)
- UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+ {
+ UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+ nexthops_free(nexthop->resolved);
+ nexthop->resolved = NULL;
+ }
/* Make lookup prefix. */
memset (&p, 0, sizeof (struct prefix_ipv4));
{
route_unlock_node (rn);
- /* If lookup self prefix return immidiately. */
+ /* 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->flags, ZEBRA_FLAG_SELECTED))
- break;
+ RNODE_FOREACH_RIB (rn, match)
+ {
+ 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. */
}
else
{
+ /* If the longest prefix match for the nexthop yields
+ * a blackhole, mark it as inactive. */
+ if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE)
+ || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT))
+ return 0;
+
if (match->type == ZEBRA_ROUTE_CONNECT)
{
/* Directly point connected route. */
}
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
{
+ resolved = 0;
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;
+
+ resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop));
+ SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+ /* If the resolving route specifies a gateway, use it */
+ if (newhop->type == NEXTHOP_TYPE_IPV4
+ || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+ || newhop->type == NEXTHOP_TYPE_IPV4_IFNAME)
+ {
+ resolved_hop->type = newhop->type;
+ resolved_hop->gate.ipv4 = newhop->gate.ipv4;
+
+ if (newhop->ifindex)
+ {
+ resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ resolved_hop->ifindex = newhop->ifindex;
+ }
+ }
+
+ /* If the resolving route is an interface route,
+ * it means the gateway we are looking up is connected
+ * to that interface. (The actual network is _not_ onlink).
+ * Therefore, the resolved route should have the original
+ * gateway as nexthop as it is directly connected.
+ *
+ * On Linux, we have to set the onlink netlink flag because
+ * otherwise, the kernel won't accept the route. */
if (newhop->type == NEXTHOP_TYPE_IFINDEX
- || newhop->type == NEXTHOP_TYPE_IFNAME
- || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
- nexthop->rifindex = newhop->ifindex;
+ || newhop->type == NEXTHOP_TYPE_IFNAME)
+ {
+ resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+ resolved_hop->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ resolved_hop->gate.ipv4 = nexthop->gate.ipv4;
+ resolved_hop->ifindex = newhop->ifindex;
+ }
+
+ _nexthop_add(&nexthop->resolved, resolved_hop);
}
- return 1;
+ resolved = 1;
}
- return 0;
+ return resolved;
}
else
{
struct route_table *table;
struct route_node *rn;
struct rib *match;
+ int resolved;
struct nexthop *newhop;
+ struct nexthop *resolved_hop;
if (nexthop->type == NEXTHOP_TYPE_IPV6)
nexthop->ifindex = 0;
if (set)
- UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+ {
+ UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+ nexthops_free(nexthop->resolved);
+ nexthop->resolved = NULL;
+ }
/* Make lookup prefix. */
memset (&p, 0, sizeof (struct prefix_ipv6));
{
route_unlock_node (rn);
- /* If lookup self prefix return immidiately. */
+ /* 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->flags, ZEBRA_FLAG_SELECTED))
- break;
+ RNODE_FOREACH_RIB (rn, match)
+ {
+ 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. */
}
else
{
+ /* If the longest prefix match for the nexthop yields
+ * a blackhole, mark it as inactive. */
+ if (CHECK_FLAG (match->flags, ZEBRA_FLAG_BLACKHOLE)
+ || CHECK_FLAG (match->flags, ZEBRA_FLAG_REJECT))
+ return 0;
+
if (match->type == ZEBRA_ROUTE_CONNECT)
{
/* Directly point connected route. */
}
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
{
+ resolved = 0;
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;
+
+ resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop));
+ SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
+ /* See nexthop_active_ipv4 for a description how the
+ * resolved nexthop is constructed. */
if (newhop->type == NEXTHOP_TYPE_IPV6
|| newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX
|| newhop->type == NEXTHOP_TYPE_IPV6_IFNAME)
- nexthop->rgate.ipv6 = newhop->gate.ipv6;
+ {
+ resolved_hop->type = newhop->type;
+ resolved_hop->gate.ipv6 = newhop->gate.ipv6;
+
+ if (newhop->ifindex)
+ {
+ resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ resolved_hop->ifindex = newhop->ifindex;
+ }
+ }
+
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;
+ || newhop->type == NEXTHOP_TYPE_IFNAME)
+ {
+ resolved_hop->flags |= NEXTHOP_FLAG_ONLINK;
+ resolved_hop->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ resolved_hop->gate.ipv6 = nexthop->gate.ipv6;
+ resolved_hop->ifindex = newhop->ifindex;
+ }
+
+ _nexthop_add(&nexthop->resolved, resolved_hop);
}
- return 1;
+ resolved = 1;
}
- return 0;
+ return resolved;
}
else
{
struct route_table *table;
struct route_node *rn;
struct rib *match;
- struct nexthop *newhop;
+ struct nexthop *newhop, *tnewhop;
+ int recursing;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
route_unlock_node (rn);
/* Pick up selected route. */
- for (match = rn->info; match; match = match->next)
- if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
- break;
+ RNODE_FOREACH_RIB (rn, match)
+ {
+ 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. */
return match;
else
{
- for (newhop = match->nexthop; newhop; newhop = newhop->next)
+ for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
return match;
return NULL;
struct route_table *table;
struct route_node *rn;
struct rib *match;
- struct nexthop *nexthop;
+ struct nexthop *nexthop, *tnexthop;
+ int recursing;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
/* Unlock node. */
route_unlock_node (rn);
- /* Pick up selected route. */
- for (match = rn->info; match; match = match->next)
- if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
- break;
+ RNODE_FOREACH_RIB (rn, match)
+ {
+ 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)
+ for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing))
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, *tnexthop;
+ int recursing;
+ int nexthops_active;
+
+ /* 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. */
+ RNODE_FOREACH_RIB (rn, match)
+ {
+ 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... */
+ nexthops_active = 0;
+ for (ALL_NEXTHOPS_RO(match->nexthop, nexthop, tnexthop, recursing))
+ if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
+ {
+ nexthops_active = 1;
+ if (nexthop->gate.ipv4.s_addr == sockunion2ip (qgate))
+ return ZEBRA_RIB_FOUND_EXACT;
+ if (IS_ZEBRA_DEBUG_RIB)
+ {
+ char gate_buf[INET_ADDRSTRLEN], qgate_buf[INET_ADDRSTRLEN];
+ inet_ntop (AF_INET, &nexthop->gate.ipv4.s_addr, gate_buf, INET_ADDRSTRLEN);
+ inet_ntop (AF_INET, &sockunion2ip(qgate), qgate_buf, INET_ADDRSTRLEN);
+ zlog_debug ("%s: qgate == %s, %s == %s", __func__,
+ qgate_buf, recursing ? "rgate" : "gate", gate_buf);
+ }
+ }
+
+ if (nexthops_active)
+ return ZEBRA_RIB_FOUND_NOGATE;
+
+ return ZEBRA_RIB_NOTFOUND;
+}
+
#ifdef HAVE_IPV6
struct rib *
rib_match_ipv6 (struct in6_addr *addr)
struct route_table *table;
struct route_node *rn;
struct rib *match;
- struct nexthop *newhop;
+ struct nexthop *newhop, *tnewhop;
+ int recursing;
/* Lookup table. */
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
route_unlock_node (rn);
/* Pick up selected route. */
- for (match = rn->info; match; match = match->next)
- if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED))
- break;
+ RNODE_FOREACH_RIB (rn, match)
+ {
+ 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. */
return match;
else
{
- for (newhop = match->nexthop; newhop; newhop = newhop->next)
+ for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
return match;
return NULL;
#define RIB_SYSTEM_ROUTE(R) \
((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT)
-static int
+/* 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)
{
+ rib_table_info_t *info = rn->table->info;
struct interface *ifp;
route_map_result_t ret = RMAP_MATCH;
extern char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1];
{
case NEXTHOP_TYPE_IFINDEX:
ifp = if_lookup_by_index (nexthop->ifindex);
- if (ifp && if_is_up (ifp))
+ if (ifp && if_is_operative(ifp))
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
family = AFI_IP6;
case NEXTHOP_TYPE_IFNAME:
ifp = if_lookup_by_name (nexthop->ifname);
- if (ifp && if_is_up (ifp))
+ if (ifp && if_is_operative(ifp))
{
if (set)
nexthop->ifindex = ifp->ifindex;
if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6))
{
ifp = if_lookup_by_index (nexthop->ifindex);
- if (ifp && if_is_up (ifp))
+ if (ifp && if_is_operative(ifp))
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
else
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
return 0;
+ /* XXX: What exactly do those checks do? Do we support
+ * e.g. IPv4 routes with IPv6 nexthops or vice versa? */
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);
+ /* The original code didn't determine the family correctly
+ * e.g. for NEXTHOP_TYPE_IFINDEX. Retrieve the correct afi
+ * from the rib_table_info in those cases.
+ * Possibly it may be better to use only the rib_table_info
+ * in every case.
+ */
+ if (!family)
+ family = info->afi;
+
rmap = 0;
if (rib->type >= 0 && rib->type < ZEBRA_ROUTE_MAX &&
proto_rm[family][rib->type])
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;
- int active;
+ 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)
- {
- active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
-
- nexthop_active_check (rn, rib, nexthop, set);
- if ((MULTIPATH_NUM == 0 || rib->nexthop_active_num < MULTIPATH_NUM)
- && active != CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
- SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
-
- if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
- rib->nexthop_active_num++;
- }
+ {
+ 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;
}
-\f
+
static void
rib_install_kernel (struct route_node *rn, struct rib *rib)
{
int ret = 0;
- struct nexthop *nexthop;
+ struct nexthop *nexthop, *tnexthop;
+ int recursing;
+ /*
+ * Make sure we update the FPM any time we send new information to
+ * the kernel.
+ */
+ zfpm_trigger_update (rn, "installing in kernel");
switch (PREFIX_FAMILY (&rn->p))
{
case AF_INET:
#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)
+ for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
}
}
rib_uninstall_kernel (struct route_node *rn, struct rib *rib)
{
int ret = 0;
- struct nexthop *nexthop;
+ struct nexthop *nexthop, *tnexthop;
+ int recursing;
+
+ /*
+ * Make sure we update the FPM any time we send new information to
+ * the kernel.
+ */
+ zfpm_trigger_update (rn, "uninstalling from kernel");
switch (PREFIX_FAMILY (&rn->p))
{
#endif /* HAVE_IPV6 */
}
- for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+ for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
return ret;
{
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
{
+ zfpm_trigger_update (rn, "rib_uninstall");
+
redistribute_delete (&rn->p, rib);
if (! RIB_SYSTEM_ROUTE (rib))
rib_uninstall_kernel (rn, rib);
static void rib_unlink (struct route_node *, struct rib *);
+/*
+ * rib_can_delete_dest
+ *
+ * Returns TRUE if the given dest can be deleted from the table.
+ */
+static int
+rib_can_delete_dest (rib_dest_t *dest)
+{
+ if (dest->routes)
+ {
+ return 0;
+ }
+
+ /*
+ * Don't delete the dest if we have to update the FPM about this
+ * prefix.
+ */
+ if (CHECK_FLAG (dest->flags, RIB_DEST_UPDATE_FPM) ||
+ CHECK_FLAG (dest->flags, RIB_DEST_SENT_TO_FPM))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * rib_gc_dest
+ *
+ * Garbage collect the rib dest corresponding to the given route node
+ * if appropriate.
+ *
+ * Returns TRUE if the dest was deleted, FALSE otherwise.
+ */
+int
+rib_gc_dest (struct route_node *rn)
+{
+ rib_dest_t *dest;
+ char buf[INET6_ADDRSTRLEN];
+
+ dest = rib_dest_from_rnode (rn);
+ if (!dest)
+ return 0;
+
+ if (!rib_can_delete_dest (dest))
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_RIB)
+ {
+ inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf));
+ zlog_debug ("%s: %s/%d: removing dest from table", __func__,
+ buf, rn->p.prefixlen);
+ }
+
+ dest->rnode = NULL;
+ XFREE (MTYPE_RIB_DEST, dest);
+ rn->info = NULL;
+
+ /*
+ * Release the one reference that we keep on the route node.
+ */
+ route_unlock_node (rn);
+ return 1;
+}
+
/* Core function for processing routing information base. */
-static wq_item_status
-rib_process (struct work_queue *wq, void *data)
+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;
- struct route_node *rn = data;
int installed = 0;
- struct nexthop *nexthop = NULL;
+ struct nexthop *nexthop = NULL, *tnexthop;
+ int recursing;
+ char buf[INET6_ADDRSTRLEN];
assert (rn);
- for (rib = rn->info; rib; rib = next)
+ if (IS_ZEBRA_DEBUG_RIB || IS_ZEBRA_DEBUG_RIB_Q)
+ inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN);
+
+ RNODE_FOREACH_RIB_SAFE (rn, rib, next)
{
- next = rib->next;
-
/* Currently installed rib. */
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
{
if (rib != fib)
{
if (IS_ZEBRA_DEBUG_RIB)
- zlog_debug ("%s: rn %p, removing rib %p", __func__, rn, rib);
- rib_unlink (rn, 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;
/* metric tie-breaks equal distance */
if (rib->metric <= select->metric)
select = rib;
- }
-
- /* Same route is selected. */
+ } /* RNODE_FOREACH_RIB_SAFE */
+
+ /* 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: Updating existing route, select %p, fib %p",
- __func__, select, fib);
+ 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))
{
+ zfpm_trigger_update (rn, "updating existing route");
+
redistribute_delete (&rn->p, select);
if (! RIB_SYSTEM_ROUTE (select))
rib_uninstall_kernel (rn, select);
This makes sure the routes are IN the kernel.
*/
- for (nexthop = select->nexthop; nexthop; nexthop = nexthop->next)
+ for (ALL_NEXTHOPS_RO(select->nexthop, nexthop, tnexthop, recursing))
+ if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
{
- if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
- installed = 1;
+ installed = 1;
+ break;
}
if (! installed)
rib_install_kernel (rn, select);
goto end;
}
- /* Uninstall old rib from forwarding table. */
+ /* 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: Removing existing route, fib %p", __func__, fib);
+ zlog_debug ("%s: %s/%d: Removing existing route, fib %p", __func__,
+ buf, rn->p.prefixlen, fib);
+
+ zfpm_trigger_update (rn, "removing existing route");
+
redistribute_delete (&rn->p, fib);
if (! RIB_SYSTEM_ROUTE (fib))
rib_uninstall_kernel (rn, fib);
nexthop_active_update (rn, fib, 1);
}
- /* Install new rib into forwarding table. */
+ /* 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: Adding route, select %p", __func__, select);
+ zlog_debug ("%s: %s/%d: Adding route, select %p", __func__, buf,
+ rn->p.prefixlen, select);
+
+ zfpm_trigger_update (rn, "new route selected");
+
/* Set real nexthop. */
nexthop_active_update (rn, select, 1);
if (del)
{
if (IS_ZEBRA_DEBUG_RIB)
- zlog_debug ("%s: Deleting fib %p, rn %p", __func__, del, rn);
+ 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: rn %p dequeued", __func__, rn);
- if (rn->info)
- UNSET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED);
- route_unlock_node (rn); /* rib queue lock */
- return WQ_SUCCESS;
+ zlog_debug ("%s: %s/%d: rn %p dequeued", __func__, buf, rn->p.prefixlen, rn);
+
+ /*
+ * Check if the dest can be deleted now.
+ */
+ rib_gc_dest (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)
+ UNSET_FLAG (rib_dest_from_rnode (rnode)->flags, 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;
+}
+
+/*
+ * All meta queues have been processed. Trigger next-hop evaluation.
+ */
+static void
+meta_queue_process_complete (struct work_queue *dummy)
+{
+ zebra_evaluate_rnh_table(0, AF_INET);
+#ifdef HAVE_IPV6
+ zebra_evaluate_rnh_table(0, AF_INET6);
+#endif /* HAVE_IPV6 */
+}
+
+/* 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);
+
+ RNODE_FOREACH_RIB (rn, rib)
+ {
+ u_char qindex = meta_queue_map[rib->type];
+
+ /* Invariant: at this point we always have rn->info set. */
+ if (CHECK_FLAG (rib_dest_from_rnode (rn)->flags,
+ 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 (rib_dest_from_rnode (rn)->flags, 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)
+ if (!rnode_to_ribs (rn))
{
zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
__func__, rn, rn->lock);
return;
}
- /* Route-table node already queued, so nothing to do */
- if (CHECK_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED))
- {
- if (IS_ZEBRA_DEBUG_RIB_Q)
- zlog_debug ("%s: rn %p already queued", __func__, rn);
- return;
- }
-
- route_lock_node (rn); /* rib queue lock */
-
if (IS_ZEBRA_DEBUG_RIB_Q)
- zlog_info ("%s: work queue added", __func__);
+ 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__);
- route_unlock_node (rn);
return;
}
-
- work_queue_add (zebra->ribq, rn);
- SET_FLAG (((struct rib *)rn->info)->rn_status, RIB_ROUTE_QUEUED);
+ /*
+ * 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: rn %p queued", __func__, rn);
+ 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)
}
/* fill in the work queue spec */
- zebra->ribq->spec.workfunc = &rib_process;
+ zebra->ribq->spec.workfunc = &meta_queue_process;
zebra->ribq->spec.errorfunc = NULL;
+ zebra->ribq->spec.completion_func = &meta_queue_process_complete;
/* 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;
}
* |-> 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.
+ * The 'info' pointer of a route_node points to a rib_dest_t
+ * ('dest'). Queueing state for a route_node is kept on the dest. The
+ * dest is created on-demand by rib_link() and is kept around at least
+ * as long as there are ribs hanging off it (@see rib_gc_dest()).
*
* Refcounting (aka "locking" throughout the GNU Zebra and Quagga code):
*
* - route_nodes: refcounted by:
- * - RIBs attached to route_node:
- * - managed by: rib_link/unlink
+ * - dest attached to route_node:
+ * - managed by: rib_link/rib_gc_dest
* - route_node processing queue
* - managed by: rib_addqueue, rib_process.
*
rib_link (struct route_node *rn, struct rib *rib)
{
struct rib *head;
-
+ rib_dest_t *dest;
+ 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);
+ }
- if (IS_ZEBRA_DEBUG_RIB)
- zlog_debug ("%s: rn %p, rib %p", __func__, rn, rib);
+ dest = rib_dest_from_rnode (rn);
+ if (!dest)
+ {
+ if (IS_ZEBRA_DEBUG_RIB)
+ {
+ zlog_debug ("%s: %s/%d: adding dest to table", __func__,
+ buf, rn->p.prefixlen);
+ }
+
+ dest = XCALLOC (MTYPE_RIB_DEST, sizeof (rib_dest_t));
+ route_lock_node (rn); /* rn route table reference */
+ rn->info = dest;
+ dest->rnode = rn;
+ }
- head = rn->info;
+ head = dest->routes;
if (head)
{
- if (IS_ZEBRA_DEBUG_RIB)
- zlog_debug ("%s: new head, rn_status copied over", __func__);
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;
+ dest->routes = rib;
rib_queue_add (&zebrad, rn);
}
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
{
if (IS_ZEBRA_DEBUG_RIB)
- zlog_debug ("%s: rn %p, un-removed rib %p",
- __func__, rn, 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);
}
+/*
+ * rib_unlink
+ *
+ * Detach a rib structure from a route_node.
+ *
+ * Note that a call to rib_unlink() should be followed by a call to
+ * rib_gc_dest() at some point. This allows a rib_dest_t that is no
+ * longer required to be deleted.
+ */
static void
rib_unlink (struct route_node *rn, struct rib *rib)
{
- struct nexthop *nexthop, *next;
+ char buf[INET6_ADDRSTRLEN];
+ rib_dest_t *dest;
assert (rn && rib);
if (IS_ZEBRA_DEBUG_RIB)
- zlog_debug ("%s: rn %p, rib %p",
- __func__, rn, 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);
+ }
+
+ dest = rib_dest_from_rnode (rn);
if (rib->next)
rib->next->prev = rib->prev;
rib->prev->next = rib->next;
else
{
- rn->info = rib->next;
-
- if (rn->info)
- {
- if (IS_ZEBRA_DEBUG_RIB)
- zlog_debug ("%s: rn %p, rib %p, new head copy",
- __func__, rn, rib);
- rib->next->rn_status = rib->rn_status;
- }
+ dest->routes = rib->next;
}
/* free RIB and nexthops */
- for (nexthop = rib->nexthop; nexthop; nexthop = next)
- {
- next = nexthop->next;
- nexthop_free (nexthop);
- }
+ nexthops_free(rib->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)
- zlog_debug ("%s: rn %p, rib %p, removing", __func__, rn, 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);
}
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)
+ u_int32_t metric, u_char distance, safi_t safi)
{
struct rib *rib;
struct rib *same = NULL;
struct nexthop *nexthop;
/* Lookup table. */
- table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
+ table = vrf_table (AFI_IP, safi, 0);
if (! table)
return 0;
/* Set default distance by route type. */
if (distance == 0)
{
- distance = route_info[type].distance;
+ if ((unsigned)type >= array_size(route_info))
+ distance = 150;
+ else
+ distance = route_info[type].distance;
/* iBGP distance is 200. */
if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP))
/* If same type of route are installed, treat it as a implicit
withdraw. */
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
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,
+ union prefix46constptr pp, const struct rib * rib)
+{
+ const struct prefix *p = pp.p;
+ char straddr[INET6_ADDRSTRLEN];
+ struct nexthop *nexthop, *tnexthop;
+ int recursing;
+
+ inet_ntop (p->family, &p->u.prefix, straddr, INET6_ADDRSTRLEN);
+ zlog_debug ("%s: dumping RIB entry %p for %s/%d", func, rib, straddr, 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 (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+ {
+ inet_ntop (p->family, &nexthop->gate, straddr, INET6_ADDRSTRLEN);
+ zlog_debug
+ (
+ "%s: %s %s with flags %s%s%s",
+ func,
+ (recursing ? " NH" : "NH"),
+ straddr,
+ (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 */
+ RNODE_FOREACH_RIB (rn, rib)
+ {
+ 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 (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.
+ */
+ RNODE_FOREACH_RIB (rn, rib)
+ {
+ 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 (&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)
+rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi)
{
struct route_table *table;
struct route_node *rn;
struct nexthop *nexthop;
/* Lookup table. */
- table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
+ table = vrf_table (AFI_IP, safi, 0);
if (! table)
return 0;
+
/* Make it sure prefixlen is applied to the prefix. */
apply_mask_ipv4 (p);
/* If same type of route are installed, treat it as a implicit
withdraw. */
- for (same = rn->info; same; same = same->next)
+ RNODE_FOREACH_RIB (rn, same)
{
if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED))
continue;
/* 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 (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 (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)
+ 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[BUFSIZ];
- char buf2[BUFSIZ];
+ struct nexthop *nexthop, *tnexthop;
+ int recursing;
+ char buf1[INET_ADDRSTRLEN];
+ char buf2[INET_ADDRSTRLEN];
/* Lookup table. */
- table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
+ 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, BUFSIZ),
- p->prefixlen,
- inet_ntoa (*gate),
- ifindex);
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ {
+ if (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);
+ else
+ zlog_debug ("rib_delete_ipv4(): route delete %s/%d ifindex %d",
+ inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
+ p->prefixlen,
+ ifindex);
+ }
/* Lookup route node. */
rn = route_node_lookup (table, (struct prefix *) p);
{
if (gate)
zlog_debug ("route %s/%d via %s ifindex %d doesn't exist in rib",
- inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ),
+ inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
- inet_ntop (AF_INET, gate, buf2, BUFSIZ),
+ 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, BUFSIZ),
+ inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
ifindex);
}
}
/* Lookup same type route. */
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type != type)
continue;
if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) &&
- nexthop->type == NEXTHOP_TYPE_IFINDEX && nexthop->ifindex == ifindex)
+ nexthop->type == NEXTHOP_TYPE_IFINDEX)
{
+ if (nexthop->ifindex != ifindex)
+ continue;
if (rib->refcnt)
{
rib->refcnt--;
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))))
+ else
{
- same = rib;
- break;
- }
+ if (gate == NULL)
+ {
+ same = rib;
+ break;
+ }
+ for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+ if (IPV4_ADDR_SAME (&nexthop->gate.ipv4, gate))
+ {
+ same = rib;
+ break;
+ }
+ if (same)
+ break;
+ }
}
-
/* If same type of route can't be found and this message is from
kernel. */
if (! same)
{
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, BUFSIZ),
+ inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
- inet_ntop (AF_INET, gate, buf2, BUFSIZ),
+ 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, BUFSIZ),
+ inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN),
p->prefixlen,
ifindex,
type);
route_unlock_node (rn);
return 0;
}
-\f
+
/* Install static route into rib. */
static void
static_install_ipv4 (struct prefix *p, struct static_ipv4 *si)
/* Lookup existing route */
rn = route_node_get (table, p);
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
rib->type = ZEBRA_ROUTE_STATIC;
rib->distance = si->distance;
rib->metric = 0;
+ rib->table = zebrad.rtm_table_default;
rib->nexthop_num = 0;
switch (si->type)
if (! rn)
return;
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
static_delete_ipv4 (p, gate, ifname, update->distance, vrf_id);
/* Make new static route structure. */
- si = XMALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4));
- memset (si, 0, sizeof (struct static_ipv4));
+ si = XCALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4));
si->type = type;
si->distance = distance;
return 1;
}
-\f
+
#ifdef HAVE_IPV6
-static int
+int
rib_bogus_ipv6 (int type, struct prefix_ipv6 *p,
struct in6_addr *gate, unsigned int ifindex, int table)
{
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)
+ u_int32_t metric, u_char distance, safi_t safi)
{
struct rib *rib;
struct rib *same = NULL;
struct nexthop *nexthop;
/* Lookup table. */
- table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
+ table = vrf_table (AFI_IP6, safi, 0);
if (! table)
return 0;
/* If same type of route are installed, treat it as a implicit
withdraw. */
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
/* 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 (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 (p, same);
+ }
rib_delnode (rn, same);
+ }
route_unlock_node (rn);
return 0;
}
+int
+rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi,
+ unsigned long ifindex)
+{
+ struct route_table *table;
+ struct route_node *rn;
+ struct rib *same = NULL;
+ struct nexthop *nexthop;
+ int ret = 0;
+ int table_id = 0;
+
+ if (rib)
+ table_id = rib->table;
+ else
+ return 0; /* why are we getting called with NULL rib */
+
+ /* 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 (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. */
+ RNODE_FOREACH_RIB (rn, same) {
+ if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED)) {
+ continue;
+ }
+ if (same->type != rib->type) {
+ continue;
+ }
+
+ if (same->table != rib->table) {
+ continue;
+ }
+ if (same->type != ZEBRA_ROUTE_CONNECT) {
+ break;
+ }
+ else if ((nexthop = same->nexthop) &&
+ nexthop->type == NEXTHOP_TYPE_IFINDEX &&
+ nexthop->ifindex == ifindex) {
+ same->refcnt++;
+ return 0;
+ }
+ }
+
+ /* 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);
+ ret = 1;
+ /* 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 ((struct prefix *)p, same);
+ }
+ rib_delnode (rn, same);
+ ret = -1;
+ }
+
+ route_unlock_node (rn);
+ return ret;
+}
+
/* 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)
+ 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[BUFSIZ];
- char buf2[BUFSIZ];
+ struct nexthop *nexthop, *tnexthop;
+ int recursing;
+ char buf1[INET6_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
/* Apply mask. */
apply_mask_ipv6 (p);
/* Lookup table. */
- table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
+ table = vrf_table (AFI_IP6, safi, 0);
if (! table)
return 0;
{
if (gate)
zlog_debug ("route %s/%d via %s ifindex %d doesn't exist in rib",
- inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ),
+ inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
p->prefixlen,
- inet_ntop (AF_INET6, gate, buf2, BUFSIZ),
+ 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, BUFSIZ),
+ inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
p->prefixlen,
ifindex);
}
}
/* Lookup same type route. */
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED))
continue;
if (rib->type != type)
continue;
if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) &&
- nexthop->type == NEXTHOP_TYPE_IFINDEX && nexthop->ifindex == ifindex)
+ nexthop->type == NEXTHOP_TYPE_IFINDEX)
{
+ if (nexthop->ifindex != ifindex)
+ continue;
if (rib->refcnt)
{
rib->refcnt--;
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;
- }
+ else
+ {
+ if (gate == NULL)
+ {
+ same = rib;
+ break;
+ }
+ for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing))
+ if (IPV6_ADDR_SAME (&nexthop->gate.ipv6, gate))
+ {
+ same = rib;
+ break;
+ }
+ if (same)
+ break;
+ }
}
/* If same type of route can't be found and this message is from
{
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, BUFSIZ),
+ inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
p->prefixlen,
- inet_ntop (AF_INET6, gate, buf2, BUFSIZ),
+ 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, BUFSIZ),
+ inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN),
p->prefixlen,
ifindex,
type);
route_unlock_node (rn);
return 0;
}
-\f
+
/* Install static route into rib. */
static void
static_install_ipv6 (struct prefix *p, struct static_ipv6 *si)
/* Lookup existing route */
rn = route_node_get (table, p);
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED))
continue;
rib->type = ZEBRA_ROUTE_STATIC;
rib->distance = si->distance;
rib->metric = 0;
+ rib->table = zebrad.rtm_table_default;
rib->nexthop_num = 0;
switch (si->type)
if (! rn)
return;
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
}
/* Make new static route structure. */
- si = XMALLOC (MTYPE_STATIC_IPV6, sizeof (struct static_ipv6));
- memset (si, 0, sizeof (struct static_ipv6));
+ si = XCALLOC (MTYPE_STATIC_IPV6, sizeof (struct static_ipv6));
si->type = type;
si->distance = distance;
return 1;
}
#endif /* HAVE_IPV6 */
-\f
+
/* RIB update function. */
void
rib_update (void)
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
- if (rn->info)
+ if (rnode_to_ribs (rn))
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)
+ if (rnode_to_ribs (rn))
rib_queue_add (&zebrad, rn);
}
-/* Interface goes up. */
-static void
-rib_if_up (struct interface *ifp)
-{
- rib_update ();
-}
-/* Interface goes down. */
-static void
-rib_if_down (struct interface *ifp)
-{
- rib_update ();
-}
-\f
/* Remove all routes which comes from non main table. */
static void
rib_weed_table (struct route_table *table)
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
- for (rib = rn->info; rib; rib = next)
+ RNODE_FOREACH_RIB_SAFE (rn, rib, next)
{
- next = rib->next;
-
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
rib_weed_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_weed_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
-\f
+
/* Delete self installed routes after zebra is relaunched. */
static void
rib_sweep_table (struct route_table *table)
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
- for (rib = rn->info; rib; rib = next)
+ RNODE_FOREACH_RIB_SAFE (rn, rib, next)
{
- next = rib->next;
-
if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
continue;
rib_sweep_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_sweep_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
-\f
+
+/* 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))
+ RNODE_FOREACH_RIB_SAFE (rn, 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)
if (table)
for (rn = route_top (table); rn; rn = route_next (rn))
- for (rib = rn->info; rib; rib = rib->next)
+ RNODE_FOREACH_RIB (rn, rib)
{
- if (! RIB_SYSTEM_ROUTE (rib)
- && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
- rib_uninstall_kernel (rn, rib);
+ if (!CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
+ continue;
+
+ zfpm_trigger_update (rn, NULL);
+
+ if (! RIB_SYSTEM_ROUTE (rib))
+ rib_uninstall_kernel (rn, rib);
}
}
rib_close_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_close_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
-\f
+
/* Routing information base initialize. */
void
rib_init (void)
/* VRF initialization. */
vrf_init ();
}
+
+/*
+ * vrf_id_get_next
+ *
+ * Get the first vrf id that is greater than the given vrf id if any.
+ *
+ * Returns TRUE if a vrf id was found, FALSE otherwise.
+ */
+static inline int
+vrf_id_get_next (uint32_t id, uint32_t *next_id_p)
+{
+ while (++id < vector_active (vrf_vector))
+ {
+ if (vrf_lookup (id))
+ {
+ *next_id_p = id;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * rib_tables_iter_next
+ *
+ * Returns the next table in the iteration.
+ */
+struct route_table *
+rib_tables_iter_next (rib_tables_iter_t *iter)
+{
+ struct route_table *table;
+
+ /*
+ * Array that helps us go over all AFI/SAFI combinations via one
+ * index.
+ */
+ static struct {
+ afi_t afi;
+ safi_t safi;
+ } afi_safis[] = {
+ { AFI_IP, SAFI_UNICAST },
+ { AFI_IP, SAFI_MULTICAST },
+ { AFI_IP6, SAFI_UNICAST },
+ { AFI_IP6, SAFI_MULTICAST },
+ };
+
+ table = NULL;
+
+ switch (iter->state)
+ {
+
+ case RIB_TABLES_ITER_S_INIT:
+ iter->vrf_id = 0;
+ iter->afi_safi_ix = -1;
+
+ /* Fall through */
+
+ case RIB_TABLES_ITER_S_ITERATING:
+ iter->afi_safi_ix++;
+ while (1)
+ {
+
+ while (iter->afi_safi_ix < (int) ZEBRA_NUM_OF (afi_safis))
+ {
+ table = vrf_table (afi_safis[iter->afi_safi_ix].afi,
+ afi_safis[iter->afi_safi_ix].safi,
+ iter->vrf_id);
+ if (table)
+ break;
+
+ iter->afi_safi_ix++;
+ }
+
+ /*
+ * Found another table in this vrf.
+ */
+ if (table)
+ break;
+
+ /*
+ * Done with all tables in the current vrf, go to the next
+ * one.
+ */
+ if (!vrf_id_get_next (iter->vrf_id, &iter->vrf_id))
+ break;
+
+ iter->afi_safi_ix = 0;
+ }
+
+ break;
+
+ case RIB_TABLES_ITER_S_DONE:
+ return NULL;
+ }
+
+ if (table)
+ iter->state = RIB_TABLES_ITER_S_ITERATING;
+ else
+ iter->state = RIB_TABLES_ITER_S_DONE;
+
+ return table;
+}