mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-16 19:19:59 +00:00
zebra: wip: early version of dplane result handler
Early try at a result handler for async dplane route updates Signed-off-by: Mark Stapp <mjs@voltanet.io>
This commit is contained in:
parent
7cdb1a8445
commit
e5ac2adf17
@ -1868,6 +1868,252 @@ static void rib_process(struct route_node *rn)
|
|||||||
rib_gc_dest(rn);
|
rib_gc_dest(rn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Utility to match route with dplane context data
|
||||||
|
*/
|
||||||
|
static bool rib_route_match_ctx(const struct route_entry *re,
|
||||||
|
const dplane_ctx_h ctx, bool is_update)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
if (is_update) {
|
||||||
|
/*
|
||||||
|
* In 'update' case, we test info about the 'previous' or
|
||||||
|
* 'old' route
|
||||||
|
*/
|
||||||
|
if ((re->type == dplane_ctx_get_old_type(ctx)) &&
|
||||||
|
(re->instance == dplane_ctx_get_old_instance(ctx))) {
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
/* TODO -- we're using this extra test, but it's not
|
||||||
|
* exactly clear why.
|
||||||
|
*/
|
||||||
|
if (re->type == ZEBRA_ROUTE_STATIC &&
|
||||||
|
(re->distance != dplane_ctx_get_old_distance(ctx) ||
|
||||||
|
re->tag != dplane_ctx_get_old_tag(ctx))) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Ordinary, single-route case using primary context info
|
||||||
|
*/
|
||||||
|
if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) &&
|
||||||
|
CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) {
|
||||||
|
/* Skip route that's been deleted */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((re->type == dplane_ctx_get_type(ctx)) &&
|
||||||
|
(re->instance == dplane_ctx_get_instance(ctx))) {
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
/* TODO -- we're using this extra test, but it's not
|
||||||
|
* exactly clear why.
|
||||||
|
*/
|
||||||
|
if (re->type == ZEBRA_ROUTE_STATIC &&
|
||||||
|
(re->distance != dplane_ctx_get_distance(ctx) ||
|
||||||
|
re->tag != dplane_ctx_get_tag(ctx))) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO - WIP
|
||||||
|
*/
|
||||||
|
static void rib_process_after(dplane_ctx_h ctx)
|
||||||
|
{
|
||||||
|
struct route_table *table = NULL;
|
||||||
|
struct route_node *rn = NULL;
|
||||||
|
struct route_entry *re = NULL, *old_re = NULL, *rib;
|
||||||
|
bool is_update = false;
|
||||||
|
struct nexthop *nexthop;
|
||||||
|
char dest_str[PREFIX_STRLEN];
|
||||||
|
dplane_op_e op;
|
||||||
|
enum zebra_dplane_result status;
|
||||||
|
const struct prefix *dest_pfx, *src_pfx;
|
||||||
|
|
||||||
|
/* Locate rn and re(s) from ctx */
|
||||||
|
|
||||||
|
table = zebra_vrf_table_with_table_id(dplane_ctx_get_afi(ctx),
|
||||||
|
dplane_ctx_get_safi(ctx),
|
||||||
|
dplane_ctx_get_vrf(ctx),
|
||||||
|
dplane_ctx_get_table(ctx));
|
||||||
|
if (table == NULL) {
|
||||||
|
if (IS_ZEBRA_DEBUG_DPLANE) {
|
||||||
|
zlog_debug("Failed to process dplane results: no table "
|
||||||
|
"for afi %d, safi %d, vrf %u",
|
||||||
|
dplane_ctx_get_afi(ctx),
|
||||||
|
dplane_ctx_get_safi(ctx),
|
||||||
|
dplane_ctx_get_vrf(ctx));
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_pfx = dplane_ctx_get_dest(ctx);
|
||||||
|
|
||||||
|
/* Note well: only capturing the prefix string if debug is enabled here;
|
||||||
|
* unconditional log messages will have to generate the string.
|
||||||
|
*/
|
||||||
|
if (IS_ZEBRA_DEBUG_DPLANE) {
|
||||||
|
prefix2str(dest_pfx, dest_str, sizeof(dest_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
src_pfx = dplane_ctx_get_src(ctx);
|
||||||
|
rn = srcdest_rnode_get(table, dplane_ctx_get_dest(ctx),
|
||||||
|
src_pfx ? (struct prefix_ipv6 * )src_pfx : NULL);
|
||||||
|
if (rn == NULL) {
|
||||||
|
if (IS_ZEBRA_DEBUG_DPLANE) {
|
||||||
|
zlog_debug("Failed to process dplane results: no "
|
||||||
|
"route for %u:%s",
|
||||||
|
dplane_ctx_get_vrf(ctx), dest_str);
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
srcdest_rnode_prefixes(rn, &dest_pfx, &src_pfx);
|
||||||
|
|
||||||
|
op = dplane_ctx_get_op(ctx);
|
||||||
|
status = dplane_ctx_get_status(ctx);
|
||||||
|
|
||||||
|
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
|
||||||
|
zlog_debug("%u:%s Processing dplane ctx %p, op %s result %d",
|
||||||
|
dplane_ctx_get_vrf(ctx), dest_str, ctx,
|
||||||
|
dplane_op2str(op), status);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == DPLANE_OP_ROUTE_DELETE) {
|
||||||
|
/*
|
||||||
|
* In the delete case, the zebra core datastructs were
|
||||||
|
* updated (or removed) at the time the delete was issued,
|
||||||
|
* so we're just notifying the route owner.
|
||||||
|
*/
|
||||||
|
if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
|
||||||
|
zsend_route_notify_owner_ctx(ctx, ZAPI_ROUTE_REMOVED);
|
||||||
|
} else {
|
||||||
|
zsend_route_notify_owner_ctx(ctx,
|
||||||
|
ZAPI_ROUTE_FAIL_INSTALL);
|
||||||
|
|
||||||
|
zlog_warn("%u:%s: Route Deletion failure",
|
||||||
|
dplane_ctx_get_vrf(ctx),
|
||||||
|
prefix2str(dest_pfx,
|
||||||
|
dest_str, sizeof(dest_str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nothing more to do in delete case */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update is a bit of a special case, where we may have both old and new
|
||||||
|
* routes to post-process.
|
||||||
|
*/
|
||||||
|
is_update = dplane_ctx_is_update(ctx);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take a pass through the routes, look for matches with the context
|
||||||
|
* info.
|
||||||
|
*/
|
||||||
|
RNODE_FOREACH_RE(rn, rib) {
|
||||||
|
|
||||||
|
if (re == NULL) {
|
||||||
|
if (rib_route_match_ctx(rib, ctx, false)) {
|
||||||
|
re = rib;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for old route match */
|
||||||
|
if (is_update && (old_re == NULL)) {
|
||||||
|
if (rib_route_match_ctx(rib, ctx, true /*is_update*/)) {
|
||||||
|
old_re = rib;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Have we found the routes we need to work on? */
|
||||||
|
if (re && ((!is_update || old_re))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check sequence number(s) to detect stale results before continuing
|
||||||
|
*/
|
||||||
|
if (re && (re->dplane_sequence != dplane_ctx_get_seq(ctx))) {
|
||||||
|
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
|
||||||
|
zlog_debug("%u:%s Stale dplane result for re %p",
|
||||||
|
dplane_ctx_get_vrf(ctx), dest_str, re);
|
||||||
|
}
|
||||||
|
re = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_re &&
|
||||||
|
(old_re->dplane_sequence != dplane_ctx_get_old_seq(ctx))) {
|
||||||
|
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
|
||||||
|
zlog_debug("%u:%s Stale dplane result for old_re %p",
|
||||||
|
dplane_ctx_get_vrf(ctx), dest_str, old_re);
|
||||||
|
}
|
||||||
|
old_re = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here's sort of a tough one: the route update result is stale.
|
||||||
|
* Is it better to use the context block info to generate
|
||||||
|
* redist and owner notification, or is it better to wait
|
||||||
|
* for the up-to-date result to arrive?
|
||||||
|
*/
|
||||||
|
if (re == NULL) {
|
||||||
|
/* TODO -- for now, only expose up-to-date results */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) {
|
||||||
|
/* Set nexthop FIB flags */
|
||||||
|
for (ALL_NEXTHOPS(re->ng, nexthop)) {
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
|
||||||
|
SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
|
||||||
|
else
|
||||||
|
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Redistribute */
|
||||||
|
/* TODO -- still calling the redist api using the route_entries,
|
||||||
|
* and there's a corner-case here: if there's no client
|
||||||
|
* for the 'new' route, a redist deleting the 'old' route
|
||||||
|
* will be sent. But if the 'old' context info was stale,
|
||||||
|
* 'old_re' will be NULL here and that delete will not be sent.
|
||||||
|
*/
|
||||||
|
redistribute_update(dest_pfx, src_pfx, re, old_re);
|
||||||
|
|
||||||
|
/* Notify route owner */
|
||||||
|
zsend_route_notify_owner(re,
|
||||||
|
dest_pfx, ZAPI_ROUTE_INSTALLED);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
zsend_route_notify_owner(re, dest_pfx,
|
||||||
|
ZAPI_ROUTE_FAIL_INSTALL);
|
||||||
|
|
||||||
|
zlog_warn("%u:%s: Route install failed",
|
||||||
|
dplane_ctx_get_vrf(ctx),
|
||||||
|
prefix2str(dest_pfx,
|
||||||
|
dest_str, sizeof(dest_str)));
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
/* Return context to dataplane module */
|
||||||
|
dplane_ctx_fini(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
/* Take a list of route_node structs and return 1, if there was a record
|
/* 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,
|
* picked from it and processed by rib_process(). Don't process more,
|
||||||
* than one RN record; operate only in the specified sub-queue.
|
* than one RN record; operate only in the specified sub-queue.
|
||||||
@ -3029,7 +3275,7 @@ static int rib_process_dplane_results(struct thread *thread)
|
|||||||
pthread_mutex_unlock(&dplane_mutex);
|
pthread_mutex_unlock(&dplane_mutex);
|
||||||
|
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
dplane_ctx_fini(&ctx);
|
rib_process_after(ctx);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user