frr/zebra/zebra_ns.c
Donald Sharp ac6314d380
Merge pull request #17297 from mjstapp/mjs_ifp_table
zebra, lib: use internal rbtree for per-NS tree of ifps
2024-11-12 15:12:07 -05:00

449 lines
9.8 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/* zebra NS Routines
* Copyright (C) 2016 Cumulus Networks, Inc.
* Donald Sharp
* Copyright (C) 2017/2018 6WIND
*/
#include "zebra.h"
#include "lib/ns.h"
#include "lib/vrf.h"
#include "lib/prefix.h"
#include "lib/memory.h"
#include "zebra_ns.h"
#include "zebra_vrf.h"
#include "rt.h"
#include "zebra_vxlan.h"
#include "debug.h"
#include "zebra_netns_notify.h"
#include "zebra_netns_id.h"
#include "zebra_pbr.h"
#include "zebra_tc.h"
#include "rib.h"
#include "table_manager.h"
#include "zebra_errors.h"
#include "zebra_dplane.h"
extern struct zebra_privs_t zserv_privs;
DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_NS, "Zebra Name Space");
DEFINE_MTYPE_STATIC(ZEBRA, ZNS_IFP, "Zebra NS Ifp");
static int ifp_tree_cmp(const struct ifp_tree_link *a, const struct ifp_tree_link *b);
DECLARE_RBTREE_UNIQ(ifp_tree, struct ifp_tree_link, link, ifp_tree_cmp);
static struct zebra_ns *dzns;
static int ifp_tree_cmp(const struct ifp_tree_link *a, const struct ifp_tree_link *b)
{
return (a->ifindex - b->ifindex);
}
/*
* Link an ifp into its parent NS
*/
void zebra_ns_link_ifp(struct zebra_ns *zns, struct interface *ifp)
{
struct zebra_if *zif;
struct ifp_tree_link *link, tlink = {};
zif = ifp->info;
assert(zif != NULL);
if (zif->ns_tree_link) {
assert(zif->ns_tree_link->zns == zns);
assert(zif->ns_tree_link->ifp == ifp);
return;
}
/* Lookup first - already linked? */
tlink.ifindex = ifp->ifindex;
link = ifp_tree_find(&zns->ifp_tree, &tlink);
if (link) {
assert(link->ifp == ifp);
return;
}
/* Allocate new linkage struct and add */
link = XCALLOC(MTYPE_ZNS_IFP, sizeof(struct ifp_tree_link));
link->ifp = ifp;
link->ifindex = ifp->ifindex;
link->zns = zns;
ifp_tree_add(&zns->ifp_tree, link);
zif->ns_tree_link = link;
}
/*
* Unlink an ifp from its parent NS (probably because the ifp is being deleted)
*/
void zebra_ns_unlink_ifp(struct interface *ifp)
{
struct zebra_if *zif;
struct ifp_tree_link *link;
struct zebra_ns *zns;
zif = ifp->info;
if (zif && zif->ns_tree_link) {
link = zif->ns_tree_link;
zns = link->zns;
ifp_tree_del(&zns->ifp_tree, link);
zif->ns_tree_link = NULL;
XFREE(MTYPE_ZNS_IFP, link);
}
}
/*
* ifp lookup apis
*/
struct interface *zebra_ns_lookup_ifp(struct zebra_ns *zns, uint32_t ifindex)
{
struct interface *ifp = NULL;
struct ifp_tree_link *link, tlink = {};
/* Init temp struct for lookup */
tlink.ifindex = ifindex;
link = ifp_tree_find(&zns->ifp_tree, &tlink);
if (link)
ifp = link->ifp;
return ifp;
}
static int lookup_ifp_name_cb(struct interface *ifp, void *arg);
struct ifp_name_ctx {
const char *ifname;
struct interface *ifp;
};
struct interface *zebra_ns_lookup_ifp_name(struct zebra_ns *zns, const char *ifname)
{
struct ifp_name_ctx ctx = {};
/* Hand context struct into walker function for use in its callback */
ctx.ifname = ifname;
zebra_ns_ifp_walk(zns, lookup_ifp_name_cb, &ctx);
return ctx.ifp;
}
static int lookup_ifp_name_cb(struct interface *ifp, void *arg)
{
struct ifp_name_ctx *pctx = arg;
if (strcmp(ifp->name, pctx->ifname) == 0) {
pctx->ifp = ifp;
return NS_WALK_STOP;
}
return NS_WALK_CONTINUE;
}
/* Iterate collection of ifps, calling application's callback. Callback uses
* return semantics from lib/ns.h: return NS_WALK_STOP to stop the iteration.
* Caller's 'arg' is included in each callback.
*/
int zebra_ns_ifp_walk(struct zebra_ns *zns,
int (*func)(struct interface *ifp, void *arg), void *arg)
{
struct ifp_tree_link *link;
int ret = NS_WALK_CONTINUE;
frr_each (ifp_tree, &zns->ifp_tree, link) {
ret = (func)(link->ifp, arg);
if (ret == NS_WALK_STOP)
break;
}
if (ret == NS_WALK_STOP)
return NS_WALK_STOP;
else
return NS_WALK_CONTINUE;
}
/*
* Walk all NSes, and all ifps for each NS.
*/
struct ns_ifp_walk_ctx {
int (*func)(struct interface *ifp, void *arg);
void *arg;
int ret;
};
static int ns_ifp_walker(struct ns *ns, void *in_param, void **unused);
void zebra_ns_ifp_walk_all(int (*func)(struct interface *ifp, void *arg), void *arg)
{
struct ns_ifp_walk_ctx ctx = {};
ctx.func = func;
ctx.arg = arg;
ns_walk_func(ns_ifp_walker, &ctx, NULL);
}
static int ns_ifp_walker(struct ns *ns, void *in_param, void **unused)
{
struct zebra_ns *zns;
struct ns_ifp_walk_ctx *ctx = in_param;
int ret = NS_WALK_CONTINUE;
zns = ns->info;
if (zns == NULL)
goto done;
ret = zebra_ns_ifp_walk(zns, ctx->func, ctx->arg);
done:
return ret;
}
static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete);
struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id)
{
if (ns_id == NS_DEFAULT)
return dzns;
struct zebra_ns *info = (struct zebra_ns *)ns_info_lookup(ns_id);
return (info == NULL) ? dzns : info;
}
static int zebra_ns_new(struct ns *ns)
{
struct zebra_ns *zns;
if (!ns)
return -1;
if (IS_ZEBRA_DEBUG_EVENT)
zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id);
zns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns));
ns->info = zns;
zns->ns = ns;
zns->ns_id = ns->ns_id;
/* Do any needed per-NS data structure allocation. */
ifp_tree_init(&zns->ifp_tree);
return 0;
}
static int zebra_ns_delete(struct ns *ns)
{
struct zebra_ns *zns = (struct zebra_ns *)ns->info;
struct zebra_if *zif;
struct ifp_tree_link *link;
if (IS_ZEBRA_DEBUG_EVENT)
zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id);
if (!zns)
return 0;
/* Clean up ifp tree */
while ((link = ifp_tree_pop(&zns->ifp_tree)) != NULL) {
zif = link->ifp->info;
zif->ns_tree_link = NULL;
XFREE(MTYPE_ZNS_IFP, link);
}
XFREE(MTYPE_ZEBRA_NS, ns->info);
return 0;
}
static int zebra_ns_enabled(struct ns *ns)
{
struct zebra_ns *zns = ns->info;
if (IS_ZEBRA_DEBUG_EVENT)
zlog_info("ZNS %s with id %u (enabled)", ns->name, ns->ns_id);
if (!zns)
return 0;
return zebra_ns_enable(ns->ns_id, (void **)&zns);
}
int zebra_ns_disabled(struct ns *ns)
{
struct zebra_ns *zns = ns->info;
if (IS_ZEBRA_DEBUG_EVENT)
zlog_info("ZNS %s with id %u (disabled)", ns->name, ns->ns_id);
if (!zns)
return 0;
return zebra_ns_disable_internal(zns, true);
}
void zebra_ns_startup_continue(struct zebra_dplane_ctx *ctx)
{
struct zebra_ns *zns = zebra_ns_lookup(dplane_ctx_get_ns_id(ctx));
enum zebra_dplane_startup_notifications spot;
if (!zns) {
zlog_err("%s: No Namespace associated with %u", __func__,
dplane_ctx_get_ns_id(ctx));
return;
}
spot = dplane_ctx_get_startup_spot(ctx);
switch (spot) {
case ZEBRA_DPLANE_INTERFACES_READ:
interface_list_tunneldump(zns);
break;
case ZEBRA_DPLANE_TUNNELS_READ:
interface_list_second(zns);
break;
case ZEBRA_DPLANE_ADDRESSES_READ:
route_read(zns);
vlan_read(zns);
kernel_read_pbr_rules(zns);
kernel_read_tc_qdisc(zns);
/*
* At this point FRR has requested and read a bunch
* of data from the dplane about initial state of
* the system. Zebra now needs to initialize
* the gr subsystem ( or the route sweeping
* subsystem ) to allow that to properly work.
* This must be done *immediately* after the
* load of all data from the underlying dplane.
*/
zebra_main_router_started();
break;
}
}
/* Do global enable actions - open sockets, read kernel config etc. */
int zebra_ns_enable(ns_id_t ns_id, void **info)
{
struct zebra_ns *zns = (struct zebra_ns *)(*info);
zns->ns_id = ns_id;
kernel_init(zns);
zebra_dplane_ns_enable(zns, true);
interface_list(zns);
return 0;
}
/* Common handler for ns disable - this can be called during ns config,
* or during zebra shutdown.
*/
static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete)
{
zebra_dplane_ns_enable(zns, false /*Disable*/);
kernel_terminate(zns, complete);
zns->ns_id = NS_DEFAULT;
return 0;
}
/* During zebra shutdown, do partial cleanup while the async dataplane
* is still running.
*/
int zebra_ns_early_shutdown(struct ns *ns,
void *param_in __attribute__((unused)),
void **param_out __attribute__((unused)))
{
struct zebra_ns *zns = ns->info;
if (zns == NULL)
return 0;
zebra_ns_disable_internal(zns, false);
return NS_WALK_CONTINUE;
}
/* During zebra shutdown, do kernel cleanup
* netlink sockets, ..
*/
int zebra_ns_kernel_shutdown(struct ns *ns, void *param_in __attribute__((unused)),
void **param_out __attribute__((unused)))
{
struct zebra_ns *zns = ns->info;
if (zns == NULL)
return NS_WALK_CONTINUE;
kernel_terminate(zns, true);
return NS_WALK_CONTINUE;
}
/* During zebra shutdown, do final cleanup
* after all dataplane work is complete.
*/
int zebra_ns_final_shutdown(struct ns *ns,
void *param_in __attribute__((unused)),
void **param_out __attribute__((unused)))
{
struct zebra_ns *zns = ns->info;
if (zns == NULL)
return NS_WALK_CONTINUE;
zebra_ns_delete(ns);
return NS_WALK_CONTINUE;
}
int zebra_ns_init(void)
{
struct ns *default_ns;
ns_id_t ns_id;
ns_id_t ns_id_external;
struct ns *ns;
frr_with_privs(&zserv_privs) {
ns_id = zebra_ns_id_get_default();
}
ns_id_external = ns_map_nsid_with_external(ns_id, true);
ns_init_management(ns_id_external, ns_id);
ns = ns_get_default();
if (ns)
ns->relative_default_ns = ns_id;
default_ns = ns_lookup(NS_DEFAULT);
if (!default_ns) {
flog_err(EC_ZEBRA_NS_NO_DEFAULT,
"%s: failed to find default ns", __func__);
exit(EXIT_FAILURE); /* This is non-recoverable */
}
/* Do any needed per-NS data structure allocation. */
zebra_ns_new(default_ns);
dzns = default_ns->info;
/* Register zebra VRF callbacks, create and activate default VRF. */
zebra_vrf_init();
/* Default NS is activated */
zebra_ns_enable(ns_id_external, (void **)&dzns);
if (vrf_is_backend_netns()) {
ns_add_hook(NS_NEW_HOOK, zebra_ns_new);
ns_add_hook(NS_ENABLE_HOOK, zebra_ns_enabled);
ns_add_hook(NS_DISABLE_HOOK, zebra_ns_disabled);
ns_add_hook(NS_DELETE_HOOK, zebra_ns_delete);
zebra_ns_notify_parse();
zebra_ns_notify_init();
}
return 0;
}