mirror of
https://github.com/FRRouting/frr.git
synced 2025-04-30 13:37:17 +02:00
449 lines
9.8 KiB
C
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;
|
|
}
|