}
/* Add nexthop to the end of a nexthop list. */
-static void
+void
_nexthop_add (struct nexthop **target, struct nexthop *nexthop)
{
struct nexthop *last;
rib->nexthop_num++;
}
+static void
+_copy_nexthops (struct nexthop **tnh, struct nexthop *nh)
+{
+ struct nexthop *nexthop;
+ struct nexthop *nh1;
+
+ for (nh1 = nh; nh1; nh1 = nh1->next)
+ {
+ nexthop = nexthop_new();
+ nexthop->flags = nh->flags;
+ nexthop->type = nh->type;
+ nexthop->ifindex = nh->ifindex;
+ if (nh->ifname)
+ nexthop->ifname = XSTRDUP(0, nh->ifname);
+ memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr));
+ memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr));
+ _nexthop_add(tnh, nexthop);
+
+ if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE))
+ _copy_nexthops(&nexthop->resolved, nh1->resolved);
+ }
+}
+
+/**
+ * copy_nexthop - copy a nexthop to the rib structure.
+ */
+void
+copy_nexthops (struct rib *rib, struct nexthop *nh)
+{
+ struct nexthop *nexthop;
+
+ nexthop = nexthop_new();
+ nexthop->flags = nh->flags;
+ nexthop->type = nh->type;
+ nexthop->ifindex = nh->ifindex;
+ if (nh->ifname)
+ nexthop->ifname = XSTRDUP(0, nh->ifname);
+ memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr));
+ memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr));
+ nexthop_add(rib, nexthop);
+ if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_RECURSIVE))
+ _copy_nexthops(&nexthop->resolved, nh->resolved);
+}
+
/* Delete specified nexthop from the list. */
static void
nexthop_delete (struct rib *rib, struct nexthop *nexthop)
/* Free nexthop. */
void
-nexthop_free (struct nexthop *nexthop)
+nexthop_free (struct nexthop *nexthop, struct route_node *rn)
{
if (nexthop->ifname)
XFREE (0, nexthop->ifname);
if (nexthop->resolved)
- nexthops_free(nexthop->resolved);
+ nexthops_free(nexthop->resolved, rn);
XFREE (MTYPE_NEXTHOP, nexthop);
}
/* Frees a list of nexthops */
void
-nexthops_free (struct nexthop *nexthop)
+nexthops_free (struct nexthop *nexthop, struct route_node *rn)
{
struct nexthop *nh, *next;
+ struct prefix nh_p;
for (nh = nexthop; nh; nh = next)
{
next = nh->next;
- nexthop_free (nh);
+ if (nh->type == NEXTHOP_TYPE_IPV4)
+ {
+ nh_p.family = AF_INET;
+ nh_p.prefixlen = IPV4_MAX_BITLEN;
+ nh_p.u.prefix4 = nh->gate.ipv4;
+ }
+ else if (nh->type == NEXTHOP_TYPE_IPV6)
+ {
+ nh_p.family = AF_INET6;
+ nh_p.prefixlen = IPV6_MAX_BITLEN;
+ nh_p.u.prefix6 = nh->gate.ipv6;
+ }
+ zebra_deregister_rnh_static_nh(&nh_p, rn);
+ nexthop_free (nh, rn);
}
}
struct route_node *rn;
struct rib *match;
int resolved;
- struct nexthop *newhop;
+ struct nexthop *newhop, *tnewhop;
struct nexthop *resolved_hop;
+ int recursing = 0;
if (nexthop->type == NEXTHOP_TYPE_IPV4)
nexthop->ifindex = 0;
if (set)
{
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
- nexthops_free(nexthop->resolved);
+ nexthops_free(nexthop->resolved, top);
nexthop->resolved = NULL;
}
+ /* Skip nexthops that have been filtered out due to route-map */
+ /* The nexthops are specific to this route and so the same */
+ /* nexthop for a different route may not have this flag set */
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED))
+ return 0;
+
/* Make lookup prefix. */
memset (&p, 0, sizeof (struct prefix_ipv4));
p.family = AF_INET;
/* If there is no selected route or matched route is EGP, go up
tree. */
- if (! match
- || match->type == ZEBRA_ROUTE_BGP)
+ if (! match)
{
do {
rn = rn->parent;
if (set)
{
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+ SET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
resolved_hop = XCALLOC(MTYPE_NEXTHOP, sizeof (struct nexthop));
SET_FLAG (resolved_hop->flags, NEXTHOP_FLAG_ACTIVE);
resolved_hop->ifindex = newhop->ifindex;
}
+ _nexthop_add(&nexthop->resolved, resolved_hop);
+ }
+ resolved = 1;
+ }
+ return resolved;
+ }
+ else if (rib->type == ZEBRA_ROUTE_STATIC)
+ {
+ resolved = 0;
+ for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
+ if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
+ {
+ if (set)
+ {
+ SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
+
+ 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)
+ {
+ 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);
}
resolved = 1;
struct route_node *rn;
struct rib *match;
int resolved;
- struct nexthop *newhop;
+ struct nexthop *newhop, *tnewhop;
+ int recursing = 0;
struct nexthop *resolved_hop;
if (nexthop->type == NEXTHOP_TYPE_IPV6)
if (set)
{
UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
- nexthops_free(nexthop->resolved);
+ nexthops_free(nexthop->resolved, top);
nexthop->resolved = NULL;
}
+ /* Skip nexthops that have been filtered out due to route-map */
+ /* The nexthops are specific to this route and so the same */
+ /* nexthop for a different route may not have this flag set */
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED))
+ return 0;
+
/* Make lookup prefix. */
memset (&p, 0, sizeof (struct prefix_ipv6));
p.family = AF_INET6;
/* If there is no selected route or matched route is EGP, go up
tree. */
- if (! match
- || match->type == ZEBRA_ROUTE_BGP)
+ if (! match)
{
do {
rn = rn->parent;
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);
+ SET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+
+ 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)
+ {
+ 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)
+ {
+ 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);
+ }
+ resolved = 1;
+ }
+ return resolved;
+ }
+ else if (rib->type == ZEBRA_ROUTE_STATIC)
+ {
+ resolved = 0;
+ for (ALL_NEXTHOPS_RO(match->nexthop, newhop, tnewhop, recursing))
+ if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB))
{
if (set)
{
/* If there is no selected route or matched route is EGP, go up
tree. */
- if (! match
- || match->type == ZEBRA_ROUTE_BGP)
+ if (! match)
{
do {
rn = rn->parent;
break;
}
- if (! match || match->type == ZEBRA_ROUTE_BGP)
+ if (! match)
return NULL;
if (match->type == ZEBRA_ROUTE_CONNECT)
/* If there is no selected route or matched route is EGP, go up
tree. */
- if (! match
- || match->type == ZEBRA_ROUTE_BGP)
+ if (! match)
{
do {
rn = rn->parent;
nexthop_active_update (struct route_node *rn, struct rib *rib, int set)
{
struct nexthop *nexthop;
- unsigned int prev_active, prev_index, new_active;
+ unsigned int prev_active, prev_index, new_active, old_num_nh;
+
+ old_num_nh = rib->nexthop_active_num;
rib->nexthop_active_num = 0;
UNSET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
rib->nexthop_active_num++;
if (prev_active != new_active ||
prev_index != nexthop->ifindex)
- SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
+ {
+ SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
+ SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+ }
}
+
+ if (old_num_nh != rib->nexthop_active_num)
+ SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED);
+
+ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_CHANGED))
+ {
+ SET_FLAG (rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+ }
+
return rib->nexthop_active_num;
}
RNODE_FOREACH_RIB_SAFE (rn, rib, next)
{
+ UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+
/* Currently installed rib. */
if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
{
}
/* Skip unreachable nexthop. */
- if (! nexthop_active_update (rn, rib, 0))
+ /* With static routes that may have recursive nexthops, calling
+ * nexthop_active_update will clear the ZEBRA_FLAG_CHANGED flag
+ * as the top level NH may not have changed. Those flags are set
+ * by the NHT evaluation. So, we skip an active_update_check here
+ * for static routes as its job has already been done.
+ */
+ if (rib->type != ZEBRA_ROUTE_STATIC &&
+ ! nexthop_active_update (rn, rib, 0))
continue;
/* Infinit distance. */
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);
- }
+ /* Need to check if any NHs are active to clear the
+ * the selected flag
+ */
+ if (nexthop_active_update (rn, select, 1))
+ {
+ if (! RIB_SYSTEM_ROUTE (select))
+ rib_install_kernel (rn, select);
+ redistribute_add (&rn->p, select);
+ }
+ else
+ {
+ UNSET_FLAG (select->flags, ZEBRA_FLAG_SELECTED);
+ }
+ }
else if (! RIB_SYSTEM_ROUTE (select))
{
/* Housekeeping code to deal with
zfpm_trigger_update (rn, "new route selected");
/* 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);
+ if (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 */
}
/* Add route_node to work queue and schedule processing */
-static void
+void
rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
{
char buf[INET_ADDRSTRLEN];
}
/* free RIB and nexthops */
- nexthops_free(rib->nexthop);
+ nexthops_free(rib->nexthop, rn);
XFREE (MTYPE_RIB, rib);
}
struct rib *rib;
struct route_node *rn;
struct route_table *table;
+ struct prefix nh_p;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
{
case STATIC_IPV4_GATEWAY:
nexthop_ipv4_add (rib, &si->gate.ipv4, NULL);
+ nh_p.family = AF_INET;
+ nh_p.prefixlen = IPV4_MAX_BITLEN;
+ nh_p.u.prefix4 = si->gate.ipv4;
+ zebra_register_rnh_static_nh(&nh_p, rn);
break;
case STATIC_IPV4_IFNAME:
nexthop_ifname_add (rib, si->gate.ifname);
{
case STATIC_IPV4_GATEWAY:
nexthop_ipv4_add (rib, &si->gate.ipv4, NULL);
+ nh_p.family = AF_INET;
+ nh_p.prefixlen = IPV4_MAX_BITLEN;
+ nh_p.u.prefix4 = si->gate.ipv4;
+ zebra_register_rnh_static_nh(&nh_p, rn);
break;
case STATIC_IPV4_IFNAME:
nexthop_ifname_add (rib, si->gate.ifname);
struct rib *rib;
struct nexthop *nexthop;
struct route_table *table;
+ struct prefix nh_p;
/* Lookup table. */
table = vrf_table (AFI_IP, SAFI_UNICAST, 0);
{
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
rib_uninstall (rn, rib);
+
+ nh_p.family = AF_INET;
+ nh_p.prefixlen = IPV4_MAX_BITLEN;
+ nh_p.u.prefix4 = nexthop->gate.ipv4;
nexthop_delete (rib, nexthop);
- nexthop_free (nexthop);
+ zebra_deregister_rnh_static_nh(&nh_p, rn);
+ nexthop_free (nexthop, rn);
rib_queue_add (&zebrad, rn);
}
/* Unlock node. */
struct rib *same = NULL;
struct nexthop *nexthop;
int ret = 0;
- int table_id = 0;
- if (rib)
- table_id = rib->table;
- else
+ if (!rib)
return 0; /* why are we getting called with NULL rib */
/* Lookup table. */
struct rib *rib;
struct route_table *table;
struct route_node *rn;
+ struct prefix nh_p;
/* Lookup table. */
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
{
case STATIC_IPV6_GATEWAY:
nexthop_ipv6_add (rib, &si->ipv6);
+ nh_p.family = AF_INET6;
+ nh_p.prefixlen = IPV6_MAX_BITLEN;
+ nh_p.u.prefix6 = si->ipv6;
+ zebra_register_rnh_static_nh(&nh_p, rn);
break;
case STATIC_IPV6_IFNAME:
nexthop_ifname_add (rib, si->ifname);
{
case STATIC_IPV6_GATEWAY:
nexthop_ipv6_add (rib, &si->ipv6);
+ nh_p.family = AF_INET6;
+ nh_p.prefixlen = IPV6_MAX_BITLEN;
+ nh_p.u.prefix6 = si->ipv6;
+ zebra_register_rnh_static_nh(&nh_p, rn);
break;
case STATIC_IPV6_IFNAME:
nexthop_ifname_add (rib, si->ifname);
struct route_node *rn;
struct rib *rib;
struct nexthop *nexthop;
+ struct prefix nh_p;
/* Lookup table. */
table = vrf_table (AFI_IP6, SAFI_UNICAST, 0);
{
if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))
rib_uninstall (rn, rib);
+
+ nh_p.family = AF_INET6;
+ nh_p.prefixlen = IPV6_MAX_BITLEN;
+ nh_p.u.prefix6 = nexthop->gate.ipv6;
nexthop_delete (rib, nexthop);
- nexthop_free (nexthop);
+ zebra_deregister_rnh_static_nh(&nh_p, rn);
+ nexthop_free (nexthop, rn);
rib_queue_add (&zebrad, rn);
}
/* Unlock node. */
t; \
})
-static void free_state(struct rib *rib);
-static void copy_state(struct rnh *rnh, struct rib *rib);
+/* Default rtm_table for all clients */
+extern struct zebra_t zebrad;
+
+static void free_state(struct rib *rib, struct route_node *rn);
+static void copy_state(struct rnh *rnh, struct rib *rib, struct route_node *rn);
static int compare_state(struct rib *r1, struct rib *r2);
static int send_client(struct rnh *rnh, struct zserv *client);
static void print_rnh(struct route_node *rn, struct vty *vty);
{
rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh));
rnh->client_list = list_new();
+ rnh->zebra_static_route_list = list_new();
route_lock_node (rn);
rn->info = rnh;
rnh->node = rn;
}
list_free(rnh->client_list);
- free_state(rnh->state);
+ list_free(rnh->zebra_static_route_list);
+ free_state(rnh->state, rn);
XFREE(MTYPE_RNH, rn->info);
rn->info = NULL;
route_unlock_node (rn);
rnh_str(rnh, buf, INET6_ADDRSTRLEN));
}
listnode_delete(rnh->client_list, client);
- if (list_isempty(rnh->client_list))
+ if (list_isempty(rnh->client_list) &&
+ list_isempty(rnh->zebra_static_route_list))
+ zebra_delete_rnh(rnh);
+}
+
+void
+zebra_register_rnh_static_nh(struct prefix *nh, struct route_node *static_rn)
+{
+ struct rnh *rnh;
+
+ rnh = zebra_add_rnh(nh, 0);
+ if (rnh && !listnode_lookup(rnh->zebra_static_route_list, static_rn))
+ {
+ listnode_add(rnh->zebra_static_route_list, static_rn);
+ }
+}
+
+void
+zebra_deregister_rnh_static_nh(struct prefix *nh, struct route_node *static_rn)
+{
+ struct rnh *rnh;
+
+ rnh = zebra_lookup_rnh(nh, 0);
+ if (!rnh)
+ return;
+
+ listnode_delete(rnh->zebra_static_route_list, static_rn);
+
+ if (list_isempty(rnh->client_list) &&
+ list_isempty(rnh->zebra_static_route_list))
zebra_delete_rnh(rnh);
}
+static int
+zebra_evaluate_rnh_nexthops(int family, struct rib *rib, struct route_node *prn,
+ int proto)
+{
+ int at_least_one = 0;
+ int rmap_family; /* Route map has diff AF family enum */
+ struct nexthop *nexthop;
+ int ret;
+
+ rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6;
+
+ if (prn && rib)
+ {
+ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
+ {
+ ret = zebra_nht_route_map_check(rmap_family, proto, &prn->p, rib,
+ nexthop);
+ if (ret != RMAP_DENYMATCH)
+ {
+ SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ at_least_one++; /* at least one valid NH */
+ }
+ else
+ {
+ UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
+ }
+ }
+ }
+ return (at_least_one);
+}
+
int
zebra_evaluate_rnh_table (int vrfid, int family, int force)
{
struct rnh *rnh;
struct zserv *client;
struct listnode *node;
- struct rib *rib;
- int rmap_family; /* Route map has diff AF family enum */
- route_map_result_t ret = RMAP_MATCH;
- struct nexthop *nexthop;
+ struct rib *rib, *srib;
int state_changed = 0;
int at_least_one = 0;
char bufn[INET6_ADDRSTRLEN];
char bufp[INET6_ADDRSTRLEN];
-
- rmap_family = (family == AF_INET) ? AFI_IP : AFI_IP6;
-
+ char bufs[INET6_ADDRSTRLEN];
+ struct route_node *static_rn;
+ struct nexthop *nexthop;
ntable = lookup_rnh_table(vrfid, family);
if (!ntable)
{
continue;
rnh = nrn->info;
+ at_least_one = 0;
+
prn = route_node_match(ptable, &nrn->p);
if (!prn)
rib = NULL;
if (compare_state(rib, rnh->state))
{
- copy_state(rnh, rib);
+ if (rib)
+ UNSET_FLAG(rib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+
+ copy_state(rnh, rib, nrn);
state_changed = 1;
}
prefix2str(&prn->p, bufp, INET6_ADDRSTRLEN);
else
strcpy(bufp, "null");
+
+ zlog_debug("%s: State changed for %s/%s", __FUNCTION__, bufn, bufp);
+
}
/* Notify registered clients */
+ rib = rnh->state;
+
if (state_changed || force)
- for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
- {
- rib = rnh->state;
- if (prn && rib)
- {
- at_least_one = 0;
- for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
- {
- ret = zebra_nht_route_map_check(rmap_family, client->proto,
- &prn->p, rib, nexthop);
- if (ret == RMAP_DENYMATCH)
- {
- UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- }
- else
- {
- SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE);
- at_least_one++; /* at least one valid NH */
- }
- }
- if (at_least_one)
- rnh->filtered[client->proto] = 0;
- else
- rnh->filtered[client->proto] = 1;
-
- if (IS_ZEBRA_DEBUG_NHT && (state_changed || force))
- zlog_debug("%srnh %s resolved through route %s - sending "
- "nexthop %s event to clients",
- at_least_one ? "":"(filtered)", bufn, bufp,
- rib ? "reachable" : "unreachable");
-
- send_client(rnh, client); /* Route-map passed */
- }
- else if (state_changed)
- {
- if (IS_ZEBRA_DEBUG_NHT && (state_changed || force))
- zlog_debug("rnh %s resolved through route %s - sending "
- "nexthop %s event to clients", bufn, bufp,
- rib ? "reachable" : "unreachable");
+ {
+ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
+ {
+ if (prn && rib)
+ {
+ at_least_one = zebra_evaluate_rnh_nexthops(family, rib, prn,
+ client->proto);
+ if (at_least_one)
+ rnh->filtered[client->proto] = 0;
+ else
+ rnh->filtered[client->proto] = 1;
+ }
+ else if (state_changed)
rnh->filtered[client->proto] = 0;
- send_client(rnh, client);
- /* We don't need to send a RNH update on force since no
- * update really happened, just a route-map change.
- */
- }
- }
+
+ if (IS_ZEBRA_DEBUG_NHT && (state_changed || force))
+ zlog_debug("%srnh %s resolved through route %s - sending "
+ "nexthop %s event to clients",
+ at_least_one ? "":"(filtered)", bufn, bufp,
+ rib ? "reachable" : "unreachable");
+
+ send_client(rnh, client); /* Route-map passed */
+ }
+
+ /* Now evaluate static client */
+ if (prn && rib)
+ {
+ at_least_one = zebra_evaluate_rnh_nexthops(family, rib, prn,
+ ZEBRA_ROUTE_STATIC);
+ if (at_least_one)
+ rnh->filtered[ZEBRA_ROUTE_STATIC] = 0;
+ else
+ rnh->filtered[ZEBRA_ROUTE_STATIC] = 1;
+ }
+ else if (state_changed)
+ rnh->filtered[ZEBRA_ROUTE_STATIC] = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(rnh->zebra_static_route_list, node,
+ static_rn))
+ {
+ RNODE_FOREACH_RIB(static_rn, srib)
+ {
+ break; /* pick the first and only(?) rib for static */
+ }
+
+ if (!srib)
+ {
+ if (IS_ZEBRA_DEBUG_NHT)
+ {
+ prefix2str(&static_rn->p, bufs, INET6_ADDRSTRLEN);
+ zlog_debug("%s: Unable to find RIB for static route %s, skipping NH resolution",
+ __FUNCTION__, bufs);
+ continue;
+ }
+ }
+
+ /* Mark the appropriate static route's NH as filtered */
+ for (nexthop = srib->nexthop; nexthop; nexthop = nexthop->next)
+ {
+ switch (nexthop->type)
+ {
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ /* Don't see a use case for *_IFNAME */
+ if (nexthop->gate.ipv4.s_addr == nrn->p.u.prefix4.s_addr)
+ {
+ if (at_least_one)
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED);
+ else
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED);
+ }
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ /* Don't see a use case for *_IFNAME */
+ if (memcmp(&nexthop->gate.ipv6,&nrn->p.u.prefix6, 16) == 0)
+ {
+ if (at_least_one)
+ UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED);
+ else
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FILTERED);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (IS_ZEBRA_DEBUG_NHT && (state_changed || force))
+ zlog_debug("%srnh %s resolved through route %s - sending "
+ "nexthop %s event to zebra",
+ at_least_one ? "":"(filtered)", bufn, bufp,
+ rib ? "reachable" : "unreachable");
+
+ if (srib && (state_changed || force))
+ {
+ SET_FLAG(srib->flags, ZEBRA_FLAG_CHANGED);
+ SET_FLAG(srib->status, RIB_ENTRY_NEXTHOPS_CHANGED);
+ rib_queue_add(&zebrad, static_rn);
+ }
+ }
+ }
}
return 1;
}
* free_state - free up the rib structure associated with the rnh.
*/
static void
-free_state (struct rib *rib)
+free_state (struct rib *rib, struct route_node *rn)
{
- struct nexthop *nexthop, *next;
if (!rib)
return;
/* free RIB and nexthops */
- for (nexthop = rib->nexthop; nexthop; nexthop = next)
- {
- next = nexthop->next;
- nexthop_free (nexthop);
- }
+ nexthops_free(rib->nexthop, rn);
XFREE (MTYPE_RIB, rib);
}
-/**
- * copy_nexthop - copy a nexthop to the rib structure.
- */
-static void
-copy_nexthop (struct rib *state, struct nexthop *nh)
-{
- struct nexthop *nexthop;
-
- nexthop = nexthop_new();
- nexthop->flags = nh->flags;
- nexthop->type = nh->type;
- nexthop->ifindex = nh->ifindex;
- if (nh->ifname)
- nexthop->ifname = XSTRDUP(0, nh->ifname);
- memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr));
- memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr));
-
- nexthop_add(state, nexthop);
-}
-
static void
-copy_state (struct rnh *rnh, struct rib *rib)
+copy_state (struct rnh *rnh, struct rib *rib, struct route_node *rn)
{
struct rib *state;
struct nexthop *nh;
if (rnh->state)
{
- free_state(rnh->state);
+ free_state(rnh->state, rn);
rnh->state = NULL;
}
state->metric = rib->metric;
for (nh = rib->nexthop; nh; nh = nh->next)
- copy_nexthop(state, nh);
+ copy_nexthops(state, nh);
rnh->state = state;
}
static int
compare_state (struct rib *r1, struct rib *r2)
{
- struct nexthop *nh1;
- struct nexthop *nh2;
- u_char found_nh = 0;
if (!r1 && !r2)
return 0;
if (r1->nexthop_num != r2->nexthop_num)
return 1;
- /* We need to verify that the nexthops for r1 match the nexthops for r2.
- * Since it is possible for a rib entry to have the same nexthop multiple
- * times (Example: [a,a]) we need to keep track of which r2 nexthops we have
- * already used as a match against a r1 nexthop. We track this
- * via NEXTHOP_FLAG_MATCHED. Clear this flag for all r2 nexthops when you
- * are finished.
- *
- * TRUE: r1 [a,b], r2 [a,b]
- * TRUE: r1 [a,b], r2 [b,a]
- * FALSE: r1 [a,b], r2 [a,c]
- * FALSE: r1 [a,a], r2 [a,b]
- */
- for (nh1 = r1->nexthop; nh1; nh1 = nh1->next)
- {
- found_nh = 0;
- for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
- {
- if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
- continue;
-
- if (nexthop_same_no_recurse(nh1, nh2))
- {
- SET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
- found_nh = 1;
- break;
- }
- }
-
- if (!found_nh)
- {
- for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
- if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
- UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
- return 1;
- }
- }
-
- for (nh2 = r2->nexthop; nh2; nh2 = nh2->next)
- if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED))
- UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED);
+ if (CHECK_FLAG(r1->status, RIB_ENTRY_NEXTHOPS_CHANGED))
+ return 1;
return 0;
}
for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client))
vty_out(vty, " %s(fd %d)%s", zebra_route_string(client->proto),
client->sock, rnh->filtered[client->proto] ? "(filtered)" : "");
+ if (!list_isempty(rnh->zebra_static_route_list))
+ vty_out(vty, " zebra%s", rnh->filtered[ZEBRA_ROUTE_STATIC] ? "(filtered)" : "");
vty_out(vty, "%s", VTY_NEWLINE);
}