diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/if.h | 1 | ||||
| -rw-r--r-- | lib/keychain_nb.c | 4 | ||||
| -rw-r--r-- | lib/northbound.c | 50 | ||||
| -rw-r--r-- | lib/northbound.h | 50 | ||||
| -rw-r--r-- | lib/northbound_notif.c | 45 | ||||
| -rw-r--r-- | lib/northbound_oper.c | 371 | ||||
| -rw-r--r-- | lib/prefix.c | 5 | ||||
| -rw-r--r-- | lib/srv6.h | 27 | ||||
| -rw-r--r-- | lib/yang.c | 16 | ||||
| -rw-r--r-- | lib/yang.h | 9 |
10 files changed, 481 insertions, 97 deletions
@@ -242,6 +242,7 @@ struct interface { #define ZEBRA_INTERFACE_SUB (1 << 1) #define ZEBRA_INTERFACE_LINKDETECTION (1 << 2) #define ZEBRA_INTERFACE_VRF_LOOPBACK (1 << 3) +#define ZEBRA_INTERFACE_DUMMY (1 << 4) /* Interface flags. */ uint64_t flags; diff --git a/lib/keychain_nb.c b/lib/keychain_nb.c index 57967b30a5..7c3df1c857 100644 --- a/lib/keychain_nb.c +++ b/lib/keychain_nb.c @@ -587,9 +587,9 @@ static int key_chains_key_chain_key_crypto_algorithm_destroy( if (args->event != NB_EV_APPLY) return NB_OK; - name = yang_dnode_get_string(args->dnode, "../../../name"); + name = yang_dnode_get_string(args->dnode, "../../name"); keychain = keychain_lookup(name); - index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../../key-id"); + index = (uint32_t)yang_dnode_get_uint64(args->dnode, "../key-id"); key = key_lookup(keychain, index); key->hash_algo = KEYCHAIN_ALGO_NULL; keychain_touch(keychain); diff --git a/lib/northbound.c b/lib/northbound.c index 60794b8728..f860b83c45 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -127,6 +127,8 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg) if (module && module->ignore_cfg_cbs) SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS); + if (module && module->get_tree_locked) + SET_FLAG(nb_node->flags, F_NB_NODE_HAS_GET_TREE); return YANG_ITER_CONTINUE; } @@ -256,6 +258,7 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node) { unsigned int error = 0; + bool state_optional = CHECK_FLAG(nb_node->flags, F_NB_NODE_HAS_GET_TREE); if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS)) return error; @@ -273,15 +276,15 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node) error += nb_node_validate_cb(nb_node, NB_CB_APPLY_FINISH, !!nb_node->cbs.apply_finish, true); error += nb_node_validate_cb(nb_node, NB_CB_GET_ELEM, - (nb_node->cbs.get_elem || nb_node->cbs.get), false); + (nb_node->cbs.get_elem || nb_node->cbs.get), state_optional); error += nb_node_validate_cb(nb_node, NB_CB_GET_NEXT, (nb_node->cbs.get_next || (nb_node->snode->nodetype == LYS_LEAFLIST && nb_node->cbs.get)), - false); - error += nb_node_validate_cb(nb_node, NB_CB_GET_KEYS, - !!nb_node->cbs.get_keys, false); - error += nb_node_validate_cb(nb_node, NB_CB_LOOKUP_ENTRY, - !!nb_node->cbs.lookup_entry, false); + state_optional); + error += nb_node_validate_cb(nb_node, NB_CB_GET_KEYS, !!nb_node->cbs.get_keys, + state_optional); + error += nb_node_validate_cb(nb_node, NB_CB_LOOKUP_ENTRY, !!nb_node->cbs.lookup_entry, + state_optional); error += nb_node_validate_cb(nb_node, NB_CB_RPC, !!nb_node->cbs.rpc, false); error += nb_node_validate_cb(nb_node, NB_CB_NOTIFY, @@ -516,20 +519,33 @@ void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq, struct nb_config_cbs *changes) { + struct nb_node *nb_node = dnode->schema->priv; + struct lyd_node *child; + bool recursed = false; + /* Ignore unimplemented nodes. */ - if (!dnode->schema->priv) + if (!nb_node) return; + /* + * If the CB structure indicates it (recurse flag set), call the destroy + * callbacks for the children of a containment node. + */ + if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER | LYS_LIST) && + CHECK_FLAG(nb_node->cbs.flags, F_NB_CB_DESTROY_RECURSE)) { + recursed = true; + LY_LIST_FOR (lyd_child(dnode), child) { + nb_config_diff_deleted(child, seq, changes); + } + } + if (nb_cb_operation_is_valid(NB_CB_DESTROY, dnode->schema)) nb_config_diff_add_change(changes, NB_CB_DESTROY, seq, dnode); - else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) { - struct lyd_node *child; - + else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER) && !recursed) { /* - * Non-presence containers need special handling since they - * don't have "destroy" callbacks. In this case, what we need to - * do is to call the "destroy" callbacks of their child nodes - * when applicable (i.e. optional nodes). + * If we didn't already above, call destroy on the children of + * this container (it's an NP container) as NP containers have + * no destroy CB themselves. */ LY_LIST_FOR (lyd_child(dnode), child) { nb_config_diff_deleted(child, seq, changes); @@ -2717,7 +2733,7 @@ void nb_init(struct event_loop *tm, const struct frr_yang_module_info *const modules[], size_t nmodules, bool db_enabled, bool load_library) { - struct yang_module *loaded[nmodules], **loadedp = loaded; + struct yang_module *loaded[nmodules]; /* * Currently using this explicit compile feature in libyang2 leads to @@ -2737,8 +2753,8 @@ void nb_init(struct event_loop *tm, for (size_t i = 0; i < nmodules; i++) { DEBUGD(&nb_dbg_events, "northbound: loading %s.yang", modules[i]->name); - *loadedp++ = yang_module_load(modules[i]->name, - modules[i]->features); + loaded[i] = yang_module_load(modules[i]->name, modules[i]->features); + loaded[i]->frr_info = modules[i]; } if (explicit_compile) diff --git a/lib/northbound.h b/lib/northbound.h index c31f007e70..0468c58de3 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -386,6 +386,11 @@ struct nb_callbacks { int (*destroy)(struct nb_cb_destroy_args *args); /* + * Flags to control the how northbound callbacks are invoked. + */ + uint flags; + + /* * Configuration callback. * * A list entry or leaf-list entry has been moved. Only applicable when @@ -622,6 +627,12 @@ struct nb_callbacks { void (*cli_show_end)(struct vty *vty, const struct lyd_node *dnode); }; +/* + * Flag indicating the northbound should recurse destroy the children of this + * node when it is destroyed. + */ +#define F_NB_CB_DESTROY_RECURSE 0x01 + struct nb_dependency_callbacks { void (*get_dependant_xpath)(const struct lyd_node *dnode, char *xpath); void (*get_dependency_xpath)(const struct lyd_node *dnode, char *xpath); @@ -663,6 +674,8 @@ struct nb_node { #define F_NB_NODE_KEYLESS_LIST 0x02 /* Ignore config callbacks for this node */ #define F_NB_NODE_IGNORE_CFG_CBS 0x04 +/* Ignore state callbacks for this node */ +#define F_NB_NODE_HAS_GET_TREE 0x08 /* * HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays @@ -690,6 +703,22 @@ struct frr_yang_module_info { */ const char **features; + /* + * If the module keeps its oper-state in a libyang tree + * this function should return that tree (locked if multi-threading). + * If this function is provided then the state callback functions + * (get_elem, get_keys, get_next, lookup_entry) need not be set for a + * module. The unlock_tree function if non-NULL will be called with + * the returned tree and the *user_lock value. + */ + const struct lyd_node *(*get_tree_locked)(const char *xpath, void **user_lock); + + /* + * This function will be called following a call to get_tree_locked() in + * order to unlock the tree if locking was required. + */ + void (*unlock_tree)(const struct lyd_node *tree, void *user_lock); + /* Northbound callbacks. */ const struct { /* Data path of this YANG node. */ @@ -1825,6 +1854,18 @@ extern struct lyd_node *nb_op_updatef(struct lyd_node *tree, const char *path, c extern struct lyd_node *nb_op_vupdatef(struct lyd_node *tree, const char *path, const char *val_fmt, va_list ap); +/** + * nb_notif_add() - Notice that the value at `path` has changed. + * @path - Absolute path in the state tree that has changed (either added or + * updated). + */ +void nb_notif_add(const char *path); + +/** + * nb_notif_delete() - Notice that the value at `path` has been deleted. + * @path - Absolute path in the state tree that has been deleted. + */ +void nb_notif_delete(const char *path); /** * nb_notif_set_filters() - add or replace notification filters @@ -1835,6 +1876,15 @@ extern struct lyd_node *nb_op_vupdatef(struct lyd_node *tree, const char *path, */ extern void nb_notif_set_filters(const char **selectors, bool replace); +/** + * nb_notif_enable_multi_thread() - enable use of multiple threads with nb_notif + * + * If the nb_notif_XXX calls will be made from multiple threads then locking is + * required. Call this function to enable that functionality, prior to using the + * nb_notif_XXX API. + */ +extern void nb_notif_enable_multi_thread(void); + extern void nb_notif_init(struct event_loop *loop); extern void nb_notif_terminate(void); diff --git a/lib/northbound_notif.c b/lib/northbound_notif.c index 9caca9f6d7..746b33beb2 100644 --- a/lib/northbound_notif.c +++ b/lib/northbound_notif.c @@ -58,6 +58,9 @@ RB_HEAD(op_changes, op_change); RB_PROTOTYPE(op_changes, op_change, link, op_change_cmp) RB_GENERATE(op_changes, op_change, link, op_change_cmp) +pthread_mutex_t _nb_notif_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t *nb_notif_lock; + struct op_changes nb_notif_adds = RB_INITIALIZER(&nb_notif_adds); struct op_changes nb_notif_dels = RB_INITIALIZER(&nb_notif_dels); struct event_loop *nb_notif_master; @@ -158,12 +161,14 @@ static void op_change_free(struct op_change *note) } /** - * op_changes_group_push() - Save the current set of changes on the queue. + * __op_changes_group_push() - Save the current set of changes on the queue. * * This function will save the current set of changes on the queue and * initialize a new set of changes. + * + * The lock must be held during this call. */ -static void op_changes_group_push(void) +static void __op_changes_group_push(void) { struct op_changes_group *changes; @@ -312,17 +317,34 @@ static void __op_change_add_del(const char *path, struct op_changes *this_head, nb_notif_set_walk_timer(); } -static void nb_notif_add(const char *path) +void nb_notif_add(const char *path) { + if (nb_notif_lock) + pthread_mutex_lock(nb_notif_lock); + __op_change_add_del(path, &nb_notif_adds, &nb_notif_dels); + + if (nb_notif_lock) + pthread_mutex_unlock(nb_notif_lock); } -static void nb_notif_delete(const char *path) +void nb_notif_delete(const char *path) { + if (nb_notif_lock) + pthread_mutex_lock(nb_notif_lock); + __op_change_add_del(path, &nb_notif_dels, &nb_notif_adds); + + if (nb_notif_lock) + pthread_mutex_unlock(nb_notif_lock); } + +/* ---------------------------------------------- */ +/* User functions to update and delete oper state */ +/* ---------------------------------------------- */ + struct lyd_node *nb_op_update(struct lyd_node *tree, const char *path, const char *value) { struct lyd_node *dnode; @@ -459,13 +481,21 @@ static struct op_changes_group *op_changes_group_next(void) { struct op_changes_group *group; + if (nb_notif_lock) + pthread_mutex_lock(nb_notif_lock); + group = op_changes_queue_pop(&op_changes_queue); if (!group) { - op_changes_group_push(); + __op_changes_group_push(); group = op_changes_queue_pop(&op_changes_queue); } + + if (nb_notif_lock) + pthread_mutex_unlock(nb_notif_lock); + if (!group) return NULL; + group->cur_changes = &group->dels; group->cur_change = RB_MIN(op_changes, group->cur_changes); if (!group->cur_change) { @@ -674,6 +704,11 @@ void nb_notif_set_filters(const char **selectors, bool replace) darr_free(selectors); } +void nb_notif_enable_multi_thread(void) +{ + nb_notif_lock = &_nb_notif_lock; +} + void nb_notif_init(struct event_loop *tm) { nb_notif_master = tm; diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c index 6336db502a..ad495b6f9c 100644 --- a/lib/northbound_oper.c +++ b/lib/northbound_oper.c @@ -48,6 +48,9 @@ DEFINE_MTYPE_STATIC(LIB, NB_NODE_INFOS, "NB Node Infos"); /* ---------- */ PREDECL_LIST(nb_op_walks); +typedef const struct lyd_node *(*get_tree_locked_cb)(const char *xpath, void **user_tree_lock); +typedef void (*unlock_tree_cb)(const struct lyd_node *tree, void *user_tree_lock); + /* * This is our information about a node on the branch we are looking at */ @@ -81,6 +84,7 @@ struct nb_op_node_info { * @walk_start_level: @walk_root_level + 1. * @query_base_level: the level the query string stops at and full walks * commence below that. + * @user_tree: the user's existing state tree to copy state from or NULL. */ struct nb_op_yield_state { /* Walking state */ @@ -96,6 +100,11 @@ struct nb_op_yield_state { int query_base_level; bool query_list_entry; /* XXX query was for a specific list entry */ + /* For now we support a single use of this. */ + const struct lyd_node *user_tree; + void *user_tree_lock; + unlock_tree_cb user_tree_unlock; + /* Yielding state */ bool query_did_entry; /* currently processing the entry */ bool should_batch; @@ -125,6 +134,11 @@ static struct nb_op_walks_head nb_op_walks; static enum nb_error nb_op_yield(struct nb_op_yield_state *ys); static struct lyd_node *ys_root_node(struct nb_op_yield_state *ys); +static const void *nb_op_list_get_next(struct nb_op_yield_state *ys, struct nb_node *nb_node, + const struct nb_op_node_info *pni, const void *list_entry); +static const void *nb_op_list_lookup_entry(struct nb_op_yield_state *ys, struct nb_node *nb_node, + const struct nb_op_node_info *pni, struct lyd_node *node, + const struct yang_list_keys *keys); /* -------------------- */ /* Function Definitions */ @@ -140,6 +154,11 @@ nb_op_create_yield_state(const char *xpath, struct yang_translator *translator, ys = XCALLOC(MTYPE_NB_YIELD_STATE, sizeof(*ys)); ys->xpath = darr_strdup_cap(xpath, (size_t)XPATH_MAXLEN); + /* remove trailing '/'s */ + while (darr_len(ys->xpath) > 1 && ys->xpath[darr_len(ys->xpath) - 2] == '/') { + darr_setlen(ys->xpath, darr_len(ys->xpath) - 1); + *darr_last(ys->xpath) = 0; + } ys->xpath_orig = darr_strdup(xpath); ys->translator = translator; ys->flags = flags; @@ -158,6 +177,8 @@ static inline void nb_op_free_yield_state(struct nb_op_yield_state *ys, bool nofree_tree) { if (ys) { + if (ys->user_tree && ys->user_tree_unlock) + ys->user_tree_unlock(ys->user_tree, ys->user_tree_lock); EVENT_OFF(ys->walk_ev); nb_op_walks_del(&nb_op_walks, ys); /* if we have a branch then free up it's libyang tree */ @@ -295,9 +316,8 @@ static bool __move_back_to_next(struct nb_op_yield_state *ys, int i) static void nb_op_resume_data_tree(struct nb_op_yield_state *ys) { - struct nb_op_node_info *ni; + struct nb_op_node_info *pni, *ni; struct nb_node *nn; - const void *parent_entry; const void *list_entry; uint i; @@ -320,6 +340,7 @@ static void nb_op_resume_data_tree(struct nb_op_yield_state *ys) * restored. */ darr_foreach_i (ys->node_infos, i) { + pni = i > 0 ? &ys->node_infos[i - 1] : NULL; ni = &ys->node_infos[i]; nn = ni->schema->priv; @@ -330,9 +351,7 @@ static void nb_op_resume_data_tree(struct nb_op_yield_state *ys) ni == darr_last(ys->node_infos)); /* Verify the entry is still present */ - parent_entry = (i == 0 ? NULL : ni[-1].list_entry); - list_entry = nb_callback_lookup_entry(nn, parent_entry, - &ni->keys); + list_entry = nb_op_list_lookup_entry(ys, nn, pni, NULL, &ni->keys); if (!list_entry || list_entry != ni->list_entry) { /* May be NULL or a different pointer * move back to first of @@ -404,6 +423,7 @@ static enum nb_error nb_op_xpath_to_trunk(const char *xpath_in, char **xpath_out static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys, uint index) { + struct nb_op_node_info *pni = index == 0 ? NULL : &ys->node_infos[index - 1]; struct nb_op_node_info *ni = &ys->node_infos[index]; struct lyd_node *inner = ni->inner; struct nb_node *nn = ni->schema->priv; @@ -417,8 +437,7 @@ static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys, /* Assert that we are walking the rightmost branch */ assert(!inner->parent || inner == inner->parent->child->prev); - if (CHECK_FLAG(inner->schema->nodetype, - LYS_CASE | LYS_CHOICE | LYS_CONTAINER)) { + if (CHECK_FLAG(inner->schema->nodetype, LYS_CONTAINER)) { /* containers have only zero or one child on a branch of a tree */ inner = ((struct lyd_node_inner *)inner)->child; assert(!inner || inner->prev == inner); @@ -448,17 +467,12 @@ static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys, */ /* ni->list_entry starts as the parent entry of this node */ - ni->list_entry = nb_callback_get_next(nn, ni->list_entry, NULL); + ni->list_entry = nb_op_list_get_next(ys, nn, pni, NULL); for (i = 1; i < ni->position && ni->list_entry; i++) - ni->list_entry = nb_callback_get_next(nn, ni->list_entry, ni->list_entry); + ni->list_entry = nb_op_list_get_next(ys, nn, pni, ni->list_entry); - if (i != ni->position || !ni->list_entry) { - flog_warn(EC_LIB_NB_OPERATIONAL_DATA, - "%s: entry at position %d doesn't exist in: %s", __func__, - ni->position, ys->xpath); + if (i != ni->position || !ni->list_entry) return NB_ERR_NOT_FOUND; - } - } else { nb_op_get_keys((struct lyd_node_inner *)inner, &ni->keys); /* A list entry cannot be present in a tree w/o it's keys */ @@ -468,8 +482,10 @@ static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys, * Get this nodes opaque list_entry object */ + /* We need a lookup entry unless this is a keyless list */ - if (!nn->cbs.lookup_entry && ni->keys.num) { + if (!nn->cbs.lookup_entry && ni->keys.num && + !CHECK_FLAG(nn->flags, F_NB_NODE_HAS_GET_TREE)) { flog_warn(EC_LIB_NB_OPERATIONAL_DATA, "%s: data path doesn't support iteration over operational data: %s", __func__, ys->xpath); @@ -477,7 +493,7 @@ static enum nb_error nb_op_ys_finalize_node_info(struct nb_op_yield_state *ys, } /* ni->list_entry starts as the parent entry of this node */ - ni->list_entry = nb_callback_lookup_entry(nn, ni->list_entry, &ni->keys); + ni->list_entry = nb_op_list_lookup_entry(ys, nn, pni, NULL, &ni->keys); if (ni->list_entry == NULL) { flog_warn(EC_LIB_NB_OPERATIONAL_DATA, "%s: list entry lookup failed", __func__); @@ -568,7 +584,8 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys) inner = node; prevlen = 0; xplen = strlen(xpath); - darr_free(xpath); + darr_free(ys->xpath); + ys->xpath = xpath; for (i = len; i > 0; i--, inner = &inner->parent->node) { ni = &ys->node_infos[i - 1]; ni->inner = inner; @@ -630,6 +647,222 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys) /* End of init code */ /* ================ */ +static const char *__module_name(const struct nb_node *nb_node) +{ + return nb_node->snode->module->name; +} + +static get_tree_locked_cb __get_get_tree_funcs(const char *module_name, + unlock_tree_cb *unlock_func_pp) +{ + struct yang_module *module = yang_module_find(module_name); + + if (!module || !module->frr_info->get_tree_locked) + return NULL; + + *unlock_func_pp = module->frr_info->unlock_tree; + return module->frr_info->get_tree_locked; +} + +static const struct lyd_node *__get_tree(struct nb_op_yield_state *ys, + const struct nb_node *nb_node, const char *xpath) +{ + get_tree_locked_cb get_tree_cb; + + if (ys->user_tree) + return ys->user_tree; + + get_tree_cb = __get_get_tree_funcs(__module_name(nb_node), &ys->user_tree_unlock); + assert(get_tree_cb); + + ys->user_tree = get_tree_cb(xpath, &ys->user_tree_lock); + return ys->user_tree; +} + +/** + * nb_op_libyang_cb_get() - get a leaf value from user supplied libyang tree. + */ +static enum nb_error nb_op_libyang_cb_get(struct nb_op_yield_state *ys, + const struct nb_node *nb_node, struct lyd_node *parent, + const char *xpath) +{ + const struct lysc_node *snode = nb_node->snode; + const struct lyd_node *tree = __get_tree(ys, nb_node, xpath); + struct lyd_node *node; + LY_ERR err; + + err = lyd_find_path(tree, xpath, false, &node); + /* We are getting LY_EINCOMPLETE for missing `type empty` nodes */ + if (err == LY_ENOTFOUND || err == LY_EINCOMPLETE) + return NB_OK; + else if (err != LY_SUCCESS) + return NB_ERR; + if (lyd_dup_single_to_ctx(node, snode->module->ctx, (struct lyd_node_inner *)parent, 0, + &node)) + return NB_ERR; + return NB_OK; +} + +static enum nb_error nb_op_libyang_cb_get_leaflist(struct nb_op_yield_state *ys, + const struct nb_node *nb_node, + struct lyd_node *parent, const char *xpath) +{ + const struct lysc_node *snode = nb_node->snode; + const struct lyd_node *tree = __get_tree(ys, nb_node, xpath); + struct ly_set *set = NULL; + LY_ERR err; + int ret = NB_OK; + uint i; + + err = lyd_find_xpath(tree, xpath, &set); + /* We are getting LY_EINCOMPLETE for missing `type empty` nodes */ + if (err == LY_ENOTFOUND || err == LY_EINCOMPLETE) + return NB_OK; + else if (err != LY_SUCCESS) + return NB_ERR; + + for (i = 0; i < set->count; i++) { + if (lyd_dup_single_to_ctx(set->dnodes[i], snode->module->ctx, + (struct lyd_node_inner *)parent, 0, NULL)) { + ret = NB_ERR; + break; + } + } + ly_set_free(set, NULL); + return ret; +} + +static const struct lyd_node *__get_node_other_tree(const struct lyd_node *tree, + const struct lyd_node *parent_node, + const struct lysc_node *schema, + const struct yang_list_keys *keys) +{ + char xpath[XPATH_MAXLEN]; + struct lyd_node *node; + int schema_len = strlen(schema->name); + struct ly_set *set = NULL; + int len; + + if (!parent_node) { + /* we need a full path to the schema node */ + if (!lysc_path(schema, LYSC_PATH_DATA, xpath, sizeof(xpath))) + return NULL; + len = strlen(xpath); + } else { + if (!lyd_path(parent_node, LYD_PATH_STD, xpath, sizeof(xpath))) + return NULL; + len = strlen(xpath); + /* do we have room for slash and the node basename? */ + if (len + 1 + schema_len + 1 > XPATH_MAXLEN) + return NULL; + xpath[len++] = '/'; + strlcpy(&xpath[len], schema->name, sizeof(xpath) - len); + len += schema_len; + } + if (keys) + yang_get_key_preds(&xpath[len], schema, keys, sizeof(xpath) - len); + + if (lyd_find_xpath(tree, xpath, &set)) + return NULL; + if (set->count < 1) + return NULL; + node = set->dnodes[0]; + ly_set_free(set, NULL); + return node; +} + +static const void *nb_op_list_lookup_entry(struct nb_op_yield_state *ys, struct nb_node *nb_node, + const struct nb_op_node_info *pni, struct lyd_node *node, + const struct yang_list_keys *keys) +{ + struct yang_list_keys _keys; + const struct lyd_node *tree; + const struct lyd_node *parent_node; + + /* Use user callback */ + if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_HAS_GET_TREE)) { + if (node) + return nb_callback_lookup_node_entry(node, pni ? pni->list_entry : NULL); + + assert(keys); + return nb_callback_lookup_entry(nb_node, pni ? pni->list_entry : NULL, keys); + } + + if (!keys) { + assert(node); + if (yang_get_node_keys(node, &_keys)) { + flog_warn(EC_LIB_LIBYANG, + "%s: can't get keys for lookup from existing data node %s", + __func__, node->schema->name); + return NULL; + } + keys = &_keys; + } + tree = __get_tree(ys, nb_node, NULL); + parent_node = pni ? pni->inner : NULL; + return __get_node_other_tree(tree, parent_node, nb_node->snode, keys); +} + +static const void *__get_next(struct nb_op_yield_state *ys, struct nb_node *nb_node, + const struct nb_op_node_info *pni, const void *list_entry) +{ + const struct lysc_node *snode = nb_node->snode; + const struct lyd_node *tree = __get_tree(ys, nb_node, NULL); + const struct lyd_node *parent_node = pni ? pni->inner : NULL; + const struct lyd_node *node = list_entry; + + if (!node) + return __get_node_other_tree(tree, parent_node, snode, NULL); + + node = node->next; + LY_LIST_FOR (node, node) { + if (node->schema == snode) + break; + } + return node; +} + +static const void *nb_op_list_get_next(struct nb_op_yield_state *ys, struct nb_node *nb_node, + const struct nb_op_node_info *pni, const void *list_entry) +{ + if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_HAS_GET_TREE)) + return nb_callback_get_next(nb_node, pni ? pni->list_entry : NULL, list_entry); + return __get_next(ys, nb_node, pni, list_entry); +} + +static enum nb_error nb_op_list_get_keys(struct nb_op_yield_state *ys, struct nb_node *nb_node, + const void *list_entry, struct yang_list_keys *keys) +{ + const struct lyd_node_inner *list_node = list_entry; + const struct lyd_node *child; + uint count = 0; + + /* Use user callback */ + if (!CHECK_FLAG(nb_node->flags, F_NB_NODE_HAS_GET_TREE)) + return nb_callback_get_keys(nb_node, list_entry, keys); + + assert(list_node->schema->nodetype == LYS_LIST); + + /* + * NOTE: libyang current stores the keys as the first children of a list + * node we count on that here. + */ + + LY_LIST_FOR (lyd_child(&list_node->node), child) { + if (!lysc_is_key(child->schema)) + break; + if (count == LIST_MAXKEYS) { + zlog_err("Too many keys for list_node: %s", list_node->schema->name); + break; + } + strlcpy(keys->key[count++], lyd_get_value(child), sizeof(keys->key[0])); + } + keys->num = count; + + return 0; +} + + /** * nb_op_add_leaf() - Add leaf data to the get tree results * @ys - the yield state for this tree walk. @@ -655,8 +888,13 @@ static enum nb_error nb_op_iter_leaf(struct nb_op_yield_state *ys, if (lysc_is_key(snode)) return NB_OK; + /* See if we use data tree directly */ + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_HAS_GET_TREE)) + return nb_op_libyang_cb_get(ys, nb_node, ni->inner, xpath); + /* Check for new simple get */ if (nb_node->cbs.get) + /* XXX: need to run through translator */ return nb_node->cbs.get(nb_node, ni->list_entry, ni->inner); data = nb_callback_get_elem(nb_node, xpath, ni->list_entry); @@ -694,8 +932,13 @@ static enum nb_error nb_op_iter_leaflist(struct nb_op_yield_state *ys, /* Check for new simple get */ if (nb_node->cbs.get) + /* XXX: need to run through translator */ return nb_node->cbs.get(nb_node, ni->list_entry, ni->inner); + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_HAS_GET_TREE)) + /* XXX: need to run through translator */ + return nb_op_libyang_cb_get_leaflist(ys, nb_node, ni->inner, xpath); + do { struct yang_data *data; @@ -897,8 +1140,7 @@ static const struct lysc_node *nb_op_sib_first(struct nb_op_yield_state *ys, * base of the user query, return the next schema node from the query * string (schema_path). */ - if (last != NULL && - !CHECK_FLAG(last->schema->nodetype, LYS_CASE | LYS_CHOICE)) + if (last != NULL) assert(last->schema == parent); if (darr_lasti(ys->node_infos) < ys->query_base_level) return ys->schema_path[darr_lasti(ys->node_infos) + 1]; @@ -975,7 +1217,8 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) if (!walk_stem_tip) return NB_ERR_NOT_FOUND; - if (ys->schema_path[0]->nodetype == LYS_CHOICE) { + if (ys->schema_path[0]->parent && + CHECK_FLAG(ys->schema_path[0]->parent->nodetype, LYS_CHOICE|LYS_CASE)) { flog_err(EC_LIB_NB_OPERATIONAL_DATA, "%s: unable to walk root level choice node from module: %s", __func__, ys->schema_path[0]->module->name); @@ -1082,8 +1325,12 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER)) xpath_child = nb_op_get_child_path(ys->xpath, sib, xpath_child); - else if (CHECK_FLAG(sib->nodetype, LYS_CASE | LYS_CHOICE)) + else if (CHECK_FLAG(sib->nodetype, LYS_CASE | LYS_CHOICE)) { darr_in_strdup(xpath_child, ys->xpath); + len = darr_last(ys->node_infos)->xpath_len; + darr_setlen(xpath_child, len + 1); + xpath_child[len] = 0; + } nn = sib->priv; @@ -1304,9 +1551,8 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) * -------------------- */ if (list_start) { - list_entry = - nb_callback_lookup_node_entry( - node, parent_list_entry); + list_entry = nb_op_list_lookup_entry(ys, nn, pni, node, + NULL); /* * If the node we created from a * specific predicate entry is not @@ -1339,10 +1585,7 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) * (list_entry != NULL) the list iteration. */ /* Obtain [next] list entry. */ - list_entry = - nb_callback_get_next(nn, - parent_list_entry, - list_entry); + list_entry = nb_op_list_get_next(ys, nn, pni, list_entry); } /* @@ -1468,8 +1711,7 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) /* Need to get keys. */ if (!CHECK_FLAG(nn->flags, F_NB_NODE_KEYLESS_LIST)) { - ret = nb_callback_get_keys(nn, list_entry, - &ni->keys); + ret = nb_op_list_get_keys(ys, nn, list_entry, &ni->keys); if (ret) { darr_pop(ys->node_infos); ret = NB_ERR_RESOURCE; @@ -1481,8 +1723,9 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) */ len = darr_strlen(ys->xpath); if (ni->keys.num) { - yang_get_key_preds(ys->xpath + len, sib, - &ni->keys, + darr_ensure_avail(ys->xpath, + yang_get_key_pred_strlen(sib, &ni->keys) + 1); + yang_get_key_preds(ys->xpath + len, sib, &ni->keys, darr_cap(ys->xpath) - len); } else { /* add a position predicate (1s based?) */ @@ -1703,22 +1946,22 @@ static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys, * NOTE: appears to be a bug in nb_node linkage where parent can be NULL, * or I'm misunderstanding the code, in any case we use the libyang * linkage to walk which works fine. - * - * XXX: we don't actually support choice/case yet, they are container - * types in the libyang schema, but won't be in data so our length - * checking gets messed up. */ - for (sn = nblast->snode, count = 0; sn; count++, sn = sn->parent) + for (sn = nblast->snode, count = 0; sn; sn = sn->parent) { if (sn != nblast->snode) assert(CHECK_FLAG(sn->nodetype, - LYS_CONTAINER | LYS_LIST | - LYS_CHOICE | LYS_CASE)); + LYS_CONTAINER | LYS_LIST | LYS_CHOICE | LYS_CASE)); + if (!CHECK_FLAG(sn->nodetype, LYS_CHOICE | LYS_CASE)) + count++; + } /* create our arrays */ darr_append_n(ys->schema_path, count); darr_append_n(ys->query_tokens, count); darr_append_nz(ys->non_specific_predicate, count); - for (sn = nblast->snode; sn; sn = sn->parent) - ys->schema_path[--count] = sn; + for (sn = nblast->snode; sn; sn = sn->parent) { + if (!CHECK_FLAG(sn->nodetype, LYS_CHOICE | LYS_CASE)) + ys->schema_path[--count] = sn; + } /* * Now tokenize the query string and get pointers to each token @@ -1737,50 +1980,42 @@ static enum nb_error nb_op_ys_init_schema_path(struct nb_op_yield_state *ys, int nlen = strlen(name); int mnlen = 0; - /* - * Technically the query_token for choice/case should probably be pointing at - * the child (leaf) rather than the parent (container), however, - * we only use these for processing list nodes so KISS. - */ - if (CHECK_FLAG(ys->schema_path[i]->nodetype, - LYS_CASE | LYS_CHOICE)) { - ys->query_tokens[i] = ys->query_tokens[i - 1]; - continue; - } - + s2 = s; while (true) { - s2 = strstr(s, name); + /* skip past any module name prefix */ + s2 = strstr(s2, name); if (!s2) goto error; - if (s2[-1] == ':') { + if (s2 > s && s2[-1] == ':') { mnlen = strlen(modname) + 1; - if (ys->query_tokstr > s2 - mnlen || - strncmp(s2 - mnlen, modname, mnlen - 1)) - goto error; + if (s2 - s < mnlen || strncmp(s2 - mnlen, modname, mnlen - 1)) { + /* No match of module prefix, advance and try again */ + s2 += strlen(name); + continue; + } s2 -= mnlen; nlen += mnlen; } - s = s2; - if ((i == 0 || s[-1] == '/') && - (s[nlen] == 0 || s[nlen] == '[' || s[nlen] == '/')) + if ((i == 0 || s2[-1] == '/') && + (s2[nlen] == 0 || s2[nlen] == '[' || s2[nlen] == '/')) { + s = s2; break; - /* - * Advance past the incorrect match, must have been - * part of previous predicate. - */ - s += nlen; + } + /* No exact match at end, advance and try again */ + s2 += strlen(name); } /* NUL terminate previous token and save this one */ - if (i > 0) + if (i > 0) { + assert(s[-1] == '/'); s[-1] = 0; + } ys->query_tokens[i] = s; s += nlen; } - /* NOTE: need to subtract choice/case nodes when these are supported */ ys->query_base_level = darr_lasti(ys->schema_path); return NB_OK; diff --git a/lib/prefix.c b/lib/prefix.c index 2485c3e61b..feaf3e5f1c 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1439,10 +1439,13 @@ bool ipv4_unicast_valid(const struct in_addr *addr) { in_addr_t ip = ntohl(addr->s_addr); + if (IPV4_CLASS_E(ip)) + return true; + if (IPV4_CLASS_D(ip)) return false; - if (IPV4_NET0(ip) || IPV4_NET127(ip) || IPV4_CLASS_E(ip)) { + if (IPV4_NET0(ip) || IPV4_NET127(ip)) { if (cmd_allow_reserved_ranges_get()) return true; else diff --git a/lib/srv6.h b/lib/srv6.h index 011705504e..467f02a3c9 100644 --- a/lib/srv6.h +++ b/lib/srv6.h @@ -176,12 +176,20 @@ struct srv6_locator_chunk { enum srv6_endpoint_behavior_codepoint { SRV6_ENDPOINT_BEHAVIOR_RESERVED = 0x0000, SRV6_ENDPOINT_BEHAVIOR_END = 0x0001, + SRV6_ENDPOINT_BEHAVIOR_END_PSP = 0x0002, SRV6_ENDPOINT_BEHAVIOR_END_X = 0x0005, + SRV6_ENDPOINT_BEHAVIOR_END_X_PSP = 0x0006, SRV6_ENDPOINT_BEHAVIOR_END_DT6 = 0x0012, SRV6_ENDPOINT_BEHAVIOR_END_DT4 = 0x0013, SRV6_ENDPOINT_BEHAVIOR_END_DT46 = 0x0014, + SRV6_ENDPOINT_BEHAVIOR_END_PSP_USD = 0x001D, + SRV6_ENDPOINT_BEHAVIOR_END_X_PSP_USD = 0x0021, SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID = 0x002B, - SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID = 0x002C, + SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID = 0x0034, + SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID_PSP = 0x002C, + SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID_PSP_USD = 0x0030, + SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID_PSP = 0x0035, + SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID_PSP_USD = 0x0039, SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID = 0x003E, SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID = 0x003F, SRV6_ENDPOINT_BEHAVIOR_END_DT46_USID = 0x0040, @@ -199,8 +207,16 @@ srv6_endpoint_behavior_codepoint2str(enum srv6_endpoint_behavior_codepoint behav return "Reserved"; case SRV6_ENDPOINT_BEHAVIOR_END: return "End"; + case SRV6_ENDPOINT_BEHAVIOR_END_PSP: + return "End PSP"; + case SRV6_ENDPOINT_BEHAVIOR_END_PSP_USD: + return "End PSP/USD"; case SRV6_ENDPOINT_BEHAVIOR_END_X: return "End.X"; + case SRV6_ENDPOINT_BEHAVIOR_END_X_PSP: + return "End.X PSP"; + case SRV6_ENDPOINT_BEHAVIOR_END_X_PSP_USD: + return "End.X PSP/USD"; case SRV6_ENDPOINT_BEHAVIOR_END_DT6: return "End.DT6"; case SRV6_ENDPOINT_BEHAVIOR_END_DT4: @@ -209,8 +225,16 @@ srv6_endpoint_behavior_codepoint2str(enum srv6_endpoint_behavior_codepoint behav return "End.DT46"; case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID: return "uN"; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID_PSP: + return "uN PSP"; + case SRV6_ENDPOINT_BEHAVIOR_END_NEXT_CSID_PSP_USD: + return "uN PSP/USD"; case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID: return "uA"; + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID_PSP: + return "uA PSP"; + case SRV6_ENDPOINT_BEHAVIOR_END_X_NEXT_CSID_PSP_USD: + return "uA PSP/USD"; case SRV6_ENDPOINT_BEHAVIOR_END_DT6_USID: return "uDT6"; case SRV6_ENDPOINT_BEHAVIOR_END_DT4_USID: @@ -297,6 +321,7 @@ struct srv6_sid_ctx { struct in_addr nh4; struct in6_addr nh6; vrf_id_t vrf_id; + ifindex_t ifindex; }; static inline const char *seg6_mode2str(enum seg6_mode_t mode) diff --git a/lib/yang.c b/lib/yang.c index dd48d8861b..a57b247634 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -1357,9 +1357,21 @@ uint32_t yang_get_list_elements_count(const struct lyd_node *node) } while (node); return count; } +int yang_get_key_pred_strlen(const struct lysc_node *snode, const struct yang_list_keys *keys) +{ + const struct lysc_node_leaf *skey; + size_t len = 0; + size_t i = 0; + + LY_FOR_KEYS (snode, skey) { + /* [%s='%s'] */ + len += 5 + strlen(skey->name) + strlen(keys->key[i]); + i++; + } + return len; +} -int yang_get_key_preds(char *s, const struct lysc_node *snode, - struct yang_list_keys *keys, ssize_t space) +int yang_get_key_preds(char *s, const struct lysc_node *snode, const struct yang_list_keys *keys, ssize_t space) { const struct lysc_node_leaf *skey; ssize_t len2, len = 0; diff --git a/lib/yang.h b/lib/yang.h index 748f089037..3877a421c5 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -20,6 +20,8 @@ extern "C" { #endif +struct frr_yang_module_info; + /* Maximum XPath length. */ #define XPATH_MAXLEN 1024 @@ -45,6 +47,7 @@ struct yang_module { RB_ENTRY(yang_module) entry; const char *name; const struct lys_module *info; + const struct frr_yang_module_info *frr_info; #ifdef HAVE_SYSREPO sr_subscription_ctx_t *sr_subscription; struct event *sr_thread; @@ -879,7 +882,11 @@ bool yang_is_last_level_dnode(const struct lyd_node *dnode); /* Create a YANG predicate string based on the keys */ extern int yang_get_key_preds(char *s, const struct lysc_node *snode, - struct yang_list_keys *keys, ssize_t space); + const struct yang_list_keys *keys, ssize_t space); + +/* Get the length of the predicate string based on the keys */ +extern int yang_get_key_pred_strlen(const struct lysc_node *snode, + const struct yang_list_keys *keys); /* Get YANG keys from an existing dnode */ extern int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys); |
