zebra: Add refcounts to evpn nexthop prefixes

With bgpd no longer sending withdraws for EVPN prefix routes
zebra needs to track the path/ref count of prefixes for an EVPN
nexthop. When a route is updated the count is first increased
with the new paths and then decreased with the paths of the
existing/"same" route entry.
However the same evpn route methods are used for EVPN MH as well,
where bgpd already tracks the references. It is expected that an
ADD operation for the respective A-D routes is handled as an
upsert, a DEL operation should really remove the respective A-D
reference on a next-hop. For this the old behaviour (no path/ref
counting in zebra) is preserved.

Signed-off-by: Christopher Dziomba <christopher.dziomba@telekom.de>
This commit is contained in:
Christopher Dziomba 2025-02-17 12:37:36 +01:00
parent 068a00f11e
commit 069a23da10
No known key found for this signature in database
4 changed files with 51 additions and 10 deletions

View file

@ -18,6 +18,7 @@ struct host_rb_entry {
RB_ENTRY(host_rb_entry) hl_entry;
struct prefix p;
uint32_t pathcnt;
};
RB_HEAD(host_rb_tree_entry, host_rb_entry);

View file

@ -3795,7 +3795,7 @@ void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS)
memset(&dummy_prefix, 0, sizeof(dummy_prefix));
dummy_prefix.family = AF_EVPN;
dummy_prefix.prefixlen = (sizeof(struct evpn_addr) * 8);
dummy_prefix.prefix.route_type = 1; /* XXX - fixup to type-1 def */
dummy_prefix.prefix.route_type = BGP_EVPN_AD_ROUTE; /* XXX - fixup to type-1 def */
dummy_prefix.prefix.ead_addr.ip.ipa_type = nh.ipa_type;
if (hdr->command == ZEBRA_EVPN_REMOTE_NH_ADD) {

View file

@ -2863,6 +2863,28 @@ static void process_subq_early_route_add(struct zebra_early_route *ere)
if (same) {
if (dest && same == dest->selected_fib)
SET_FLAG(same->status, ROUTE_ENTRY_ROUTE_REPLACING);
struct nexthop *tmp_nh;
/* Free up the evpn nhs of the re to be replaced.*/
for (ALL_NEXTHOPS(same->nhe->nhg, tmp_nh)) {
struct ipaddr vtep_ip;
if (CHECK_FLAG(tmp_nh->flags, NEXTHOP_FLAG_EVPN)) {
memset(&vtep_ip, 0, sizeof(struct ipaddr));
if (ere->afi == AFI_IP) {
vtep_ip.ipa_type = IPADDR_V4;
memcpy(&(vtep_ip.ipaddr_v4), &(tmp_nh->gate.ipv4),
sizeof(struct in_addr));
} else {
vtep_ip.ipa_type = IPADDR_V6;
memcpy(&(vtep_ip.ipaddr_v6), &(tmp_nh->gate.ipv6),
sizeof(struct in6_addr));
}
zebra_rib_queue_evpn_route_del(same->vrf_id, &vtep_ip, &ere->p);
}
}
rib_delnode(rn, same);
}

View file

@ -355,6 +355,7 @@ static void zl3vni_print_nh(struct zebra_neigh *n, struct vty *vty,
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
json_object *json_hosts = NULL;
json_object *json_prefix = NULL;
struct host_rb_entry *hle;
if (!json) {
@ -370,7 +371,7 @@ static void zl3vni_print_nh(struct zebra_neigh *n, struct vty *vty,
rb_host_count(&n->host_rb));
vty_out(vty, " Prefixes:\n");
RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb)
vty_out(vty, " %pFX\n", &hle->p);
vty_out(vty, " %pFX (paths: %d)\n", &hle->p, hle->pathcnt);
}
} else {
json_hosts = json_object_new_array();
@ -385,11 +386,13 @@ static void zl3vni_print_nh(struct zebra_neigh *n, struct vty *vty,
else {
json_object_int_add(json, "refCount",
rb_host_count(&n->host_rb));
RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb)
json_object_array_add(
json_hosts,
json_object_new_string(prefix2str(
&hle->p, buf2, sizeof(buf2))));
RB_FOREACH (hle, host_rb_tree_entry, &n->host_rb) {
json_prefix = json_object_new_object();
json_object_string_add(json_prefix, "prefix",
prefix2str(&hle->p, buf2, sizeof(buf2)));
json_object_int_add(json_prefix, "pathCount", hle->pathcnt);
json_object_array_add(json_hosts, json_prefix);
}
json_object_object_add(json, "prefixList", json_hosts);
}
}
@ -1143,11 +1146,23 @@ static void rb_find_or_add_host(struct host_rb_tree_entry *hrbe,
memcpy(&lookup.p, host, sizeof(*host));
hle = RB_FIND(host_rb_tree_entry, hrbe, &lookup);
if (hle)
if (hle) {
/* never pathcount evpn A-D / MH routes because zebra is not aware
* of specific paths. ADD operations are considered to be upsert
* leading to path count increasing without ever decreasing. A single
* DEL operation should fully remove the prefix from the next-hop.
*/
if (host->family == AF_EVPN &&
((const struct prefix_evpn *)host)->prefix.route_type == BGP_EVPN_AD_ROUTE)
return;
hle->pathcnt++;
return;
}
hle = XCALLOC(MTYPE_HOST_PREFIX, sizeof(struct host_rb_entry));
memcpy(hle, &lookup, sizeof(lookup));
hle->pathcnt = 1;
RB_INSERT(host_rb_tree_entry, hrbe, hle);
}
@ -1162,8 +1177,11 @@ static void rb_delete_host(struct host_rb_tree_entry *hrbe, struct prefix *host)
hle = RB_FIND(host_rb_tree_entry, hrbe, &lookup);
if (hle) {
RB_REMOVE(host_rb_tree_entry, hrbe, hle);
XFREE(MTYPE_HOST_PREFIX, hle);
hle->pathcnt--;
if (hle->pathcnt == 0) {
RB_REMOVE(host_rb_tree_entry, hrbe, hle);
XFREE(MTYPE_HOST_PREFIX, hle);
}
}
return;