summaryrefslogtreecommitdiff
path: root/zebra/zebra_rib.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/zebra_rib.c')
-rw-r--r--zebra/zebra_rib.c240
1 files changed, 235 insertions, 5 deletions
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 7a63c1c1..f61cbe31 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -631,6 +631,82 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p)
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)
@@ -694,6 +770,16 @@ rib_match_ipv6 (struct in6_addr *addr)
#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 int
nexthop_active_check (struct route_node *rn, struct rib *rib,
struct nexthop *nexthop, int set)
@@ -839,6 +925,7 @@ rib_install_kernel (struct route_node *rn, struct rib *rib)
#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)
@@ -860,6 +947,8 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib)
break;
#ifdef HAVE_IPV6
case AF_INET6:
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug ("%s: calling kernel_delete_ipv4 (%p, %p)", __func__, rn, rib);
ret = kernel_delete_ipv6 (&rn->p, rib);
break;
#endif /* HAVE_IPV6 */
@@ -907,6 +996,9 @@ rib_process (struct work_queue *wq, void *data)
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. */
@@ -983,9 +1075,16 @@ rib_process (struct work_queue *wq, void *data)
/* metric tie-breaks equal distance */
if (rib->metric <= select->metric)
select = rib;
- }
-
- /* Same route is selected. */
+ } /* 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)
@@ -1024,7 +1123,10 @@ rib_process (struct work_queue *wq, void *data)
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)
@@ -1039,7 +1141,10 @@ rib_process (struct work_queue *wq, void *data)
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)
@@ -1382,16 +1487,127 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p,
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 == %u, type == %u, table == %d",
+ func,
+ rib->refcnt,
+ 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);
+ }
+}
+
int
rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
{
@@ -1440,10 +1656,24 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib)
/* 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;