Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zebra, lib: use internal rbtree for per-NS tree of ifps #17297

Merged
merged 9 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 42 additions & 24 deletions zebra/if_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -1568,46 +1568,64 @@ static int netlink_request_tunneldump(struct zebra_ns *zns, int family,
return netlink_request(&zns->netlink_cmd, &req);
}

/* Prototype for tunneldump walker */
static int tunneldump_walk_cb(struct interface *ifp, void *arg);

struct tunneldump_ctx {
struct zebra_ns *zns;
struct zebra_dplane_info *dp_info;
int ret;
};

/*
* Currently we only ask for vxlan l3svd vni information.
* In the future this can be expanded.
*/
int netlink_tunneldump_read(struct zebra_ns *zns)
{
int ret = 0;
struct tunneldump_ctx ctx = {};
struct zebra_dplane_info dp_info;
struct route_node *rn;
struct interface *tmp_if = NULL;
struct zebra_if *zif;
struct nlsock *netlink_cmd = &zns->netlink_cmd;

zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);

for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
tmp_if = (struct interface *)rn->info;
if (!tmp_if)
continue;
zif = tmp_if->info;
if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
continue;
/* Set up context and call iterator */
ctx.zns = zns;
ctx.dp_info = &dp_info;

ret = netlink_request_tunneldump(zns, PF_BRIDGE,
tmp_if->ifindex);
if (ret < 0) {
route_unlock_node(rn);
return ret;
}
zebra_ns_ifp_walk(zns, tunneldump_walk_cb, &ctx);

ret = netlink_parse_info(netlink_link_change, netlink_cmd,
&dp_info, 0, true);
ret = ctx.ret;

if (ret < 0) {
route_unlock_node(rn);
return ret;
}
return ret;
}

static int tunneldump_walk_cb(struct interface *ifp, void *arg)
{
int ret;
struct tunneldump_ctx *ctx = arg;
struct zebra_if *zif;

zif = ifp->info;
if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
goto done;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why goto here? Especially since we have other returns directly in the middle?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just an artifact of moving the code from the inline loop to the callback I guess. we need to be able to have "special" continue path for this clause, and then the "normal" continue path for non-error cases at the end of the callback?


ret = netlink_request_tunneldump(ctx->zns, PF_BRIDGE, ifp->ifindex);
if (ret < 0) {
ctx->ret = ret;
return NS_WALK_STOP;
}

return 0;
ret = netlink_parse_info(netlink_link_change, &(ctx->zns->netlink_cmd),
ctx->dp_info, 0, true);

if (ret < 0) {
ctx->ret = ret;
return NS_WALK_STOP;
}

done:
return NS_WALK_CONTINUE;
}

static uint8_t netlink_get_dplane_vlan_state(uint8_t state)
Expand Down
2 changes: 1 addition & 1 deletion zebra/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information");
DEFINE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp),
(vty, ifp));

DEFINE_MTYPE(ZEBRA, ZIF_DESC, "Intf desc");
DEFINE_MTYPE_STATIC(ZEBRA, ZIF_DESC, "Intf desc");

static void if_down_del_nbr_connected(struct interface *ifp);

Expand Down
3 changes: 0 additions & 3 deletions zebra/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ enum zebra_if_flags {
#define ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif) \
((zif)->protodown_rc == ZEBRA_PROTODOWN_EXTERNAL)

/* Mem type for zif desc */
DECLARE_MTYPE(ZIF_DESC);

/* `zebra' daemon local interface structure. */
struct zebra_if {
/* back pointer to the interface */
Expand Down
188 changes: 188 additions & 0 deletions zebra/zebra_ns.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,185 @@
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)
Expand Down Expand Up @@ -59,18 +235,30 @@ static int zebra_ns_new(struct ns *ns)

/* Do any needed per-NS data structure allocation. */
zns->if_table = route_table_init();
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;
}
Expand Down
34 changes: 34 additions & 0 deletions zebra/zebra_ns.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ struct nlsock {
};
#endif

/* Tree of interfaces: external linkage struct, and rbtree */
PREDECL_RBTREE_UNIQ(ifp_tree);

struct ifp_tree_link {
struct ifp_tree_item link;

ifindex_t ifindex;

struct interface *ifp;

/* Backpointer */
struct zebra_ns *zns;
};

struct zebra_ns {
/* net-ns name. */
char name[VRF_NAMSIZ];
Expand All @@ -55,12 +69,32 @@ struct zebra_ns {

struct route_table *if_table;

/* Tree of interfaces in this ns */
struct ifp_tree_head ifp_tree;

/* Back pointer */
struct ns *ns;
};

struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id);

/* Manage collection of ifps per-NS */
void zebra_ns_link_ifp(struct zebra_ns *zns, struct interface *ifp);
void zebra_ns_unlink_ifp(struct interface *ifp);
struct interface *zebra_ns_lookup_ifp(struct zebra_ns *zns, uint32_t ifindex);
struct interface *zebra_ns_lookup_ifp_name(struct zebra_ns *zns, const char *ifname);

/* 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.
* The iterator returns STOP or CONTINUE also.
*/
int zebra_ns_ifp_walk(struct zebra_ns *zns,
int (*func)(struct interface *ifp, void *arg), void *arg);

/* Walk all NSes, and all ifps for each NS. */
void zebra_ns_ifp_walk_all(int (*func)(struct interface *ifp, void *arg), void *arg);

int zebra_ns_init(void);
int zebra_ns_enable(ns_id_t ns_id, void **info);
int zebra_ns_disabled(struct ns *ns);
Expand Down